summaryrefslogtreecommitdiff
path: root/usr/src/boot/lib
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/boot/lib')
-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
5 files changed, 2923 insertions, 379 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);
}