summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToomas Soome <tsoome@me.com>2016-11-06 13:25:49 +0200
committerToomas Soome <tsoome@me.com>2020-11-13 19:03:33 +0200
commitb713c91e508f40be7797bedd4ae1146ef0652625 (patch)
treed908f410faac15af9c495769709dd95daf496578
parent09fcda9fe16a733cc35aa3156a47ef4b909251a6 (diff)
downloadillumos-joyent-b713c91e508f40be7797bedd4ae1146ef0652625.tar.gz
7537 want nextboot (one time boot) support
Reviewed by: Jason King <jason.brian.king+illumos@gmail.com> Reviewed by: Andy Fiddaman <andy@omniosce.org> Reviewed by: C Fraire <cfraire@me.com> Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r--usr/src/boot/lib/libstand/zfs/Makefile.inc3
-rw-r--r--usr/src/boot/lib/libstand/zfs/libzfs.h115
-rw-r--r--usr/src/boot/lib/libstand/zfs/nvlist.c1568
-rw-r--r--usr/src/boot/lib/libstand/zfs/zfs.c863
-rw-r--r--usr/src/boot/lib/libstand/zfs/zfsimpl.c753
-rw-r--r--usr/src/boot/sys/boot/common/bootstrap.h33
-rw-r--r--usr/src/boot/sys/boot/common/nvstore.c309
-rw-r--r--usr/src/boot/sys/boot/efi/loader/Makefile.com3
-rw-r--r--usr/src/boot/sys/boot/efi/loader/main.c44
-rw-r--r--usr/src/boot/sys/boot/i386/gptzfsboot/Makefile9
-rw-r--r--usr/src/boot/sys/boot/i386/gptzfsboot/zfsboot.c30
-rw-r--r--usr/src/boot/sys/boot/i386/libi386/Makefile5
-rw-r--r--usr/src/boot/sys/boot/i386/loader/Makefile7
-rw-r--r--usr/src/boot/sys/boot/i386/loader/main.c13
-rw-r--r--usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h51
-rw-r--r--usr/src/cmd/beadm/beadm.c62
-rw-r--r--usr/src/cmd/boot/bootadm/bootadm_loader.c26
-rw-r--r--usr/src/cmd/eeprom/i386/Makefile1
-rw-r--r--usr/src/cmd/eeprom/i386/benv.c278
-rw-r--r--usr/src/lib/Makefile3
-rw-r--r--usr/src/lib/libbe/Makefile.com2
-rw-r--r--usr/src/lib/libbe/common/be_activate.c54
-rw-r--r--usr/src/lib/libbe/common/be_list.c22
-rw-r--r--usr/src/lib/libbe/common/libbe.h2
-rw-r--r--usr/src/lib/libbe/common/libbe_priv.h8
-rw-r--r--usr/src/man/man1m/beadm.1m59
26 files changed, 3884 insertions, 439 deletions
diff --git a/usr/src/boot/lib/libstand/zfs/Makefile.inc b/usr/src/boot/lib/libstand/zfs/Makefile.inc
index 3596dd3a26..ef840587c9 100644
--- a/usr/src/boot/lib/libstand/zfs/Makefile.inc
+++ b/usr/src/boot/lib/libstand/zfs/Makefile.inc
@@ -23,6 +23,7 @@ SRCS += $(SRC)/common/list/list.c
OBJS += zfs.o
OBJS += gzip.o
OBJS += edonr.o
+OBJS += nvlist.o
OBJS += skein.o
OBJS += skein_iv.o
OBJS += skein_block.o
@@ -30,6 +31,8 @@ OBJS += list.o
zfs.o := CPPFLAGS += -I../../common
zfs.o := CPPFLAGS += -I../../../cddl/boot/zfs -I$(LZ4)
+zfs.o := CPPFLAGS += -I$(SRC)/uts/common/fs/zfs
+nvlist.o := CPPFLAGS += -I../../common -I../../../cddl/boot/zfs
# Do not unroll skein loops, reduce code size
skein_block.o := CPPFLAGS += -DSKEIN_LOOP=111
diff --git a/usr/src/boot/lib/libstand/zfs/libzfs.h b/usr/src/boot/lib/libstand/zfs/libzfs.h
index 7a6fcf57ea..ddf7f91975 100644
--- a/usr/src/boot/lib/libstand/zfs/libzfs.h
+++ b/usr/src/boot/lib/libstand/zfs/libzfs.h
@@ -27,24 +27,133 @@
#ifndef _BOOT_LIBZFS_H_
#define _BOOT_LIBZFS_H_
+#include <zfsimpl.h>
+
#define ZFS_MAXNAMELEN 256
/*
* ZFS fully-qualified device descriptor.
*/
struct zfs_devdesc {
- struct devdesc dd; /* Must be first. */
- uint64_t pool_guid;
- uint64_t root_guid;
+ struct devdesc dd; /* Must be first. */
+ uint64_t pool_guid;
+ uint64_t root_guid;
};
+/* nvp implementation version */
+#define NV_VERSION 0
+
+/* nvlist persistent unique name flags, stored in nvl_nvflags */
+#define NV_UNIQUE_NAME 0x1
+#define NV_UNIQUE_NAME_TYPE 0x2
+
+#define NV_ALIGN4(x) (((x) + 3) & ~3)
+#define NV_ALIGN(x) (((x) + 7) & ~7)
+
+/*
+ * nvlist header.
+ * nvlist has 4 bytes header followed by version and flags, then nvpairs
+ * and the list is terminated by double zero.
+ */
+typedef struct {
+ char nvh_encoding;
+ char nvh_endian;
+ char nvh_reserved1;
+ char nvh_reserved2;
+} nvs_header_t;
+
+typedef struct {
+ nvs_header_t nv_header;
+ size_t nv_asize;
+ size_t nv_size;
+ uint8_t *nv_data;
+ uint8_t *nv_idx;
+} nvlist_t;
+
+/*
+ * nvpair header.
+ * nvpair has encoded and decoded size
+ * name string (size and data)
+ * data type and number of elements
+ * data
+ */
+typedef struct {
+ unsigned encoded_size;
+ unsigned decoded_size;
+} nvp_header_t;
+
+/*
+ * nvlist stream head.
+ */
+typedef struct {
+ unsigned nvl_version;
+ unsigned nvl_nvflag;
+ nvp_header_t nvl_pair;
+} nvs_data_t;
+
+typedef struct {
+ unsigned nv_size;
+ uint8_t nv_data[]; /* NV_ALIGN4(string) */
+} nv_string_t;
+
+typedef struct {
+ unsigned nv_type; /* data_type_t */
+ unsigned nv_nelem; /* number of elements */
+ uint8_t nv_data[]; /* data stream */
+} nv_pair_data_t;
+
+nvlist_t *nvlist_create(int);
+void nvlist_destroy(nvlist_t *);
+nvlist_t *nvlist_import(const char *, size_t);
+int nvlist_export(nvlist_t *);
+int nvlist_remove(nvlist_t *, const char *, data_type_t);
+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 *);
+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 *, const char *, const char **);
char *zfs_bootfs(void *);
char *zfs_fmtdev(void *);
int zfs_probe_dev(const char *, uint64_t *);
int zfs_list(const char *);
+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 *);
+nvlist_t *vdev_read_bootenv(vdev_t *);
+
extern struct devsw zfs_dev;
extern struct fs_ops zfs_fsops;
diff --git a/usr/src/boot/lib/libstand/zfs/nvlist.c b/usr/src/boot/lib/libstand/zfs/nvlist.c
new file mode 100644
index 0000000000..ff5bc8e0da
--- /dev/null
+++ b/usr/src/boot/lib/libstand/zfs/nvlist.c
@@ -0,0 +1,1568 @@
+/*
+ * Copyright 2020 Toomas Soome <tsoome@me.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <stand.h>
+#include <stdbool.h>
+#include <sys/endian.h>
+#include <sys/stdint.h>
+#include <sys/param.h>
+#include <zfsimpl.h>
+#include "libzfs.h"
+
+enum xdr_op {
+ XDR_OP_ENCODE = 1,
+ XDR_OP_DECODE = 2
+};
+
+typedef struct xdr {
+ 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 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 *);
+
+typedef int (*xdrproc_t)(xdr_t *, void *);
+
+/* 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));
+}
+
+static int
+_putint(struct xdr *xdr, int i)
+{
+ int *ip = (int *)xdr->xdr_idx;
+
+ *ip = htobe32(i);
+ return (sizeof (int));
+}
+
+static int
+_getuint(struct xdr *xdr, unsigned *ip)
+{
+ *ip = be32dec(xdr->xdr_idx);
+ return (sizeof (unsigned));
+}
+
+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 bool
+xdr_u_short(xdr_t *xdr, unsigned short *ip)
+{
+ unsigned u;
+ bool rv;
+
+ u = *ip;
+ if ((rv = xdr_u_int(xdr, &u))) {
+ if (xdr->xdr_op == XDR_OP_DECODE)
+ *ip = u;
+ }
+ return (rv);
+}
+
+/*
+ * translate xdr->xdr_idx, increment it by size of int.
+ */
+static bool
+xdr_int(xdr_t *xdr, int *ip)
+{
+ 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);
+}
+
+/*
+ * translate xdr->xdr_idx, increment it by size of unsigned int.
+ */
+static bool
+xdr_u_int(xdr_t *xdr, unsigned *ip)
+{
+ bool rv = false;
+ unsigned *u = (unsigned *)xdr->xdr_idx;
+
+ 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 bool
+xdr_int64(xdr_t *xdr, int64_t *lp)
+{
+ int hi;
+ unsigned lo;
+ bool rv = false;
+
+ 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 bool
+xdr_uint64(xdr_t *xdr, uint64_t *lp)
+{
+ unsigned hi, lo;
+ bool rv = false;
+
+ 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 bool
+xdr_char(xdr_t *xdr, char *cp)
+{
+ int i;
+ bool rv = false;
+
+ i = *cp;
+ if ((rv = xdr_int(xdr, &i))) {
+ if (xdr->xdr_op == XDR_OP_DECODE)
+ *cp = i;
+ }
+ return (rv);
+}
+
+static bool
+xdr_string(xdr_t *xdr, nv_string_t *s)
+{
+ 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)
+{
+ if (nvl != NULL) {
+ /* Free data if it was allocated by us. */
+ if (nvl->nv_asize > 0)
+ free(nvl->nv_data);
+ }
+ free(nvl);
+}
+
+char *
+nvstring_get(nv_string_t *nvs)
+{
+ char *s;
+
+ s = malloc(nvs->nv_size + 1);
+ if (s != NULL) {
+ bcopy(nvs->nv_data, s, nvs->nv_size);
+ s[nvs->nv_size] = '\0';
+ }
+ return (s);
+}
+
+/*
+ * Create empty nvlist.
+ * The nvlist is terminated by 2x zeros (8 bytes).
+ */
+nvlist_t *
+nvlist_create(int flag)
+{
+ nvlist_t *nvl;
+ nvs_data_t *nvs;
+
+ 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);
+ nvs = calloc(1, nvl->nv_asize);
+ if (nvs == NULL) {
+ free(nvl);
+ return (NULL);
+ }
+ /* data in nvlist is byte stream */
+ nvl->nv_data = (uint8_t *)nvs;
+
+ nvs->nvl_version = NV_VERSION;
+ nvs->nvl_nvflag = flag;
+ return (nvl);
+}
+
+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 *)xdr->xdr_idx;
+ if (!xdr_string(xdr, nv_string)) {
+ return (false);
+ }
+ nvp_data = (nv_pair_data_t *)xdr->xdr_idx;
+
+ type = nvp_data->nv_type;
+ nelem = nvp_data->nv_nelem;
+ if (!xdr_u_int(xdr, &type) || !xdr_u_int(xdr, &nelem))
+ return (false);
+
+ switch (type) {
+ case DATA_TYPE_NVLIST:
+ case DATA_TYPE_NVLIST_ARRAY:
+ bzero(&nvlist, sizeof (nvlist));
+ nvlist.nv_data = xdr->xdr_idx;
+ nvlist.nv_idx = nvlist.nv_data;
+
+ /* 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;
+
+ case DATA_TYPE_BOOLEAN:
+ /* BOOLEAN does not take value space */
+ break;
+ case DATA_TYPE_BYTE:
+ case DATA_TYPE_INT8:
+ case DATA_TYPE_UINT8:
+ if (!xdr_char(xdr, (char *)&nvp_data->nv_data[0]))
+ return (false);
+ break;
+
+ case DATA_TYPE_INT16:
+ if (!xdr_short(xdr, (short *)&nvp_data->nv_data[0]))
+ return (false);
+ break;
+
+ case DATA_TYPE_UINT16:
+ if (!xdr_u_short(xdr, (unsigned short *)&nvp_data->nv_data[0]))
+ return (false);
+ break;
+
+ case DATA_TYPE_BOOLEAN_VALUE:
+ case DATA_TYPE_INT32:
+ if (!xdr_int(xdr, (int *)&nvp_data->nv_data[0]))
+ return (false);
+ break;
+
+ case DATA_TYPE_UINT32:
+ if (!xdr_u_int(xdr, (unsigned *)&nvp_data->nv_data[0]))
+ return (false);
+ break;
+
+ case DATA_TYPE_HRTIME:
+ case DATA_TYPE_INT64:
+ if (!xdr_int64(xdr, (int64_t *)&nvp_data->nv_data[0]))
+ return (false);
+ break;
+
+ case DATA_TYPE_UINT64:
+ 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];
+ 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 int
+nvlist_xdr_nvlist(xdr_t *xdr, nvlist_t *nvl)
+{
+ nvp_header_t *nvph;
+ nvs_data_t *nvs;
+ unsigned encoded_size, decoded_size;
+ int rv;
+
+ nvs = (nvs_data_t *)xdr->xdr_idx;
+ nvph = &nvs->nvl_pair;
+
+ if (!xdr_u_int(xdr, &nvs->nvl_version))
+ return (EINVAL);
+ if (!xdr_u_int(xdr, &nvs->nvl_nvflag))
+ return (EINVAL);
+
+ 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);
+}
+
+/*
+ * Calculate nvlist size, translating encoded_size and decoded_size.
+ */
+static bool
+nvlist_size_xdr(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_u_int(xdr, &encoded_size) || !xdr_u_int(xdr, &decoded_size))
+ return (false);
+
+ while (encoded_size && 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);
+ }
+ *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 char *stream, size_t size)
+{
+ nvlist_t *nvl;
+ xdr_t xdr = {
+ .xdr_op = XDR_OP_DECODE,
+ .xdr_getint = _getint,
+ .xdr_getuint = _getuint
+ };
+
+ /* Check the nvlist head. */
+ if (stream[0] != NV_ENCODE_XDR ||
+ (stream[1] != '\0' && stream[1] != '\1') ||
+ stream[2] != '\0' || stream[3] != '\0' ||
+ be32toh(*(uint32_t *)(stream + 4)) != NV_VERSION ||
+ be32toh(*(uint32_t *)(stream + 8)) != NV_UNIQUE_NAME)
+ return (NULL);
+
+ nvl = malloc(sizeof (*nvl));
+ if (nvl == NULL)
+ return (nvl);
+
+ nvl->nv_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 + 4, nvl->nv_data, nvl->nv_asize);
+
+ 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);
+}
+
+/*
+ * remove pair from this nvlist.
+ */
+int
+nvlist_remove(nvlist_t *nvl, const char *name, data_type_t type)
+{
+ uint8_t *head, *tail;
+ nvs_data_t *data;
+ nvp_header_t *nvp;
+ 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);
+
+ /* 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 *)(nvp + 1);
+
+ nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] +
+ NV_ALIGN4(nvp_name->nv_size));
+
+ 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_size - (tail - nvl->nv_data);
+
+ /* adjust the size of the nvlist. */
+ nvl->nv_size -= nvp->encoded_size;
+ bcopy(tail, head, size);
+ return (0);
+ }
+ /* Not our pair, skip to next. */
+ head = head + nvp->encoded_size;
+ nvp = (nvp_header_t *)head;
+ }
+ 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)
+{
+ nvs_data_t *data;
+ nvp_header_t *nvp;
+ nv_string_t *nvp_name;
+ nv_pair_data_t *nvp_data;
+ nvlist_t **nvlist, *nv;
+ uint8_t *ptr;
+ int rv;
+
+ if (nvl == NULL || nvl->nv_data == NULL || name == NULL)
+ return (EINVAL);
+
+ 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 *)((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 (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:
+ bcopy(nvp_data->nv_data, valuep,
+ sizeof (uint64_t));
+ return (0);
+ case DATA_TYPE_STRING:
+ nvp_name = (nv_string_t *)nvp_data->nv_data;
+ if (sizep != NULL) {
+ *sizep = nvp_name->nv_size;
+ }
+ *(const uint8_t **)valuep =
+ &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 = 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);
+ }
+ *(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);
+}
+
+static int
+get_value_size(data_type_t type, const void *data, uint32_t nelem)
+{
+ uint64_t value_sz = 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);
+
+ 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);
+
+ /*
+ * 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_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
+nvpair_print(nvp_header_t *nvp, unsigned int indent)
+{
+ nv_string_t *nvp_name;
+ nv_pair_data_t *nvp_data;
+ nvlist_t nvlist;
+ xdr_t xdr;
+ unsigned i, j, u;
+ uint64_t u64;
+
+ 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 (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);
+
+ 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;
+
+ 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_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;
+
+ 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++) {
+ 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);
+ }
+ 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);
+
+ 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");
+}
diff --git a/usr/src/boot/lib/libstand/zfs/zfs.c b/usr/src/boot/lib/libstand/zfs/zfs.c
index 34c1b7c23e..0e0af60831 100644
--- a/usr/src/boot/lib/libstand/zfs/zfs.c
+++ b/usr/src/boot/lib/libstand/zfs/zfs.c
@@ -123,7 +123,7 @@ zfs_close(struct open_file *f)
{
struct file *fp = (struct file *)f->f_fsdata;
- dnode_cache_obj = 0;
+ dnode_cache_obj = NULL;
f->f_fsdata = NULL;
free(fp);
@@ -168,7 +168,6 @@ zfs_seek(struct open_file *f, off_t offset, int where)
struct stat sb;
int error;
-
switch (where) {
case SEEK_SET:
fp->f_seekp = offset;
@@ -360,6 +359,7 @@ vdev_read(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
if (ret != 0)
return (ret);
+ /* BEGIN CSTYLED */
/*
* Handling reads of arbitrary offset and size - multi-sector case
* and single-sector case.
@@ -389,6 +389,7 @@ vdev_read(vdev_t *vdev __unused, void *priv, off_t offset, void *buf,
* +-------------------------------+
* start_sec
*/
+ /* END CSTYLED */
start_sec = offset / secsz;
head = offset % secsz;
total_size = roundup2(head + bytes, secsz);
@@ -461,6 +462,118 @@ error:
}
static int
+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;
+ unsigned secsz, do_tail_write;
+ off_t start_sec;
+ ssize_t res;
+ char *outbuf, *bouncebuf;
+
+ fd = (uintptr_t)vdev->v_priv;
+ outbuf = (char *)buf;
+ bouncebuf = NULL;
+
+ ret = ioctl(fd, DIOCGSECTORSIZE, &secsz);
+ if (ret != 0)
+ return (ret);
+
+ start_sec = offset / secsz;
+ head = offset % secsz;
+ total_size = roundup2(head + bytes, secsz);
+ tail = total_size - (head + bytes);
+ do_tail_write = ((tail > 0) && (head + bytes > secsz));
+ full_sec_size = total_size;
+ if (head > 0)
+ full_sec_size -= secsz;
+ if (do_tail_write)
+ full_sec_size -= secsz;
+
+ /* Partial sector write requires a bounce buffer. */
+ if ((head > 0) || do_tail_write || bytes < secsz) {
+ bouncebuf = malloc(secsz);
+ if (bouncebuf == NULL) {
+ printf("vdev_write: out of memory\n");
+ return (ENOMEM);
+ }
+ }
+
+ if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) {
+ ret = errno;
+ goto error;
+ }
+
+ /* Partial data for first sector */
+ if (head > 0) {
+ res = read(fd, bouncebuf, 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 ((unsigned)res != secsz) {
+ ret = EIO;
+ goto error;
+ }
+ outbuf += min(secsz - head, bytes);
+ }
+
+ /*
+ * Full data write to sectors.
+ * Note, there is still corner case where we write
+ * to sector boundary, but less than sector size, e.g. write 512B
+ * to 4k sector.
+ */
+ if (full_sec_size > 0) {
+ if (bytes < full_sec_size) {
+ res = read(fd, bouncebuf, 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 ((unsigned)res != secsz) {
+ ret = EIO;
+ goto error;
+ }
+ } else {
+ res = write(fd, outbuf, full_sec_size);
+ if ((unsigned)res != full_sec_size) {
+ ret = EIO;
+ goto error;
+ }
+ outbuf += full_sec_size;
+ }
+ }
+
+ /* Partial data write to last sector */
+ if (do_tail_write) {
+ res = read(fd, bouncebuf, 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 ((unsigned)res != secsz) {
+ ret = EIO;
+ goto error;
+ }
+ }
+
+ ret = 0;
+error:
+ free(bouncebuf);
+ return (ret);
+}
+
+static int
zfs_dev_init(void)
{
spa_t *spa;
@@ -512,7 +625,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);
@@ -539,8 +652,8 @@ 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);
- pa.fd = open(devname, O_RDONLY);
+ snprintf(devname, sizeof (devname), "%s%s:", devname, partname);
+ pa.fd = open(devname, O_RDWR);
if (pa.fd == -1)
return (ret);
ret = zfs_probe(pa.fd, ppa->pool_guid);
@@ -564,6 +677,728 @@ zfs_probe_partition(void *arg, const char *partname,
return (0);
}
+/*
+ * Return bootenv nvlist from pool label.
+ */
+int
+zfs_get_bootenv(void *vdev, nvlist_t **benvp)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ nvlist_t *benv = NULL;
+ vdev_t *vd;
+ spa_t *spa;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ if (spa->spa_bootenv == NULL) {
+ STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children,
+ v_childlink) {
+ benv = vdev_read_bootenv(vd);
+
+ if (benv != NULL)
+ break;
+ }
+ spa->spa_bootenv = benv;
+ } else {
+ benv = spa->spa_bootenv;
+ }
+
+ if (benv == NULL)
+ return (ENOENT);
+
+ *benvp = benv;
+ return (0);
+}
+
+/*
+ * Store nvlist to pool label bootenv area. Also updates cached pointer in spa.
+ */
+int
+zfs_set_bootenv(void *vdev, nvlist_t *benv)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ vdev_t *vd;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (ENOTSUP);
+
+ if ((spa = spa_find_by_dev(dev)) == NULL)
+ return (ENXIO);
+
+ STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) {
+ vdev_write_bootenv(vd, benv);
+ }
+
+ spa->spa_bootenv = benv;
+ return (0);
+}
+
+/*
+ * Get bootonce value by key. The bootonce <key, value> pair is removed
+ * from the bootenv nvlist and the remaining nvlist is committed back to disk.
+ */
+int
+zfs_get_bootonce(void *vdev, const char *key, char *buf, size_t size)
+{
+ nvlist_t *benv;
+ char *result = NULL;
+ int result_size, rv;
+
+ if ((rv = zfs_get_bootenv(vdev, &benv)) != 0)
+ return (rv);
+
+ if ((rv = nvlist_find(benv, key, DATA_TYPE_STRING, NULL,
+ &result, &result_size)) == 0) {
+ if (result_size == 0) {
+ /* ignore empty string */
+ rv = ENOENT;
+ } else {
+ size = MIN((size_t)result_size + 1, size);
+ strlcpy(buf, result, size);
+ }
+ (void) nvlist_remove(benv, key, DATA_TYPE_STRING);
+ (void) zfs_set_bootenv(vdev, benv);
+ }
+
+ return (rv);
+}
+
+/*
+ * nvstore backend.
+ */
+
+static int zfs_nvstore_setter(void *, int, const char *,
+ const void *, size_t);
+static int zfs_nvstore_setter_str(void *, const char *, const char *,
+ const char *);
+static int zfs_nvstore_unset_impl(void *, const char *, bool);
+static int zfs_nvstore_setenv(void *, void *);
+
+/*
+ * nvstore is only present for current rootfs pool.
+ */
+static int
+zfs_nvstore_sethook(struct env_var *ev, int flags __unused, const void *value)
+{
+ struct zfs_devdesc *dev;
+ int rv;
+
+ archsw.arch_getdev((void **)&dev, NULL, NULL);
+ if (dev == NULL)
+ return (ENXIO);
+
+ rv = zfs_nvstore_setter_str(dev, NULL, ev->ev_name, value);
+
+ free(dev);
+ return (rv);
+}
+
+/*
+ * nvstore is only present for current rootfs pool.
+ */
+static int
+zfs_nvstore_unsethook(struct env_var *ev)
+{
+ struct zfs_devdesc *dev;
+ int rv;
+
+ archsw.arch_getdev((void **)&dev, NULL, NULL);
+ if (dev == NULL)
+ return (ENXIO);
+
+ rv = zfs_nvstore_unset_impl(dev, ev->ev_name, false);
+
+ free(dev);
+ return (rv);
+}
+
+static int
+zfs_nvstore_getter(void *vdev, const char *name, void **data)
+{
+ struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
+ spa_t *spa;
+ nvlist_t *nv;
+ char *str, **ptr;
+ int size;
+ int rv;
+
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
+ return (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));
+ }
+ break;
+
+ 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)
{
@@ -575,7 +1410,7 @@ zfs_probe_dev(const char *devname, uint64_t *pool_guid)
if (pool_guid)
*pool_guid = 0;
- pa.fd = open(devname, O_RDONLY);
+ pa.fd = open(devname, O_RDWR);
if (pa.fd == -1)
return (ENXIO);
/*
@@ -636,7 +1471,7 @@ zfs_dev_print(int verbose)
return (spa_all_status());
}
STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
- sprintf(line, " zfs:%s\n", spa->spa_name);
+ snprintf(line, sizeof (line), " zfs:%s\n", spa->spa_name);
ret = pager_output(line);
if (ret != 0)
break;
@@ -660,12 +1495,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;
@@ -856,10 +1688,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);
}
diff --git a/usr/src/boot/lib/libstand/zfs/zfsimpl.c b/usr/src/boot/lib/libstand/zfs/zfsimpl.c
index 56889431af..9ceb9b9794 100644
--- a/usr/src/boot/lib/libstand/zfs/zfsimpl.c
+++ b/usr/src/boot/lib/libstand/zfs/zfsimpl.c
@@ -30,10 +30,12 @@
* Stand-alone ZFS file reader.
*/
+#include <stdbool.h>
#include <sys/endian.h>
#include <sys/stat.h>
#include <sys/stdint.h>
#include <sys/list.h>
+#include <sys/zfs_bootenv.h>
#include <inttypes.h>
#include "zfsimpl.h"
@@ -165,284 +167,48 @@ zfs_init(void)
}
static int
-xdr_int(const unsigned char **xdr, int *ip)
+nvlist_check_features_for_read(nvlist_t *nvl)
{
- *ip = be32dec(*xdr);
- (*xdr) += 4;
- return (0);
-}
-
-static int
-xdr_u_int(const unsigned char **xdr, uint_t *ip)
-{
- *ip = be32dec(*xdr);
- (*xdr) += 4;
- return (0);
-}
-
-static int
-xdr_uint64_t(const unsigned char **xdr, uint64_t *lp)
-{
- uint_t hi, lo;
-
- xdr_u_int(xdr, &hi);
- xdr_u_int(xdr, &lo);
- *lp = (((uint64_t)hi) << 32) | lo;
- return (0);
-}
-
-static int
-nvlist_find(const unsigned char *nvlist, const char *name, int type,
- int *elementsp, void *valuep, int *sizep)
-{
- const unsigned char *p, *pair;
- int junk;
- int encoded_size, decoded_size;
-
- p = nvlist;
- xdr_int(&p, &junk);
- xdr_int(&p, &junk);
-
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
- while (encoded_size && decoded_size) {
- int namelen, pairtype, elements;
- const char *pairname;
-
- xdr_int(&p, &namelen);
- pairname = (const char *)p;
- p += roundup(namelen, 4);
- xdr_int(&p, &pairtype);
-
- if (memcmp(name, pairname, namelen) == 0 && type == pairtype) {
- xdr_int(&p, &elements);
- if (elementsp)
- *elementsp = elements;
- if (type == DATA_TYPE_UINT64) {
- xdr_uint64_t(&p, (uint64_t *)valuep);
- return (0);
- } else if (type == DATA_TYPE_STRING) {
- int len;
- xdr_int(&p, &len);
- if (sizep != NULL)
- *sizep = len;
- (*(const char **)valuep) = (const char *)p;
- return (0);
- } else if (type == DATA_TYPE_NVLIST ||
- type == DATA_TYPE_NVLIST_ARRAY) {
- (*(const unsigned char **)valuep) =
- (const unsigned char *)p;
- return (0);
- } else {
- return (EIO);
- }
- } else {
- /*
- * Not the pair we are looking for, skip to the
- * next one.
- */
- p = pair + encoded_size;
- }
-
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
- }
-
- return (EIO);
-}
-
-static int
-nvlist_check_features_for_read(const unsigned char *nvlist)
-{
- const unsigned char *p, *pair;
- int junk;
- int encoded_size, decoded_size;
+ nvlist_t *features = NULL;
+ nvs_data_t *data;
+ nvp_header_t *nvp;
+ nv_string_t *nvp_name;
int rc;
- rc = 0;
+ rc = nvlist_find(nvl, ZPOOL_CONFIG_FEATURES_FOR_READ,
+ DATA_TYPE_NVLIST, NULL, &features, NULL);
+ if (rc != 0)
+ return (rc);
- p = nvlist;
- xdr_int(&p, &junk);
- xdr_int(&p, &junk);
+ data = (nvs_data_t *)features->nv_data;
+ nvp = &data->nvl_pair; /* first pair in nvlist */
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
- while (encoded_size && decoded_size) {
- int namelen, pairtype;
- const char *pairname;
+ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
int i, found;
+ nvp_name = (nv_string_t *)((uintptr_t)nvp + sizeof (*nvp));
found = 0;
- xdr_int(&p, &namelen);
- pairname = (const char *)p;
- p += roundup(namelen, 4);
- xdr_int(&p, &pairtype);
-
for (i = 0; features_for_read[i] != NULL; i++) {
- if (memcmp(pairname, features_for_read[i],
- namelen) == 0) {
+ if (memcmp(nvp_name->nv_data, features_for_read[i],
+ nvp_name->nv_size) == 0) {
found = 1;
break;
}
}
if (!found) {
- printf("ZFS: unsupported feature: %s\n", pairname);
+ printf("ZFS: unsupported feature: %.*s\n",
+ nvp_name->nv_size, nvp_name->nv_data);
rc = EIO;
}
-
- p = pair + encoded_size;
-
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
+ nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
}
+ nvlist_destroy(features);
return (rc);
}
-/*
- * Return the next nvlist in an nvlist array.
- */
-static const unsigned char *
-nvlist_next(const unsigned char *nvlist)
-{
- const unsigned char *p, *pair;
- int junk;
- int encoded_size, decoded_size;
-
- p = nvlist;
- xdr_int(&p, &junk);
- xdr_int(&p, &junk);
-
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
- while (encoded_size && decoded_size) {
- p = pair + encoded_size;
-
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
- }
-
- return (p);
-}
-
-#ifdef TEST
-
-static const unsigned char *
-nvlist_print(const unsigned char *nvlist, 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"
- };
-
- unsigned int i, j;
- const unsigned char *p, *pair;
- int junk;
- int encoded_size, decoded_size;
-
- p = nvlist;
- xdr_int(&p, &junk);
- xdr_int(&p, &junk);
-
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
- while (encoded_size && decoded_size) {
- int namelen, pairtype, elements;
- const char *pairname;
-
- xdr_int(&p, &namelen);
- pairname = (const char *)p;
- p += roundup(namelen, 4);
- xdr_int(&p, &pairtype);
-
- for (i = 0; i < indent; i++)
- printf(" ");
- printf("%s %.*s", typenames[pairtype], namelen, pairname);
-
- xdr_int(&p, &elements);
- switch (pairtype) {
- case DATA_TYPE_UINT64: {
- uint64_t val;
- xdr_uint64_t(&p, &val);
- printf(" = 0x%jx\n", (uintmax_t)val);
- break;
- }
-
- case DATA_TYPE_STRING: {
- int len;
- xdr_int(&p, &len);
- printf(" = \"%.*s\"\n", len, p);
- break;
- }
-
- case DATA_TYPE_NVLIST:
- printf("\n");
- nvlist_print(p, indent + 1);
- break;
-
- case DATA_TYPE_NVLIST_ARRAY:
- for (j = 0; j < elements; j++) {
- printf("[%d]\n", j);
- p = nvlist_print(p, indent + 1);
- if (j != elements - 1) {
- for (i = 0; i < indent; i++)
- printf(" ");
- printf("%s %.*s", typenames[pairtype],
- namelen, pairname);
- }
- }
- break;
-
- default:
- printf("\n");
- }
-
- p = pair + encoded_size;
-
- pair = p;
- xdr_int(&p, &encoded_size);
- xdr_int(&p, &decoded_size);
- }
-
- return (p);
-}
-
-#endif
-
static int
vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
off_t offset, size_t size)
@@ -450,8 +216,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);
@@ -459,7 +225,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);
@@ -468,6 +234,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 +859,7 @@ vdev_create(uint64_t guid, vdev_read_t *vdev_read)
}
static void
-vdev_set_initial_state(vdev_t *vdev, const unsigned char *nvlist)
+vdev_set_initial_state(vdev_t *vdev, const nvlist_t *nvlist)
{
uint64_t is_offline, is_faulted, is_degraded, is_removed, isnt_present;
uint64_t is_log;
@@ -1119,7 +894,7 @@ vdev_set_initial_state(vdev_t *vdev, const unsigned char *nvlist)
}
static int
-vdev_init(uint64_t guid, const unsigned char *nvlist, vdev_t **vdevp)
+vdev_init(uint64_t guid, const nvlist_t *nvlist, vdev_t **vdevp)
{
uint64_t id, ashift, asize, nparity;
const char *path;
@@ -1333,10 +1108,10 @@ vdev_insert(vdev_t *top_vdev, vdev_t *vdev)
}
static int
-vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const unsigned char *nvlist)
+vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const nvlist_t *nvlist)
{
vdev_t *top_vdev, *vdev;
- const unsigned char *kids;
+ nvlist_t **kids = NULL;
int rc, nkids;
/* Get top vdev. */
@@ -1357,19 +1132,18 @@ vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const unsigned char *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)
- return (rc);
- rc = vdev_init(guid, kids, &vdev);
+ goto done;
+
+ rc = vdev_init(guid, kids[i], &vdev);
if (rc != 0)
- return (rc);
+ goto done;
vdev->v_spa = spa;
vdev->v_top = top_vdev;
vdev_insert(top_vdev, vdev);
-
- kids = nvlist_next(kids);
}
} else {
/*
@@ -1378,15 +1152,22 @@ vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const unsigned char *nvlist)
*/
rc = 0;
}
+done:
+ if (kids != NULL) {
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
+ }
return (rc);
}
static int
-vdev_init_from_label(spa_t *spa, const unsigned char *nvlist)
+vdev_init_from_label(spa_t *spa, const nvlist_t *nvlist)
{
uint64_t pool_guid, top_guid;
- const unsigned char *vdevs;
+ nvlist_t *vdevs;
+ int rc;
if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
NULL, &pool_guid, NULL) ||
@@ -1398,7 +1179,9 @@ vdev_init_from_label(spa_t *spa, const unsigned char *nvlist)
return (ENOENT);
}
- return (vdev_from_nvlist(spa, top_guid, vdevs));
+ rc = vdev_from_nvlist(spa, top_guid, vdevs);
+ nvlist_destroy(vdevs);
+ return (rc);
}
static void
@@ -1447,10 +1230,10 @@ vdev_set_state(vdev_t *vdev)
}
static int
-vdev_update_from_nvlist(uint64_t top_guid, const unsigned char *nvlist)
+vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist)
{
vdev_t *vdev;
- const unsigned char *kids;
+ nvlist_t **kids = NULL;
int rc, nkids;
/* Update top vdev. */
@@ -1465,29 +1248,32 @@ vdev_update_from_nvlist(uint64_t top_guid, const unsigned char *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);
-
- kids = nvlist_next(kids);
+ vdev_set_initial_state(vdev, kids[i]);
}
} else {
rc = 0;
}
+ if (kids != NULL) {
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
+ }
return (rc);
}
static int
-vdev_init_from_nvlist(spa_t *spa, const unsigned char *nvlist)
+vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist)
{
uint64_t pool_guid, vdev_children;
- const unsigned char *vdevs, *kids;
+ nvlist_t *vdevs = NULL, **kids = NULL;
int rc, nkids;
if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
@@ -1501,13 +1287,16 @@ vdev_init_from_nvlist(spa_t *spa, const unsigned char *nvlist)
}
/* Wrong guid?! */
- if (spa->spa_guid != pool_guid)
+ if (spa->spa_guid != pool_guid) {
+ nvlist_destroy(vdevs);
return (EINVAL);
+ }
spa->spa_root_vdev->v_nchildren = vdev_children;
rc = nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
&nkids, &kids, NULL);
+ nvlist_destroy(vdevs);
/*
* MOS config has at least one child for root vdev.
@@ -1519,7 +1308,7 @@ vdev_init_from_nvlist(spa_t *spa, const unsigned char *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;
@@ -1528,12 +1317,16 @@ vdev_init_from_nvlist(spa_t *spa, const unsigned char *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);
+ rc = vdev_update_from_nvlist(guid, kids[i]);
if (rc != 0)
break;
- kids = nvlist_next(kids);
+ }
+ if (kids != NULL) {
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
}
/*
@@ -1568,29 +1361,17 @@ spa_find_by_name(const char *name)
return (NULL);
}
-spa_t *
-spa_get_primary(void)
+static spa_t *
+spa_find_by_dev(struct zfs_devdesc *dev)
{
- return (STAILQ_FIRST(&zfs_pools));
-}
-vdev_t *
-spa_get_primary_vdev(const spa_t *spa)
-{
- vdev_t *vdev;
- vdev_t *kid;
-
- if (spa == NULL)
- spa = spa_get_primary();
- if (spa == NULL)
- return (NULL);
- vdev = spa->spa_root_vdev;
- if (vdev == NULL)
+ if (dev->dd.d_dev->dv_type != DEVT_ZFS)
return (NULL);
- for (kid = STAILQ_FIRST(&vdev->v_children); kid != NULL;
- kid = STAILQ_FIRST(&vdev->v_children))
- vdev = kid;
- return (vdev);
+
+ if (dev->pool_guid == 0)
+ return (STAILQ_FIRST(&zfs_pools));
+
+ return (spa_find_by_guid(dev->pool_guid));
}
static spa_t *
@@ -1835,10 +1616,258 @@ 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 contain 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 = (uint8_t *)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:
+ /*
+ * if 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(unsigned char *nvl)
+vdev_get_label_asize(nvlist_t *nvl)
{
- unsigned char *vdevs;
+ nvlist_t *vdevs;
uint64_t asize;
const char *type;
int len;
@@ -1867,7 +1896,7 @@ vdev_get_label_asize(unsigned char *nvl)
goto done;
if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0) {
- unsigned char *kids;
+ nvlist_t **kids;
int nkids;
if (nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN,
@@ -1877,6 +1906,9 @@ vdev_get_label_asize(unsigned char *nvl)
}
asize /= nkids;
+ for (int i = 0; i < nkids; i++)
+ nvlist_destroy(kids[i]);
+ free(kids);
}
asize += VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
@@ -1884,48 +1916,44 @@ done:
return (asize);
}
-static unsigned char *
+static nvlist_t *
vdev_label_read_config(vdev_t *vd, uint64_t txg)
{
vdev_phys_t *label;
uint64_t best_txg = 0;
uint64_t label_txg = 0;
uint64_t asize;
- unsigned char *nvl;
- size_t nvl_size;
+ nvlist_t *nvl = NULL, *tmp;
int error;
label = malloc(sizeof (vdev_phys_t));
if (label == NULL)
return (NULL);
- nvl_size = VDEV_PHYS_SIZE - sizeof (zio_eck_t) - 4;
- nvl = malloc(nvl_size);
- if (nvl == NULL)
- goto done;
-
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;
- if (label->vp_nvlist[0] != NV_ENCODE_XDR)
+ tmp = nvlist_import(label->vp_nvlist,
+ sizeof (label->vp_nvlist));
+ if (tmp == NULL)
continue;
- nvlist = (const unsigned char *) label->vp_nvlist + 4;
- error = nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG,
+ error = nvlist_find(tmp, ZPOOL_CONFIG_POOL_TXG,
DATA_TYPE_UINT64, NULL, &label_txg, NULL);
if (error != 0 || label_txg == 0) {
- memcpy(nvl, nvlist, nvl_size);
+ nvlist_destroy(nvl);
+ nvl = tmp;
goto done;
}
if (label_txg <= txg && label_txg > best_txg) {
best_txg = label_txg;
- memcpy(nvl, nvlist, nvl_size);
+ nvlist_destroy(nvl);
+ nvl = tmp;
+ tmp = NULL;
/*
* Use asize from pool config. We need this
@@ -1936,10 +1964,11 @@ vdev_label_read_config(vdev_t *vd, uint64_t txg)
vd->v_psize = asize;
}
}
+ nvlist_destroy(tmp);
}
if (best_txg == 0) {
- free(nvl);
+ nvlist_destroy(nvl);
nvl = NULL;
}
done:
@@ -1973,17 +2002,17 @@ vdev_uberblock_load(vdev_t *vd, uberblock_t *ub)
}
static int
-vdev_probe(vdev_phys_read_t *phys_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;
vdev_t *vdev;
- unsigned char *nvlist;
+ nvlist_t *nvl;
uint64_t val;
uint64_t guid, vdev_children;
uint64_t pool_txg, pool_guid;
const char *pool_name;
- const unsigned char *features;
int rc, namelen;
/*
@@ -1991,63 +2020,63 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
* uberblock is most current.
*/
memset(&vtmp, 0, sizeof (vtmp));
- vtmp.v_phys_read = phys_read;
- vtmp.v_read_priv = read_priv;
- vtmp.v_psize = P2ALIGN(ldi_get_size(read_priv),
+ vtmp.v_phys_read = _read;
+ 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. */
if (vtmp.v_psize < SPA_MINDEVSIZE)
return (EIO);
- nvlist = vdev_label_read_config(&vtmp, UINT64_MAX);
- if (nvlist == NULL)
+ nvl = vdev_label_read_config(&vtmp, UINT64_MAX);
+ if (nvl == NULL)
return (EIO);
- if (nvlist_find(nvlist, ZPOOL_CONFIG_VERSION, DATA_TYPE_UINT64,
+ if (nvlist_find(nvl, ZPOOL_CONFIG_VERSION, DATA_TYPE_UINT64,
NULL, &val, NULL) != 0) {
- free(nvlist);
+ nvlist_destroy(nvl);
return (EIO);
}
if (!SPA_VERSION_IS_SUPPORTED(val)) {
printf("ZFS: unsupported ZFS version %u (should be %u)\n",
(unsigned)val, (unsigned)SPA_VERSION);
- free(nvlist);
+ nvlist_destroy(nvl);
return (EIO);
}
/* Check ZFS features for read */
- if (nvlist_find(nvlist, ZPOOL_CONFIG_FEATURES_FOR_READ,
- DATA_TYPE_NVLIST, NULL, &features, NULL) == 0 &&
- nvlist_check_features_for_read(features) != 0) {
- free(nvlist);
+ rc = nvlist_check_features_for_read(nvl);
+ if (rc != 0) {
+ nvlist_destroy(nvl);
return (EIO);
}
- if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64,
+ if (nvlist_find(nvl, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64,
NULL, &val, NULL) != 0) {
- free(nvlist);
+ nvlist_destroy(nvl);
return (EIO);
}
if (val == POOL_STATE_DESTROYED) {
/* We don't boot only from destroyed pools. */
- free(nvlist);
+ nvlist_destroy(nvl);
return (EIO);
}
- if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64,
+ if (nvlist_find(nvl, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64,
NULL, &pool_txg, NULL) != 0 ||
- nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
+ nvlist_find(nvl, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
NULL, &pool_guid, NULL) != 0 ||
- nvlist_find(nvlist, ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING,
+ nvlist_find(nvl, ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING,
NULL, &pool_name, &namelen) != 0) {
/*
* Cache and spare devices end up here - just ignore
* them.
*/
- free(nvlist);
+ nvlist_destroy(nvl);
return (EIO);
}
@@ -2058,11 +2087,11 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
if (spa == NULL) {
char *name;
- nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_CHILDREN,
+ nvlist_find(nvl, ZPOOL_CONFIG_VDEV_CHILDREN,
DATA_TYPE_UINT64, NULL, &vdev_children, NULL);
name = malloc(namelen + 1);
if (name == NULL) {
- free(nvlist);
+ nvlist_destroy(nvl);
return (ENOMEM);
}
bcopy(pool_name, name, namelen);
@@ -2070,7 +2099,7 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
spa = spa_create(pool_guid, name);
free(name);
if (spa == NULL) {
- free(nvlist);
+ nvlist_destroy(nvl);
return (ENOMEM);
}
spa->spa_root_vdev->v_nchildren = vdev_children;
@@ -2084,20 +2113,20 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
* be some kind of alias (overlapping slices, dangerously dedicated
* disks etc).
*/
- if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
+ if (nvlist_find(nvl, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
NULL, &guid, NULL) != 0) {
- free(nvlist);
+ nvlist_destroy(nvl);
return (EIO);
}
vdev = vdev_find(guid);
/* Has this vdev already been inited? */
if (vdev && vdev->v_phys_read) {
- free(nvlist);
+ nvlist_destroy(nvl);
return (EIO);
}
- rc = vdev_init_from_label(spa, nvlist);
- free(nvlist);
+ rc = vdev_init_from_label(spa, nvl);
+ nvlist_destroy(nvl);
if (rc != 0)
return (rc);
@@ -2107,8 +2136,9 @@ vdev_probe(vdev_phys_read_t *phys_read, void *read_priv, spa_t **spap)
*/
vdev = vdev_find(guid);
if (vdev != NULL) {
- vdev->v_phys_read = phys_read;
- vdev->v_read_priv = read_priv;
+ vdev->v_phys_read = _read;
+ vdev->v_phys_write = _write;
+ vdev->v_priv = priv;
vdev->v_psize = vtmp.v_psize;
/*
* If no other state is set, mark vdev healthy.
@@ -3382,12 +3412,12 @@ check_mos_features(const spa_t *spa)
}
static int
-load_nvlist(spa_t *spa, uint64_t obj, unsigned char **value)
+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)
@@ -3411,7 +3441,8 @@ load_nvlist(spa_t *spa, uint64_t obj, unsigned char **value)
nv = NULL;
return (rc);
}
- *value = nv;
+ *value = nvlist_import(nv, size);
+ free(nv);
return (rc);
}
@@ -3420,7 +3451,7 @@ zfs_spa_init(spa_t *spa)
{
dnode_phys_t dir;
uint64_t config_object;
- unsigned char *nvlist;
+ nvlist_t *nvlist;
int rc;
if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
@@ -3463,8 +3494,8 @@ zfs_spa_init(spa_t *spa)
* Update vdevs from MOS config. Note, we do skip encoding bytes
* here. See also vdev_label_read_config().
*/
- rc = vdev_init_from_nvlist(spa, nvlist + 4);
- free(nvlist);
+ rc = vdev_init_from_nvlist(spa, nvlist);
+ nvlist_destroy(nvlist);
return (rc);
}
diff --git a/usr/src/boot/sys/boot/common/bootstrap.h b/usr/src/boot/sys/boot/common/bootstrap.h
index 7cdeb6d1c4..1703045266 100644
--- a/usr/src/boot/sys/boot/common/bootstrap.h
+++ b/usr/src/boot/sys/boot/common/bootstrap.h
@@ -28,9 +28,9 @@
#define _BOOTSTRAP_H_
#include <sys/types.h>
-#include <stdbool.h>
#include <sys/queue.h>
#include <sys/linker_set.h>
+#include <stdbool.h>
/* Commands and return values; nonzero return sets command_errmsg != NULL */
typedef int (bootblk_cmd_t)(int argc, char *argv[]);
@@ -375,6 +375,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 /* Allow lint to override */
#define CTASSERT(x) _CTASSERT(x, __LINE__)
#define _CTASSERT(x, y) __CTASSERT(x, y)
diff --git a/usr/src/boot/sys/boot/common/nvstore.c b/usr/src/boot/sys/boot/common/nvstore.c
new file mode 100644
index 0000000000..b3e6cdbeaa
--- /dev/null
+++ b/usr/src/boot/sys/boot/common/nvstore.c
@@ -0,0 +1,309 @@
+/*
+ * 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>
+
+#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);
+}
diff --git a/usr/src/boot/sys/boot/efi/loader/Makefile.com b/usr/src/boot/sys/boot/efi/loader/Makefile.com
index 1c8abe2a8d..58dbcfb6f2 100644
--- a/usr/src/boot/sys/boot/efi/loader/Makefile.com
+++ b/usr/src/boot/sys/boot/efi/loader/Makefile.com
@@ -34,6 +34,7 @@ SRCS= \
memmap.c \
mb_header.S \
multiboot2.c \
+ nvstore.c \
self_reloc.c \
smbios.c \
tem.c \
@@ -53,6 +54,7 @@ OBJS= \
memmap.o \
mb_header.o \
multiboot2.o \
+ nvstore.o \
self_reloc.o \
smbios.o \
tem.o \
@@ -60,6 +62,7 @@ OBJS= \
module.o := CPPFLAGS += -I$(BOOTSRC)/libcrypto
tem.o := CPPFLAGS += $(DEFAULT_CONSOLE_COLOR)
+main.o := CPPFLAGS += -I$(SRC)/uts/common/fs/zfs
CPPFLAGS += -I../../../../../include -I../../..../
CPPFLAGS += -I../../../../../lib/libstand
diff --git a/usr/src/boot/sys/boot/efi/loader/main.c b/usr/src/boot/sys/boot/efi/loader/main.c
index b5b310b9ad..9d229be58b 100644
--- a/usr/src/boot/sys/boot/efi/loader/main.c
+++ b/usr/src/boot/sys/boot/efi/loader/main.c
@@ -32,6 +32,7 @@
#include <sys/reboot.h>
#include <sys/boot.h>
#include <sys/consplat.h>
+#include <sys/zfs_bootenv.h>
#include <stand.h>
#include <inttypes.h>
#include <string.h>
@@ -230,6 +231,23 @@ out:
}
static void
+set_currdev(const char *devname)
+{
+
+ /*
+ * Don't execute hooks here; we may need to try setting these more than
+ * once here if we're probing for the ZFS pool we're supposed to boot.
+ * The currdev hook is intended to just validate user input anyways,
+ * while the loaddev hook makes it immutable once we've determined what
+ * the proper currdev is.
+ */
+ env_setenv("currdev", EV_VOLATILE | EV_NOHOOK, devname, efi_setcurrdev,
+ env_nounset);
+ env_setenv("loaddev", EV_VOLATILE | EV_NOHOOK, devname, env_noset,
+ env_nounset);
+}
+
+static void
set_currdev_devdesc(struct devdesc *currdev)
{
char *devname;
@@ -237,10 +255,7 @@ set_currdev_devdesc(struct devdesc *currdev)
devname = efi_fmtdev(currdev);
printf("Setting currdev to %s\n", devname);
-
- env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
- env_nounset);
- env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
+ set_currdev(devname);
}
static void
@@ -294,6 +309,8 @@ static bool
probe_zfs_currdev(uint64_t guid)
{
struct zfs_devdesc currdev;
+ char *bootonce;
+ bool rv;
currdev.dd.d_dev = &zfs_dev;
currdev.dd.d_unit = 0;
@@ -301,7 +318,24 @@ probe_zfs_currdev(uint64_t guid)
currdev.root_guid = 0;
set_currdev_devdesc((struct devdesc *)&currdev);
- return (sanity_check_currdev());
+ rv = sanity_check_currdev();
+ if (rv) {
+ bootonce = malloc(VDEV_PAD_SIZE);
+ if (bootonce != NULL) {
+ if (zfs_get_bootonce(&currdev, OS_BOOTONCE, bootonce,
+ VDEV_PAD_SIZE) == 0) {
+ printf("zfs bootonce: %s\n", bootonce);
+ set_currdev(bootonce);
+ setenv("zfs-bootonce", bootonce, 1);
+ }
+ free(bootonce);
+ (void) zfs_attach_nvstore(&currdev);
+ } else {
+ printf("Failed to process bootonce data: %s\n",
+ strerror(errno));
+ }
+ }
+ return (rv);
}
static bool
diff --git a/usr/src/boot/sys/boot/i386/gptzfsboot/Makefile b/usr/src/boot/sys/boot/i386/gptzfsboot/Makefile
index fdae14f16b..c826966da5 100644
--- a/usr/src/boot/sys/boot/i386/gptzfsboot/Makefile
+++ b/usr/src/boot/sys/boot/i386/gptzfsboot/Makefile
@@ -50,9 +50,10 @@ CPPFLAGS += -DBOOTPROG=\"gptzfsboot\" \
LDSCRIPT= ../boot.ldscript
LD_FLAGS= -static -N --gc-sections
-LIBI386= ../libi386/libi386.a
-LIBSTAND= ../../libstand/$(MACH)/libstand.a
+LIBI386= -L ../libi386 -li386
+LIBSTAND= -L ../../libstand/$(MACH) -lstand
LIBS= $(LIBI386) $(LIBSTAND)
+DPADD= ../libi386/libi386.a ../../libstand/$(MACH)/libstand.a
include ../Makefile.inc
@@ -65,6 +66,8 @@ install: all $(ROOTBOOTPROG)
OBJS = mb_header.o zfsboot.o sio.o cons.o devopen.o \
part.o disk.o bcache.o zfs_cmd.o
+zfsboot.o := CPPFLAGS += -I$(SRC)/uts/common -I$(SRC)/uts/common/fs/zfs
+zfs_cmd.o := CPPFLAGS += -I$(SRC)/uts/common
part.o := CPPFLAGS += -I$(ZLIB)
smbios.o := CPPFLAGS += -DSMBIOS_SERIAL_NUMBERS
smbios.o := CPPFLAGS += -DSMBIOS_LITTLE_ENDIAN_UUID
@@ -89,7 +92,7 @@ CLEANFILES += gptzfsboot.bin gptzfsboot.out
gptzfsboot.bin: gptzfsboot.out
$(OBJCOPY) -S -O binary gptzfsboot.out $@
-gptzfsboot.out: $(BTXCRT) $(OBJS) $(LIBI386) $(LIBSTAND)
+gptzfsboot.out: $(BTXCRT) $(OBJS) $(DPADD)
$(LD) $(LD_FLAGS) -T $(LDSCRIPT) -o $@ $(BTXCRT) $(OBJS) $(LIBS)
machine:
diff --git a/usr/src/boot/sys/boot/i386/gptzfsboot/zfsboot.c b/usr/src/boot/sys/boot/i386/gptzfsboot/zfsboot.c
index 63086e3cd1..a8278eec04 100644
--- a/usr/src/boot/sys/boot/i386/gptzfsboot/zfsboot.c
+++ b/usr/src/boot/sys/boot/i386/gptzfsboot/zfsboot.c
@@ -24,6 +24,7 @@
#include <sys/reboot.h>
#include <sys/queue.h>
#include <sys/multiboot.h>
+#include <sys/zfs_bootenv.h>
#include <machine/bootinfo.h>
#include <machine/elf.h>
@@ -146,6 +147,7 @@ main(void)
unsigned i;
int fd;
bool auto_boot;
+ bool nextboot = false;
struct disk_devdesc devdesc;
bios_getmem();
@@ -201,11 +203,31 @@ 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_get_bootonce(bdev, OS_BOOTONCE, cmd,
+ sizeof (cmd)) == 0) {
+ nvlist_t *benv;
+
+ nextboot = true;
+ memcpy(cmddup, cmd, sizeof (cmd));
+ if (parse_cmd()) {
+ printf("failed to parse bootonce command\n");
+ exit(0);
+ }
+ if (!OPT_CHECK(RBX_QUIET))
+ 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;
+ }
}
/* now make sure we have bdev on all cases */
- if (bdev != NULL)
- free(bdev);
+ free(bdev);
i386_getdev((void **)&bdev, boot_devname, NULL);
env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev,
@@ -244,6 +266,10 @@ main(void)
*cmd = 0;
}
+ /* Do not risk waiting at the prompt forever. */
+ if (nextboot && !auto_boot)
+ exit(0);
+
if (auto_boot && !*kname) {
/*
* Try to exec stage 3 boot loader. If interrupted by a
diff --git a/usr/src/boot/sys/boot/i386/libi386/Makefile b/usr/src/boot/sys/boot/i386/libi386/Makefile
index d28628c0dd..aa11fc2276 100644
--- a/usr/src/boot/sys/boot/i386/libi386/Makefile
+++ b/usr/src/boot/sys/boot/i386/libi386/Makefile
@@ -131,6 +131,11 @@ CFLAGS += -I$(SRC)/common/ficl -I../../libficl \
# the location of libstand
CFLAGS += -I../../../../lib/libstand/
+multiboot.o := CPPFLAGS += -I../../../cddl/boot/zfs
+multiboot2.o := CPPFLAGS += -I../../../cddl/boot/zfs
+devicename.o := CPPFLAGS += -I../../../cddl/boot/zfs
+devicename_stubs.o := CPPFLAGS += -I../../../cddl/boot/zfs
+
# Handle FreeBSD specific %b and %D printf format specifiers
#FORMAT_EXTENSIONS=-D__printf__=__freebsd_kprintf__
#CFLAGS += ${FORMAT_EXTENSIONS}
diff --git a/usr/src/boot/sys/boot/i386/loader/Makefile b/usr/src/boot/sys/boot/i386/loader/Makefile
index e8d796b7fd..c8be1beb55 100644
--- a/usr/src/boot/sys/boot/i386/loader/Makefile
+++ b/usr/src/boot/sys/boot/i386/loader/Makefile
@@ -56,7 +56,7 @@ LIBFICL= ../../libficl/$(MACH)/libficl.a
# Always add MI sources
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 linenoise.c multiboot2.c
+SRCS += module.c linenoise.c multiboot2.c nvstore.c
SRCS += zfs_cmd.c
SRCS += font.c $(FONT).c tem.c
@@ -104,6 +104,11 @@ CPPFLAGS += -I../btx/lib
include ../Makefile.inc
+conf.o := CPPFLAGS += -I../../../cddl/boot/zfs
+multiboot2.o := CPPFLAGS += -I../../../cddl/boot/zfs
+main.o := CPPFLAGS += -I../../../cddl/boot/zfs -I$(SRC)/uts/common/fs/zfs
+zfs_cmd.o := CPPFLAGS += -I../../../cddl/boot/zfs
+
# For multiboot2.h, must be last, to avoid conflicts
CPPFLAGS += -I$(SRC)/uts/common
diff --git a/usr/src/boot/sys/boot/i386/loader/main.c b/usr/src/boot/sys/boot/i386/loader/main.c
index 169d0e1ce5..7c6fc2dcde 100644
--- a/usr/src/boot/sys/boot/i386/loader/main.c
+++ b/usr/src/boot/sys/boot/i386/loader/main.c
@@ -40,6 +40,7 @@
#include <sys/disk.h>
#include <sys/param.h>
#include <sys/reboot.h>
+#include <sys/zfs_bootenv.h>
#include <rbx.h>
#include "bootstrap.h"
@@ -208,6 +209,7 @@ extract_currdev(void)
{
struct i386_devdesc new_currdev;
struct zfs_boot_args *zargs;
+ char *bootonce;
int biosdev = -1;
/* Assume we are booting from a BIOS disk by default */
@@ -247,6 +249,17 @@ 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);
+ } else {
+ printf("Failed to process bootonce data: %s\n",
+ strerror(errno));
+ }
} else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
/* The passed-in boot device is bad */
new_currdev.d_kind.biosdisk.slice = -1;
diff --git a/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h b/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h
index 4993ca754c..43ec7e614e 100644
--- a/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h
+++ b/usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h
@@ -59,8 +59,12 @@
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
-#ifndef _ZFSIMPL_H
-#define _ZFSIMPL_H
+#ifndef _ZFSIMPL_H_
+#define _ZFSIMPL_H_
+
+#include <sys/queue.h>
+#include <sys/list.h>
+#include <bootstrap.h>
#define MAXNAMELEN 256
@@ -495,7 +499,7 @@ typedef struct zio_gbh {
#define VDEV_RAIDZ_MAXPARITY 3
#define VDEV_PAD_SIZE (8 << 10)
-/* 2 padding areas (vl_pad1 and vl_pad2) to skip */
+/* 2 padding areas (vl_pad1 and vl_be) to skip */
#define VDEV_SKIP_SIZE VDEV_PAD_SIZE * 2
#define VDEV_PHYS_SIZE (112 << 10)
#define VDEV_UBERBLOCK_RING (128 << 10)
@@ -521,9 +525,29 @@ typedef struct vdev_phys {
zio_eck_t vp_zbt;
} vdev_phys_t;
+typedef enum vbe_vers {
+ /* The bootenv file is stored as ascii text in the envblock */
+ VB_RAW = 0,
+
+ /*
+ * The bootenv file is converted to an nvlist and then packed into the
+ * envblock.
+ */
+ VB_NVLIST = 1
+} vbe_vers_t;
+
+typedef struct vdev_boot_envblock {
+ uint64_t vbe_version;
+ char vbe_bootenv[VDEV_PAD_SIZE - sizeof (uint64_t) -
+ sizeof (zio_eck_t)];
+ zio_eck_t vbe_zbt;
+} vdev_boot_envblock_t;
+
+CTASSERT(sizeof (vdev_boot_envblock_t) == VDEV_PAD_SIZE);
+
typedef struct vdev_label {
char vl_pad1[VDEV_PAD_SIZE]; /* 8K */
- char vl_pad2[VDEV_PAD_SIZE]; /* 8K */
+ vdev_boot_envblock_t vl_be; /* 8K */
vdev_phys_t vl_vdev_phys; /* 112K */
char vl_uberblock[VDEV_UBERBLOCK_RING]; /* 128K */
} vdev_label_t; /* 256K total */
@@ -899,6 +923,7 @@ typedef struct uberblock {
*/
uint64_t ub_mmp_delay;
+ /* BEGIN CSTYLED */
/*
* The ub_mmp_config contains the multihost write interval, multihost
* fail intervals, sequence number for sub-second granularity, and
@@ -917,6 +942,7 @@ typedef struct uberblock {
* - 0x04 - Fail Intervals
* - 0xf8 - Reserved
*/
+ /* END CSTYLED */
uint64_t ub_mmp_config;
/*
@@ -1020,6 +1046,7 @@ typedef struct dnode_phys {
uint64_t dn_pad3[4];
+ /* BEGIN CSTYLED */
/*
* The tail region is 448 bytes for a 512 byte dnode, and
* correspondingly larger for larger dnode sizes. The spill
@@ -1036,6 +1063,7 @@ typedef struct dnode_phys {
* | dn_blkptr[0] | dn_bonus[0..191] | dn_spill |
* +---------------+-----------------------+---------------+
*/
+ /* END CSTYLED */
union {
blkptr_t dn_blkptr[1+DN_OLD_MAX_BONUSLEN/sizeof (blkptr_t)];
struct {
@@ -1641,10 +1669,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;
@@ -1774,8 +1801,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 */
/*
@@ -1802,6 +1830,7 @@ typedef struct spa {
void *spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
vdev_t *spa_boot_vdev; /* boot device for kernel */
boolean_t spa_with_log; /* this pool has log */
+ void *spa_bootenv; /* bootenv from pool label */
} spa_t;
/* IO related arguments. */
@@ -1819,6 +1848,6 @@ typedef struct zio {
int io_error;
} zio_t;
-static void decode_embedded_bp_compressed(const blkptr_t *, void *);
+extern void decode_embedded_bp_compressed(const blkptr_t *, void *);
-#endif /* _ZFSIMPL_H */
+#endif /* _ZFSIMPL_H_ */
diff --git a/usr/src/cmd/beadm/beadm.c b/usr/src/cmd/beadm/beadm.c
index 1dfc8afcd1..8a89c380b4 100644
--- a/usr/src/cmd/beadm/beadm.c
+++ b/usr/src/cmd/beadm/beadm.c
@@ -129,7 +129,7 @@ usage(void)
"\n"
"\tsubcommands:\n"
"\n"
- "\tbeadm activate [-v] beName\n"
+ "\tbeadm activate [-v] [-t | -T] beName\n"
"\tbeadm create [-a] [-d BE_desc]\n"
"\t\t[-o property=value] ... [-p zpool] \n"
"\t\t[-e nonActiveBe | beName@snapshot] [-v] beName\n"
@@ -343,7 +343,7 @@ print_be_nodes(const char *be_name, boolean_t parsable, struct hdr_info *hdr,
be_node_list_t *cur_be;
for (cur_be = nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
- char active[3] = "-\0";
+ char active[4] = "-\0\0";
int ai = 0;
const char *datetime_fmt = "%F %R";
const char *name = cur_be->be_node_name;
@@ -374,9 +374,12 @@ print_be_nodes(const char *be_name, boolean_t parsable, struct hdr_info *hdr,
active[ai++] = 'N';
if (cur_be->be_active_on_boot) {
if (!cur_be->be_global_active)
- active[ai] = 'b';
+ active[ai++] = 'b';
else
- active[ai] = 'R';
+ active[ai++] = 'R';
+ }
+ if (cur_be->be_active_next) {
+ active[ai] = 'T';
}
nicenum(used, buf, sizeof (buf));
@@ -472,7 +475,7 @@ print_fmt_nodes(const char *be_name, enum be_fmt be_fmt, boolean_t parsable,
be_node_list_t *cur_be;
for (cur_be = nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
- char active[3] = "-\0";
+ char active[4] = "-\0\0";
int ai = 0;
const char *datetime_fmt = "%F %R";
const char *name = cur_be->be_node_name;
@@ -495,7 +498,9 @@ print_fmt_nodes(const char *be_name, enum be_fmt be_fmt, boolean_t parsable,
if (cur_be->be_active)
active[ai++] = 'N';
if (cur_be->be_active_on_boot)
- active[ai] = 'R';
+ active[ai++] = 'R';
+ if (cur_be->be_active_next)
+ active[ai++] = 'T';
nicenum(used, buf, sizeof (buf));
if (be_fmt & BE_FMT_DATASET)
@@ -606,6 +611,20 @@ be_nvl_alloc(nvlist_t **nvlp)
}
static int
+be_nvl_add_boolean(nvlist_t *nvl, const char *name, boolean_t val)
+{
+ assert(nvl != NULL);
+
+ if (nvlist_add_boolean_value(nvl, name, val) != 0) {
+ (void) fprintf(stderr, _("nvlist_add_boolean_value failed for "
+ "%s (%s).\n"), name, val ? "true" : "false");
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
be_nvl_add_string(nvlist_t *nvl, const char *name, const char *val)
{
assert(nvl != NULL);
@@ -654,12 +673,30 @@ be_do_activate(int argc, char **argv)
int err = 1;
int c;
char *obe_name;
+ boolean_t nextboot = B_FALSE;
+ boolean_t do_nextboot = B_FALSE;
- while ((c = getopt(argc, argv, "v")) != -1) {
+ while ((c = getopt(argc, argv, "vtT")) != -1) {
switch (c) {
case 'v':
libbe_print_errors(B_TRUE);
break;
+ case 't':
+ if (do_nextboot == B_TRUE) {
+ usage();
+ return (1);
+ }
+ nextboot = B_TRUE;
+ do_nextboot = B_TRUE;
+ break;
+ case 'T':
+ if (do_nextboot == B_TRUE) {
+ usage();
+ return (1);
+ }
+ nextboot = B_FALSE;
+ do_nextboot = B_TRUE;
+ break;
default:
usage();
return (1);
@@ -682,11 +719,20 @@ be_do_activate(int argc, char **argv)
if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0)
goto out;
+ if (do_nextboot == B_TRUE) {
+ if (be_nvl_add_boolean(be_attrs, BE_ATTR_ACTIVE_NEXTBOOT,
+ nextboot) != 0)
+ goto out;
+ }
+
err = be_activate(be_attrs);
switch (err) {
case BE_SUCCESS:
- (void) printf(_("Activated successfully\n"));
+ if (do_nextboot && nextboot == B_FALSE)
+ (void) printf(_("Temporary activation removed\n"));
+ else
+ (void) printf(_("Activated successfully\n"));
break;
case BE_ERR_BE_NOENT:
(void) fprintf(stderr, _("%s does not exist or appear "
diff --git a/usr/src/cmd/boot/bootadm/bootadm_loader.c b/usr/src/cmd/boot/bootadm/bootadm_loader.c
index 15bf160745..e2d99b24b0 100644
--- a/usr/src/cmd/boot/bootadm/bootadm_loader.c
+++ b/usr/src/cmd/boot/bootadm/bootadm_loader.c
@@ -66,6 +66,7 @@ extern char *bam_root;
typedef struct menu_entry {
int me_idx;
boolean_t me_active;
+ boolean_t me_active_next;
char *me_title;
char *me_type;
char *me_bootfs;
@@ -114,6 +115,10 @@ print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
(void) snprintf(buf, bufsize, "%s", entry->me_type);
break;
case 4:
+ if (entry->me_active_next == B_TRUE) {
+ (void) snprintf(buf, bufsize, " T");
+ break;
+ }
if (entry->me_active == B_TRUE)
(void) snprintf(buf, bufsize, " *");
else
@@ -266,6 +271,26 @@ menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs)
return (rv);
}
+/*
+ * Get the be_active_next for bootfs.
+ */
+static boolean_t
+menu_active_next(be_node_list_t *be_nodes, const char *bootfs)
+{
+ be_node_list_t *be_node;
+ boolean_t rv = B_FALSE;
+
+ for (be_node = be_nodes; be_node != NULL;
+ be_node = be_node->be_next_node) {
+ if (strcmp(be_node->be_root_ds, bootfs) == 0) {
+ rv = be_node->be_active_next;
+ break;
+ }
+ }
+
+ return (rv);
+}
+
error_t
menu_read(struct menu_lst *menu, char *menu_path)
{
@@ -335,6 +360,7 @@ menu_read(struct menu_lst *menu, char *menu_path)
mp->me_type = type;
mp->me_bootfs = bootfs;
mp->me_active = menu_active_on_boot(be_nodes, bootfs);
+ mp->me_active_next = menu_active_next(be_nodes, bootfs);
STAILQ_INSERT_TAIL(menu, mp, me_next);
title = NULL;
diff --git a/usr/src/cmd/eeprom/i386/Makefile b/usr/src/cmd/eeprom/i386/Makefile
index d47fd832d1..baf63db774 100644
--- a/usr/src/cmd/eeprom/i386/Makefile
+++ b/usr/src/cmd/eeprom/i386/Makefile
@@ -28,6 +28,7 @@ SRCDIR = ..
include $(SRCDIR)/Makefile.com
OBJS += benv.o benv_kvm.o
+LDLIBS += -lzfsbootenv
.KEEP_STATE:
diff --git a/usr/src/cmd/eeprom/i386/benv.c b/usr/src/cmd/eeprom/i386/benv.c
index 0e3a89f0b7..697bcd9a19 100644
--- a/usr/src/cmd/eeprom/i386/benv.c
+++ b/usr/src/cmd/eeprom/i386/benv.c
@@ -27,10 +27,12 @@
#include "benv.h"
#include <ctype.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
+#include <libzfsbootenv.h>
/*
* Usage: % eeprom [-v] [-f prom_dev] [-]
@@ -671,33 +673,295 @@ get_line(void)
return (NULL);
}
+static int
+add_pair(const char *name, const char *nvlist, const char *key,
+ const char *type, const char *value)
+{
+ void *data, *nv;
+ size_t size;
+ int rv;
+ char *end;
+
+ rv = lzbe_nvlist_get(name, nvlist, &nv);
+ if (rv != 0)
+ return (rv);
+
+ data = NULL;
+ rv = EINVAL;
+ if (strcmp(type, "DATA_TYPE_STRING") == 0) {
+ data = (void *)(uintptr_t)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) {
+ u_longlong_t lv;
+ uint32_t v;
+
+ lv = strtoull(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ if (lv > UINT32_MAX)
+ goto done;
+ v = lv;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_INT32") == 0) {
+ longlong_t lv;
+ int32_t v;
+
+ lv = strtoll(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ if (lv < INT32_MIN || lv > INT32_MAX)
+ goto done;
+ v = lv;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_UINT16") == 0) {
+ uint32_t lv;
+ uint16_t v;
+
+ lv = strtoul(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ if (lv > UINT16_MAX)
+ goto done;
+ v = lv;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_INT16") == 0) {
+ int32_t lv;
+ int16_t v;
+
+ v = strtol(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ if (lv < INT16_MIN || lv > INT16_MAX)
+ goto done;
+ v = lv;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_UINT8") == 0) {
+ uint32_t lv;
+ uint8_t v;
+
+ lv = strtoul(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ if (lv > UINT8_MAX)
+ goto done;
+ v = lv;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_INT8") == 0) {
+ int32_t lv;
+ int8_t v;
+
+ lv = strtol(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ if (lv < INT8_MIN || lv > INT8_MAX)
+ goto done;
+ v = lv;
+ size = sizeof (v);
+ rv = lzbe_add_pair(nv, key, type, &v, size);
+ } else if (strcmp(type, "DATA_TYPE_BYTE") == 0) {
+ uint32_t lv;
+ uint8_t v;
+
+ lv = strtoul(value, &end, 0);
+ if (errno != 0 || *end != '\0')
+ goto done;
+ if (lv > UINT8_MAX)
+ goto done;
+ v = lv;
+ 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;
+ else 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 (rv == 0)
+ rv = lzbe_nvlist_set(name, nvlist, nv);
+
+done:
+ lzbe_nvlist_free(nv);
+ return (rv);
+}
+
+static int
+delete_pair(const char *name, const char *nvlist, const char *key)
+{
+ void *nv;
+ int rv;
+
+ 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);
+}
+
+static int
+usage(char *name)
+{
+ char *usage = "Usage: %s [-v] [-f prom-device]"
+ " [variable[=value] ...]\n"
+ "%s [-z pool] [-d key] [-k key -t type -v value] [-p]\n"
+ "%s [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p]\n";
+
+ return (_error(NO_PERROR, usage, name, name, name));
+}
+
int
main(int argc, char **argv)
{
int c;
int updates = 0;
- char *usage = "Usage: %s [-v] [-f prom-device]"
- " [variable[=value] ...]";
eplist_t *elist;
benv_des_t *bd;
char *file = NULL;
+ bool bootenv, bootenv_print, bootenv_delete;
+ char *name, *key, *type, *nvlist, *value;
+ lzbe_flags_t flag = lzbe_add;
+
+ nvlist = NULL;
+ name = "rpool";
+ key = NULL;
+ type = NULL;
+ value = NULL;
+ bootenv = false;
+ bootenv_print = false;
+ bootenv_delete = false;
setpname(argv[0]);
- while ((c = getopt(argc, argv, "f:Itv")) != -1)
+ while ((c = getopt(argc, argv, "bd:f:k:n:prt:v:z:")) != -1)
switch (c) {
- case 'v':
- verbose++;
+ case 'b':
+ bootenv = true;
+ break;
+ case 'd':
+ if (bootenv) {
+ bootenv_delete = true;
+ key = optarg;
+ } else {
+ exit(usage(argv[0]));
+ }
break;
case 'f':
file = optarg;
break;
+ case 'k':
+ if (bootenv)
+ key = optarg;
+ else
+ exit(usage(argv[0]));
+ break;
+ case 'n':
+ if (bootenv)
+ nvlist = optarg;
+ else
+ exit(usage(argv[0]));
+ break;
+ case 'p':
+ if (bootenv)
+ bootenv_print = true;
+ else
+ exit(usage(argv[0]));
+ break;
+ case 'r':
+ if (bootenv)
+ flag = lzbe_replace;
+ else
+ exit(usage(argv[0]));
+ break;
case 't':
- test++;
+ if (bootenv)
+ type = optarg;
+ else
+ test++;
+ break;
+ case 'v':
+ if (bootenv)
+ value = optarg;
+ else
+ verbose++;
+ break;
+ case 'z':
+ if (bootenv)
+ name = optarg;
+ else
+ exit(usage(argv[0]));
break;
default:
- exit(_error(NO_PERROR, usage, argv[0]));
+ exit(usage(argv[0]));
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (bootenv) {
+ int rv = 0;
+
+ if (argc == 1)
+ value = argv[0];
+
+ if (bootenv_print)
+ return (lzbe_bootenv_print(name, nvlist, stdout));
+
+ if (key != NULL || value != NULL) {
+ if (type == NULL)
+ type = "DATA_TYPE_STRING";
+
+ if (bootenv_delete)
+ rv = delete_pair(name, nvlist, key);
+ else if (key == NULL)
+ rv = lzbe_set_boot_device(name, flag, value);
+ else
+ rv = add_pair(name, nvlist, key, type, value);
+
+ if (rv == 0)
+ printf("zfs bootenv is successfully written\n");
+ else
+ printf("error: %s\n", strerror(rv));
}
+ return (rv);
+ }
(void) uname(&uts_buf);
bd = new_bd();
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 55230ca831..e3b3889305 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -603,7 +603,8 @@ krb5: gss_mechs/mech_krb5 libtecla libldap5
libads: libnsl
libadt_jni: libbsm
libadutils: libldap5 libresolv2
-libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl
+libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl \
+ libzfsbootenv
libbsm: libinetutil libscf libsecdb libtsol
libcfgadm: libdevinfo
libcontract: libnvpair
diff --git a/usr/src/lib/libbe/Makefile.com b/usr/src/lib/libbe/Makefile.com
index 969de425f3..ede66a0afc 100644
--- a/usr/src/lib/libbe/Makefile.com
+++ b/usr/src/lib/libbe/Makefile.com
@@ -52,7 +52,7 @@ INCS += -I$(SRCDIR) -I$(SRC)/cmd/boot/common -I$(SRC)/common/ficl
CSTD= $(CSTD_GNU99)
LDLIBS += -lficl-sys -lzfs -linstzones -luuid -lnvpair -lc -lgen
-LDLIBS += -ldevinfo -lefi
+LDLIBS += -ldevinfo -lefi -lzfsbootenv
CPPFLAGS += $(INCS)
CLOBBERFILES += $(LIBRARY)
diff --git a/usr/src/lib/libbe/common/be_activate.c b/usr/src/lib/libbe/common/be_activate.c
index 1c2454991d..6133f2ca59 100644
--- a/usr/src/lib/libbe/common/be_activate.c
+++ b/usr/src/lib/libbe/common/be_activate.c
@@ -47,6 +47,7 @@
#include <libbe.h>
#include <libbe_priv.h>
+#include <libzfsbootenv.h>
char *mnttab = MNTTAB;
@@ -92,6 +93,8 @@ be_activate(nvlist_t *be_attrs)
{
int ret = BE_SUCCESS;
char *be_name = NULL;
+ be_nextboot_state_t nextboot;
+ boolean_t next_boot;
/* Initialize libzfs handle */
if (!be_zfs_init())
@@ -114,7 +117,17 @@ be_activate(nvlist_t *be_attrs)
return (BE_ERR_INVAL);
}
- ret = _be_activate(be_name);
+ if (nvlist_lookup_boolean_value(be_attrs, BE_ATTR_ACTIVE_NEXTBOOT,
+ &next_boot) == 0) {
+ if (next_boot)
+ nextboot = BE_NEXTBOOT_SET;
+ else
+ nextboot = BE_NEXTBOOT_UNSET;
+ } else {
+ nextboot = BE_NEXTBOOT_IGNORE;
+ }
+
+ ret = _be_activate(be_name, nextboot);
be_zfs_fini();
@@ -206,6 +219,7 @@ be_installboot(nvlist_t *be_attrs)
* Description: This does the actual work described in be_activate.
* Parameters:
* be_name - pointer to the name of BE to activate.
+ * nextboot - flag to ignore, set or unset nextboot
*
* Return:
* BE_SUCCESS - Success
@@ -214,7 +228,7 @@ be_installboot(nvlist_t *be_attrs)
* Public
*/
int
-_be_activate(char *be_name)
+_be_activate(char *be_name, be_nextboot_state_t nextboot)
{
be_transaction_data_t cb = { 0 };
zfs_handle_t *zhp = NULL;
@@ -233,6 +247,9 @@ _be_activate(char *be_name)
if (be_name == NULL)
return (BE_ERR_INVAL);
+ if (nextboot == BE_NEXTBOOT_SET && getzoneid() != GLOBAL_ZONEID)
+ return (BE_ERR_INVAL);
+
/* Set obe_name to be_name in the cb structure */
cb.obe_name = be_name;
@@ -288,11 +305,31 @@ _be_activate(char *be_name)
}
if (getzoneid() == GLOBAL_ZONEID) {
- if ((ret = set_bootfs(be_nodes->be_rpool,
- root_ds)) != BE_SUCCESS) {
- be_print_err(gettext("be_activate: failed to set "
- "bootfs pool property for %s\n"), root_ds);
- goto done;
+ switch (nextboot) {
+ case BE_NEXTBOOT_SET:
+ if ((ret = lzbe_set_boot_device(be_nodes->be_rpool,
+ lzbe_add, root_ds)) != 0) {
+ be_print_err(gettext("be_activate: failed to "
+ "set nextboot for %s\n"), root_ds);
+ goto done;
+ }
+ break;
+ case BE_NEXTBOOT_UNSET:
+ if ((ret = lzbe_set_boot_device(be_nodes->be_rpool,
+ lzbe_add, "")) != 0) {
+ be_print_err(gettext("be_activate: failed to "
+ "clear nextboot for %s\n"), root_ds);
+ goto done;
+ }
+ break;
+ default:
+ if ((ret = set_bootfs(be_nodes->be_rpool,
+ root_ds)) != BE_SUCCESS) {
+ be_print_err(gettext("be_activate: failed to "
+ "set bootfs pool property for %s\n"),
+ root_ds);
+ goto done;
+ }
}
}
@@ -415,7 +452,8 @@ be_activate_current_be(void)
return (ret);
}
- if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) {
+ ret = _be_activate(bt.obe_name, BE_NEXTBOOT_IGNORE);
+ if (ret != BE_SUCCESS) {
be_print_err(gettext("be_activate_current_be: failed to "
"activate %s\n"), bt.obe_name);
return (ret);
diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c
index e2a5bf901c..fbccd111aa 100644
--- a/usr/src/lib/libbe/common/be_list.c
+++ b/usr/src/lib/libbe/common/be_list.c
@@ -46,6 +46,7 @@
#include <libbe.h>
#include <libbe_priv.h>
+#include <libzfsbootenv.h>
/*
* Callback data used for zfs_iter calls.
@@ -985,13 +986,8 @@ be_qsort_compare_datasets(const void *x, const void *y)
* Private
*/
static int
-be_get_node_data(
- zfs_handle_t *zhp,
- be_node_list_t *be_node,
- char *be_name,
- const char *rpool,
- char *current_be,
- char *be_ds)
+be_get_node_data(zfs_handle_t *zhp, be_node_list_t *be_node, char *be_name,
+ const char *rpool, char *current_be, char *be_ds)
{
char prop_buf[MAXPATHLEN];
nvlist_t *userprops = NULL;
@@ -1040,6 +1036,8 @@ be_get_node_data(
be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED);
if (getzoneid() == GLOBAL_ZONEID) {
+ char *nextboot;
+
if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
be_print_err(gettext("be_get_node_data: failed to open "
"pool (%s): %s\n"), rpool,
@@ -1047,6 +1045,16 @@ be_get_node_data(
return (zfs_err_to_be_err(g_zfs));
}
+ /* Set nextboot info */
+ be_node->be_active_next = B_FALSE;
+ if (lzbe_get_boot_device(rpool, &nextboot) == 0) {
+ if (nextboot != NULL) {
+ if (strcmp(nextboot, be_ds) == 0)
+ be_node->be_active_next = B_TRUE;
+ free(nextboot);
+ }
+ }
+
(void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf,
ZFS_MAXPROPLEN, NULL, B_FALSE);
if (be_has_grub() && (be_default_grub_bootfs(rpool,
diff --git a/usr/src/lib/libbe/common/libbe.h b/usr/src/lib/libbe/common/libbe.h
index b670d86ce5..774fe1ef9f 100644
--- a/usr/src/lib/libbe/common/libbe.h
+++ b/usr/src/lib/libbe/common/libbe.h
@@ -67,6 +67,7 @@ extern "C" {
#define BE_ATTR_ACTIVE "active"
#define BE_ATTR_ACTIVE_ON_BOOT "active_boot"
+#define BE_ATTR_ACTIVE_NEXTBOOT "active_nextboot"
#define BE_ATTR_GLOBAL_ACTIVE "global_active"
#define BE_ATTR_SPACE "space_used"
#define BE_ATTR_DATASET "dataset"
@@ -174,6 +175,7 @@ typedef struct be_snapshot_list {
typedef struct be_node_list {
boolean_t be_mounted; /* is BE currently mounted */
+ boolean_t be_active_next; /* is this BE active on next boot */
boolean_t be_active_on_boot; /* is this BE active on boot */
boolean_t be_active; /* is this BE active currently */
boolean_t be_global_active; /* is zone's BE associated with */
diff --git a/usr/src/lib/libbe/common/libbe_priv.h b/usr/src/lib/libbe/common/libbe_priv.h
index f2960c4f17..ace201577f 100644
--- a/usr/src/lib/libbe/common/libbe_priv.h
+++ b/usr/src/lib/libbe/common/libbe_priv.h
@@ -142,6 +142,12 @@ struct be_defaults {
char be_deflt_bename_starts_with[ZFS_MAX_DATASET_NAME_LEN];
};
+typedef enum be_nextboot_state {
+ BE_NEXTBOOT_IGNORE = -1,
+ BE_NEXTBOOT_SET,
+ BE_NEXTBOOT_UNSET
+} be_nextboot_state_t;
+
/* Library globals */
extern libzfs_handle_t *g_zfs;
extern boolean_t do_print;
@@ -201,7 +207,7 @@ int zfs_err_to_be_err(libzfs_handle_t *);
int errno_to_be_err(int);
/* be_activate.c */
-int _be_activate(char *);
+int _be_activate(char *, be_nextboot_state_t);
int be_activate_current_be(void);
boolean_t be_is_active_on_boot(char *);
diff --git a/usr/src/man/man1m/beadm.1m b/usr/src/man/man1m/beadm.1m
index 29d470a392..250f4b4400 100644
--- a/usr/src/man/man1m/beadm.1m
+++ b/usr/src/man/man1m/beadm.1m
@@ -1,7 +1,7 @@
'\" te
.\" Copyright 2013 Nexenta Systems, Inc. All rights reserved.
.\" Copyright 2016 Toomas Soome <tsoome@me.com>
-.TH BEADM 1M "Feb 21, 2016"
+.TH BEADM 1M "May 2, 2020"
.SH NAME
beadm \- utility for managing zfs boot environments
.SH SYNOPSIS
@@ -45,7 +45,7 @@ beadm \- utility for managing zfs boot environments
.LP
.nf
-\fBbeadm\fR \fBactivate\fR [\fB-v\fR] \fIbeName\fR
+\fBbeadm\fR \fBactivate\fR [\fB-v\fR] [\fB-t\fR | \fB-T\fR] \fIbeName\fR
.fi
.LP
@@ -338,7 +338,9 @@ The 'Active'
field indicates whether the boot environment is active now,
represented
by 'N'; active on reboot, represented by 'R'; or both, represented
-by 'NR'. In non-global zone the 'Active' field also indicates whether the
+by 'NR'. If temporary next boot configuration is used, the boot environment
+is marked with 'T'.
+In non-global zone the 'Active' field also indicates whether the
boot environment has a non-active parent BE, represented by 'x'; is active
on boot in a non-active parent BE, represented by 'b'. Activate, rollback
and snapshot operations for boot environments from non-active global parent
@@ -539,7 +541,7 @@ Verbose mode. Displays verbose error messages from \fBbeadm\fR.
.sp
.ne 2
.na
-\fBbeadm\fR \fBactivate\fR [\fB-v\fR] \fIbeName\fR
+\fBbeadm\fR \fBactivate\fR [\fB-v\fR] [\fB-t\fR | \fB-T\fR] \fIbeName\fR
.ad
.sp .6
.RS 4n
@@ -547,6 +549,29 @@ Makes beName the active BE on next reboot.
.sp
.ne 2
.na
+\fB-t\fR
+.ad
+.sp .6
+.RS 4n
+Sets temporary, one time activation. For next boot, the \fIbeName\fR
+is used for boot, and the temporary activation is removed.
+When temporary activation is removed, the next boot will use zfs dataset
+specified in boot pool \fIbootfs\fR property.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-T\fR
+.ad
+.sp .6
+.RS 4n
+Removes temporary next boot configuration of \fIbeName\fR.
+.RE
+
+.sp
+.ne 2
+.na
\fB-v\fR
.ad
.sp .6
@@ -793,6 +818,32 @@ BE5;215b8387-4968-627c-d2d0-f4a011414bab;NR;/;7786206208;static;1221004384
.fi
.in -2
.sp
+.LP
+\fBExample 19\fR: Create and list temporary activation.
+
+.sp
+.in +2
+.nf
+\fB# beadm list\fR
+BE Active Mountpoint Space Policy Created
+-- ------ ---------- ----- ------ -------
+BE2 - - 4,85M static 2016-10-30 14:29
+BE3 - - 4,84M static 2016-10-30 18:53
+BE4 - - 5,54M static 2016-10-30 23:53
+BE5 NR / 12,6G static 2016-11-06 01:46
+\fB# beadm create BE6\fR
+\fB# beadm activate -t BE6\fR
+\fB# beadm list\fR
+BE Active Mountpoint Space Policy Created
+-- ------ ---------- ----- ------ -------
+BE2 - - 4,85M static 2016-10-30 14:29
+BE3 - - 4,84M static 2016-10-30 18:53
+BE4 - - 5,54M static 2016-10-30 23:53
+BE5 NR / 60,0K static 2016-11-06 01:46
+BE6 T - 12,6G static 2016-11-06 12:56
+.fi
+.in -2
+.sp
.SH EXIT STATUS
The following exit values are returned: