diff options
| author | Jason King <jason.king@joyent.com> | 2020-11-16 16:37:31 +0000 |
|---|---|---|
| committer | Jason King <jason.king@joyent.com> | 2020-11-16 16:37:31 +0000 |
| commit | 5ffcb7f72a2f7a214b40ea8d0ef402f68aeada38 (patch) | |
| tree | 86b767ee8b3625c219d1f5438eb25530c9703534 /usr/src/boot | |
| parent | 45f7bf1d4508f8d85c7add1bdeb9f7ea099f0ab2 (diff) | |
| parent | de0f04687a2a3fe3692d9ad1254738343bf9c4eb (diff) | |
| download | illumos-joyent-5ffcb7f72a2f7a214b40ea8d0ef402f68aeada38.tar.gz | |
[illumos-gate merge]
commit de0f04687a2a3fe3692d9ad1254738343bf9c4eb
13310 Remove auto_ef.3ext as we don't have the software
commit 25befe07d3c1488cbbdecdb765cd0558e12cc364
13302 pthread_attr_get_np.3c erroneously refers to pthread_getattr_np
commit 509a605d87b8005c687f8d8264f1be379620e886
13304 bhyve ioport handling bungled on reinit
commit 83cd75bb2949d26e6eb38ddefc60fdeed1909643
13309 bhyve movs emulation leaks mem refcnt
commit b713c91e508f40be7797bedd4ae1146ef0652625
7537 want nextboot (one time boot) support
commit 09fcda9fe16a733cc35aa3156a47ef4b909251a6
13172 Port OpenZFS: zfs label bootenv should store data as nvlist
commit c4ecba8aa5f13f00c2439c06af2aa1198771ee66
13025 Port OpenZFS: Add support for boot environment data to be stored in the label
commit 1a2acdcd3ce765904dbf2bfc511e92d68022d100
13308 testrunner/run needs updates for python 3.9
commit 04573c73a7ab1505c46b2c4db26bfde5176dd6a5
13286 bhyve ins/outs emulation misuses %rax
commit 3dfdac06b0c70e672dbe56a2f38ec05fc0254d07
13278 CTF assertion failed cmp->cm_tmap[id].cmt_map == suid
commit a676209deb2ce5d0c98f331659de25e2483f8c4c
13252 ctf_update()/ctf_dwarf_convert_function() leak memory
commit effb27ee30c48fe502152c38487ced379d9f8693
13247 CTF conversion fails with large files
13251 CTF conversion fails if any CU is missing DWARF data
Conflicts:
usr/src/test/test-runner/cmd/run
usr/src/lib/libctf/common/libctf.h
usr/src/lib/libctf/common/ctf_convert.c
Diffstat (limited to 'usr/src/boot')
| -rw-r--r-- | usr/src/boot/lib/libstand/zfs/Makefile.inc | 3 | ||||
| -rw-r--r-- | usr/src/boot/lib/libstand/zfs/libzfs.h | 115 | ||||
| -rw-r--r-- | usr/src/boot/lib/libstand/zfs/nvlist.c | 1568 | ||||
| -rw-r--r-- | usr/src/boot/lib/libstand/zfs/zfs.c | 863 | ||||
| -rw-r--r-- | usr/src/boot/lib/libstand/zfs/zfsimpl.c | 753 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/common/bootstrap.h | 33 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/common/nvstore.c | 309 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/efi/loader/Makefile.com | 3 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/efi/loader/main.c | 44 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/i386/gptzfsboot/Makefile | 9 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/i386/gptzfsboot/zfsboot.c | 30 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/i386/libi386/Makefile | 5 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/i386/loader/Makefile | 7 | ||||
| -rw-r--r-- | usr/src/boot/sys/boot/i386/loader/main.c | 13 | ||||
| -rw-r--r-- | usr/src/boot/sys/cddl/boot/zfs/zfsimpl.h | 51 |
15 files changed, 3404 insertions, 402 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 adb6b80192..d4556f349a 100644 --- a/usr/src/boot/sys/boot/efi/loader/Makefile.com +++ b/usr/src/boot/sys/boot/efi/loader/Makefile.com @@ -36,6 +36,7 @@ SRCS= \ memmap.c \ mb_header.S \ multiboot2.c \ + nvstore.c \ self_reloc.c \ smbios.c \ tem.c \ @@ -55,6 +56,7 @@ OBJS= \ memmap.o \ mb_header.o \ multiboot2.o \ + nvstore.o \ self_reloc.o \ smbios.o \ tem.o \ @@ -62,6 +64,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 5071ec31bc..a710ce808c 100644 --- a/usr/src/boot/sys/boot/i386/loader/Makefile +++ b/usr/src/boot/sys/boot/i386/loader/Makefile @@ -57,7 +57,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 @@ -105,6 +105,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_ */ |
