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 | |
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
78 files changed, 6062 insertions, 1091 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_ */ diff --git a/usr/src/cmd/beadm/beadm.c b/usr/src/cmd/beadm/beadm.c index 1dfc8afcd1..8a89c380b4 100644 --- a/usr/src/cmd/beadm/beadm.c +++ b/usr/src/cmd/beadm/beadm.c @@ -129,7 +129,7 @@ usage(void) "\n" "\tsubcommands:\n" "\n" - "\tbeadm activate [-v] beName\n" + "\tbeadm activate [-v] [-t | -T] beName\n" "\tbeadm create [-a] [-d BE_desc]\n" "\t\t[-o property=value] ... [-p zpool] \n" "\t\t[-e nonActiveBe | beName@snapshot] [-v] beName\n" @@ -343,7 +343,7 @@ print_be_nodes(const char *be_name, boolean_t parsable, struct hdr_info *hdr, be_node_list_t *cur_be; for (cur_be = nodes; cur_be != NULL; cur_be = cur_be->be_next_node) { - char active[3] = "-\0"; + char active[4] = "-\0\0"; int ai = 0; const char *datetime_fmt = "%F %R"; const char *name = cur_be->be_node_name; @@ -374,9 +374,12 @@ print_be_nodes(const char *be_name, boolean_t parsable, struct hdr_info *hdr, active[ai++] = 'N'; if (cur_be->be_active_on_boot) { if (!cur_be->be_global_active) - active[ai] = 'b'; + active[ai++] = 'b'; else - active[ai] = 'R'; + active[ai++] = 'R'; + } + if (cur_be->be_active_next) { + active[ai] = 'T'; } nicenum(used, buf, sizeof (buf)); @@ -472,7 +475,7 @@ print_fmt_nodes(const char *be_name, enum be_fmt be_fmt, boolean_t parsable, be_node_list_t *cur_be; for (cur_be = nodes; cur_be != NULL; cur_be = cur_be->be_next_node) { - char active[3] = "-\0"; + char active[4] = "-\0\0"; int ai = 0; const char *datetime_fmt = "%F %R"; const char *name = cur_be->be_node_name; @@ -495,7 +498,9 @@ print_fmt_nodes(const char *be_name, enum be_fmt be_fmt, boolean_t parsable, if (cur_be->be_active) active[ai++] = 'N'; if (cur_be->be_active_on_boot) - active[ai] = 'R'; + active[ai++] = 'R'; + if (cur_be->be_active_next) + active[ai++] = 'T'; nicenum(used, buf, sizeof (buf)); if (be_fmt & BE_FMT_DATASET) @@ -606,6 +611,20 @@ be_nvl_alloc(nvlist_t **nvlp) } static int +be_nvl_add_boolean(nvlist_t *nvl, const char *name, boolean_t val) +{ + assert(nvl != NULL); + + if (nvlist_add_boolean_value(nvl, name, val) != 0) { + (void) fprintf(stderr, _("nvlist_add_boolean_value failed for " + "%s (%s).\n"), name, val ? "true" : "false"); + return (1); + } + + return (0); +} + +static int be_nvl_add_string(nvlist_t *nvl, const char *name, const char *val) { assert(nvl != NULL); @@ -654,12 +673,30 @@ be_do_activate(int argc, char **argv) int err = 1; int c; char *obe_name; + boolean_t nextboot = B_FALSE; + boolean_t do_nextboot = B_FALSE; - while ((c = getopt(argc, argv, "v")) != -1) { + while ((c = getopt(argc, argv, "vtT")) != -1) { switch (c) { case 'v': libbe_print_errors(B_TRUE); break; + case 't': + if (do_nextboot == B_TRUE) { + usage(); + return (1); + } + nextboot = B_TRUE; + do_nextboot = B_TRUE; + break; + case 'T': + if (do_nextboot == B_TRUE) { + usage(); + return (1); + } + nextboot = B_FALSE; + do_nextboot = B_TRUE; + break; default: usage(); return (1); @@ -682,11 +719,20 @@ be_do_activate(int argc, char **argv) if (be_nvl_add_string(be_attrs, BE_ATTR_ORIG_BE_NAME, obe_name) != 0) goto out; + if (do_nextboot == B_TRUE) { + if (be_nvl_add_boolean(be_attrs, BE_ATTR_ACTIVE_NEXTBOOT, + nextboot) != 0) + goto out; + } + err = be_activate(be_attrs); switch (err) { case BE_SUCCESS: - (void) printf(_("Activated successfully\n")); + if (do_nextboot && nextboot == B_FALSE) + (void) printf(_("Temporary activation removed\n")); + else + (void) printf(_("Activated successfully\n")); break; case BE_ERR_BE_NOENT: (void) fprintf(stderr, _("%s does not exist or appear " diff --git a/usr/src/cmd/boot/bootadm/bootadm_loader.c b/usr/src/cmd/boot/bootadm/bootadm_loader.c index 15bf160745..e2d99b24b0 100644 --- a/usr/src/cmd/boot/bootadm/bootadm_loader.c +++ b/usr/src/cmd/boot/bootadm/bootadm_loader.c @@ -66,6 +66,7 @@ extern char *bam_root; typedef struct menu_entry { int me_idx; boolean_t me_active; + boolean_t me_active_next; char *me_title; char *me_type; char *me_bootfs; @@ -114,6 +115,10 @@ print_menu_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) (void) snprintf(buf, bufsize, "%s", entry->me_type); break; case 4: + if (entry->me_active_next == B_TRUE) { + (void) snprintf(buf, bufsize, " T"); + break; + } if (entry->me_active == B_TRUE) (void) snprintf(buf, bufsize, " *"); else @@ -266,6 +271,26 @@ menu_active_on_boot(be_node_list_t *be_nodes, const char *bootfs) return (rv); } +/* + * Get the be_active_next for bootfs. + */ +static boolean_t +menu_active_next(be_node_list_t *be_nodes, const char *bootfs) +{ + be_node_list_t *be_node; + boolean_t rv = B_FALSE; + + for (be_node = be_nodes; be_node != NULL; + be_node = be_node->be_next_node) { + if (strcmp(be_node->be_root_ds, bootfs) == 0) { + rv = be_node->be_active_next; + break; + } + } + + return (rv); +} + error_t menu_read(struct menu_lst *menu, char *menu_path) { @@ -335,6 +360,7 @@ menu_read(struct menu_lst *menu, char *menu_path) mp->me_type = type; mp->me_bootfs = bootfs; mp->me_active = menu_active_on_boot(be_nodes, bootfs); + mp->me_active_next = menu_active_next(be_nodes, bootfs); STAILQ_INSERT_TAIL(menu, mp, me_next); title = NULL; diff --git a/usr/src/cmd/ctfconvert/ctfconvert.c b/usr/src/cmd/ctfconvert/ctfconvert.c index 0e37d070f1..bae355be22 100644 --- a/usr/src/cmd/ctfconvert/ctfconvert.c +++ b/usr/src/cmd/ctfconvert/ctfconvert.c @@ -37,6 +37,7 @@ #define CTFCONVERT_FATAL 1 #define CTFCONVERT_USAGE 2 +#define CTFCONVERT_DEFAULT_BATCHSIZE 256 #define CTFCONVERT_DEFAULT_NTHREADS 4 static char *ctfconvert_progname; @@ -67,17 +68,21 @@ ctfconvert_usage(const char *fmt, ...) va_end(ap); } - (void) fprintf(stderr, "Usage: %s [-ims] [-j nthrs] [-l label | " - "-L labelenv] [-o outfile] input\n" + (void) fprintf(stderr, "Usage: %s [-ikm] [-j nthrs] [-l label | " + "-L labelenv] [-b batchsize]\n" + " [-o outfile] input\n" "\n" + "\t-b batch process this many dies at a time (default %d)\n" "\t-i ignore files not built partially from C sources\n" - "\t-j use nthrs threads to perform the merge\n" + "\t-j use nthrs threads to perform the merge (default %d)\n" "\t-k keep around original input file on failure\n" - "\t-m allow input to have missing debug info\n" - "\t-o copy input to outfile and add CTF\n" "\t-l set output container's label to specified value\n" - "\t-L set output container's label to value from environment\n", - ctfconvert_progname); + "\t-L set output container's label to value from environment\n" + "\t-m allow input to have missing debug info\n" + "\t-o copy input to outfile and add CTF\n", + ctfconvert_progname, + CTFCONVERT_DEFAULT_BATCHSIZE, + CTFCONVERT_DEFAULT_NTHREADS); } /* @@ -226,35 +231,48 @@ main(int argc, char *argv[]) int c, ifd, err; boolean_t keep = B_FALSE; uint_t flags = 0; + uint_t bsize = CTFCONVERT_DEFAULT_BATCHSIZE; uint_t nthreads = CTFCONVERT_DEFAULT_NTHREADS; const char *outfile = NULL; const char *label = NULL; const char *infile = NULL; char *tmpfile; ctf_file_t *ofp; - long argj; - char *eptr; char buf[4096]; boolean_t optx = B_FALSE; boolean_t ignore_non_c = B_FALSE; ctfconvert_progname = basename(argv[0]); - while ((c = getopt(argc, argv, ":ij:kl:L:mo:X")) != -1) { + while ((c = getopt(argc, argv, ":b:ij:kl:L:mo:X")) != -1) { switch (c) { + case 'b': { + long argno; + const char *errstr; + + argno = strtonum(optarg, 1, UINT_MAX, &errstr); + if (errstr != NULL) { + ctfconvert_fatal("invalid argument for -b: " + "%s - %s\n", optarg, errstr); + } + bsize = (uint_t)argno; + break; + } case 'i': ignore_non_c = B_TRUE; break; - case 'j': - errno = 0; - argj = strtol(optarg, &eptr, 10); - if (errno != 0 || argj == LONG_MAX || - argj > 1024 || *eptr != '\0') { + case 'j': { + long argno; + const char *errstr; + + argno = strtonum(optarg, 1, 1024, &errstr); + if (errstr != NULL) { ctfconvert_fatal("invalid argument for -j: " - "%s\n", optarg); + "%s - %s\n", optarg, errstr); } - nthreads = (uint_t)argj; + nthreads = (uint_t)argno; break; + } case 'k': keep = B_TRUE; break; @@ -309,7 +327,7 @@ main(int argc, char *argv[]) if (outfile != NULL && strcmp(infile, outfile) != 0) keep = B_TRUE; - ofp = ctf_fdconvert(ifd, label, nthreads, flags, &err, buf, + ofp = ctf_fdconvert(ifd, label, bsize, nthreads, flags, &err, buf, sizeof (buf)); if (ofp == NULL) { /* diff --git a/usr/src/cmd/eeprom/i386/Makefile b/usr/src/cmd/eeprom/i386/Makefile index d47fd832d1..baf63db774 100644 --- a/usr/src/cmd/eeprom/i386/Makefile +++ b/usr/src/cmd/eeprom/i386/Makefile @@ -28,6 +28,7 @@ SRCDIR = .. include $(SRCDIR)/Makefile.com OBJS += benv.o benv_kvm.o +LDLIBS += -lzfsbootenv .KEEP_STATE: diff --git a/usr/src/cmd/eeprom/i386/benv.c b/usr/src/cmd/eeprom/i386/benv.c index 0e3a89f0b7..697bcd9a19 100644 --- a/usr/src/cmd/eeprom/i386/benv.c +++ b/usr/src/cmd/eeprom/i386/benv.c @@ -27,10 +27,12 @@ #include "benv.h" #include <ctype.h> #include <stdarg.h> +#include <stdbool.h> #include <sys/mman.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> +#include <libzfsbootenv.h> /* * Usage: % eeprom [-v] [-f prom_dev] [-] @@ -671,33 +673,295 @@ get_line(void) return (NULL); } +static int +add_pair(const char *name, const char *nvlist, const char *key, + const char *type, const char *value) +{ + void *data, *nv; + size_t size; + int rv; + char *end; + + rv = lzbe_nvlist_get(name, nvlist, &nv); + if (rv != 0) + return (rv); + + data = NULL; + rv = EINVAL; + if (strcmp(type, "DATA_TYPE_STRING") == 0) { + data = (void *)(uintptr_t)value; + size = strlen(data) + 1; + rv = lzbe_add_pair(nv, key, type, data, size); + } else if (strcmp(type, "DATA_TYPE_UINT64") == 0) { + uint64_t v; + + v = strtoull(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_INT64") == 0) { + int64_t v; + + v = strtoll(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_UINT32") == 0) { + u_longlong_t lv; + uint32_t v; + + lv = strtoull(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + if (lv > UINT32_MAX) + goto done; + v = lv; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_INT32") == 0) { + longlong_t lv; + int32_t v; + + lv = strtoll(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + if (lv < INT32_MIN || lv > INT32_MAX) + goto done; + v = lv; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_UINT16") == 0) { + uint32_t lv; + uint16_t v; + + lv = strtoul(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + if (lv > UINT16_MAX) + goto done; + v = lv; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_INT16") == 0) { + int32_t lv; + int16_t v; + + v = strtol(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + if (lv < INT16_MIN || lv > INT16_MAX) + goto done; + v = lv; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_UINT8") == 0) { + uint32_t lv; + uint8_t v; + + lv = strtoul(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + if (lv > UINT8_MAX) + goto done; + v = lv; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_INT8") == 0) { + int32_t lv; + int8_t v; + + lv = strtol(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + if (lv < INT8_MIN || lv > INT8_MAX) + goto done; + v = lv; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_BYTE") == 0) { + uint32_t lv; + uint8_t v; + + lv = strtoul(value, &end, 0); + if (errno != 0 || *end != '\0') + goto done; + if (lv > UINT8_MAX) + goto done; + v = lv; + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) { + int32_t v; + + v = strtol(value, &end, 0); + if (errno != 0 || *end != '\0') { + if (strcasecmp(value, "YES") == 0) + v = 1; + else if (strcasecmp(value, "NO") == 0) + v = 0; + else if (strcasecmp(value, "true") == 0) + v = 1; + else if (strcasecmp(value, "false") == 0) + v = 0; + else goto done; + } + size = sizeof (v); + rv = lzbe_add_pair(nv, key, type, &v, size); + } + + if (rv == 0) + rv = lzbe_nvlist_set(name, nvlist, nv); + +done: + lzbe_nvlist_free(nv); + return (rv); +} + +static int +delete_pair(const char *name, const char *nvlist, const char *key) +{ + void *nv; + int rv; + + rv = lzbe_nvlist_get(name, nvlist, &nv); + if (rv == 0) + rv = lzbe_remove_pair(nv, key); + + if (rv == 0) + rv = lzbe_nvlist_set(name, nvlist, nv); + + lzbe_nvlist_free(nv); + return (rv); +} + +static int +usage(char *name) +{ + char *usage = "Usage: %s [-v] [-f prom-device]" + " [variable[=value] ...]\n" + "%s [-z pool] [-d key] [-k key -t type -v value] [-p]\n" + "%s [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p]\n"; + + return (_error(NO_PERROR, usage, name, name, name)); +} + int main(int argc, char **argv) { int c; int updates = 0; - char *usage = "Usage: %s [-v] [-f prom-device]" - " [variable[=value] ...]"; eplist_t *elist; benv_des_t *bd; char *file = NULL; + bool bootenv, bootenv_print, bootenv_delete; + char *name, *key, *type, *nvlist, *value; + lzbe_flags_t flag = lzbe_add; + + nvlist = NULL; + name = "rpool"; + key = NULL; + type = NULL; + value = NULL; + bootenv = false; + bootenv_print = false; + bootenv_delete = false; setpname(argv[0]); - while ((c = getopt(argc, argv, "f:Itv")) != -1) + while ((c = getopt(argc, argv, "bd:f:k:n:prt:v:z:")) != -1) switch (c) { - case 'v': - verbose++; + case 'b': + bootenv = true; + break; + case 'd': + if (bootenv) { + bootenv_delete = true; + key = optarg; + } else { + exit(usage(argv[0])); + } break; case 'f': file = optarg; break; + case 'k': + if (bootenv) + key = optarg; + else + exit(usage(argv[0])); + break; + case 'n': + if (bootenv) + nvlist = optarg; + else + exit(usage(argv[0])); + break; + case 'p': + if (bootenv) + bootenv_print = true; + else + exit(usage(argv[0])); + break; + case 'r': + if (bootenv) + flag = lzbe_replace; + else + exit(usage(argv[0])); + break; case 't': - test++; + if (bootenv) + type = optarg; + else + test++; + break; + case 'v': + if (bootenv) + value = optarg; + else + verbose++; + break; + case 'z': + if (bootenv) + name = optarg; + else + exit(usage(argv[0])); break; default: - exit(_error(NO_PERROR, usage, argv[0])); + exit(usage(argv[0])); + } + + argc -= optind; + argv += optind; + + if (bootenv) { + int rv = 0; + + if (argc == 1) + value = argv[0]; + + if (bootenv_print) + return (lzbe_bootenv_print(name, nvlist, stdout)); + + if (key != NULL || value != NULL) { + if (type == NULL) + type = "DATA_TYPE_STRING"; + + if (bootenv_delete) + rv = delete_pair(name, nvlist, key); + else if (key == NULL) + rv = lzbe_set_boot_device(name, flag, value); + else + rv = add_pair(name, nvlist, key, type, value); + + if (rv == 0) + printf("zfs bootenv is successfully written\n"); + else + printf("error: %s\n", strerror(rv)); } + return (rv); + } (void) uname(&uts_buf); bd = new_bd(); diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index 107b36ee87..fd6e02bad6 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -1301,6 +1301,10 @@ const struct ioc { "zfs_cmd_t" }, { (uint_t)ZFS_IOC_CHANGE_KEY, "ZFS_IOC_CHANGE_KEY", "zfs_cmd_t" }, + { (uint_t)ZFS_IOC_SET_BOOTENV, "ZFS_IOC_SET_BOOTENV", + "zfs_cmd_t" }, + { (uint_t)ZFS_IOC_GET_BOOTENV, "ZFS_IOC_GET_BOOTENV", + "zfs_cmd_t" }, /* kssl ioctls */ { (uint_t)KSSL_ADD_ENTRY, "KSSL_ADD_ENTRY", diff --git a/usr/src/cmd/zinject/translate.c b/usr/src/cmd/zinject/translate.c index 546009ab88..32cff30e16 100644 --- a/usr/src/cmd/zinject/translate.c +++ b/usr/src/cmd/zinject/translate.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, 2020 by Delphix. All rights reserved. */ #include <libzfs.h> @@ -407,7 +407,7 @@ translate_device(const char *pool, const char *device, err_type_t label_type, record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1; break; case TYPE_LABEL_PAD2: - record->zi_start = offsetof(vdev_label_t, vl_pad2); + record->zi_start = offsetof(vdev_label_t, vl_be); record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1; break; } diff --git a/usr/src/common/ctf/ctf_create.c b/usr/src/common/ctf/ctf_create.c index c1027aa60e..abbfeddc76 100644 --- a/usr/src/common/ctf/ctf_create.c +++ b/usr/src/common/ctf/ctf_create.c @@ -26,6 +26,7 @@ */ /* * Copyright 2020 Joyent, Inc. + * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ #include <sys/sysmacros.h> @@ -671,6 +672,7 @@ ctf_update(ctf_file_t *fp) nfp->ctf_refcnt = fp->ctf_refcnt; nfp->ctf_flags |= fp->ctf_flags & ~LCTF_DIRTY; + nfp->ctf_flags |= LCTF_FREE; nfp->ctf_dthash = fp->ctf_dthash; nfp->ctf_dthashlen = fp->ctf_dthashlen; nfp->ctf_dtdefs = fp->ctf_dtdefs; diff --git a/usr/src/common/ctf/ctf_hash.c b/usr/src/common/ctf/ctf_hash.c index 0c5a71a5ac..f97115a791 100644 --- a/usr/src/common/ctf/ctf_hash.c +++ b/usr/src/common/ctf/ctf_hash.c @@ -23,6 +23,8 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ #include <ctf_impl.h> @@ -175,3 +177,25 @@ ctf_hash_destroy(ctf_hash_t *hp) hp->h_chains = NULL; } } + +void +ctf_hash_dump(const char *tag, ctf_hash_t *hp, ctf_file_t *fp) +{ + ctf_dprintf("---------------\nHash dump - %s\n", tag); + + for (ushort_t h = 0; h < hp->h_nbuckets; h++) { + ctf_helem_t *hep; + + for (ushort_t i = hp->h_buckets[h]; i != 0; i = hep->h_next) { + ctf_strs_t *ctsp; + const char *str; + + hep = &hp->h_chains[i]; + ctsp = &fp->ctf_str[CTF_NAME_STID(hep->h_name)]; + str = ctsp->cts_strs + CTF_NAME_OFFSET(hep->h_name); + + ctf_dprintf(" - %3u/%3u - '%s' type %u\n", h, i, str, + hep->h_type); + } + } +} diff --git a/usr/src/common/ctf/ctf_impl.h b/usr/src/common/ctf/ctf_impl.h index 7064abad15..b208cfb486 100644 --- a/usr/src/common/ctf/ctf_impl.h +++ b/usr/src/common/ctf/ctf_impl.h @@ -26,6 +26,7 @@ */ /* * Copyright 2020 Joyent, Inc. + * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ #ifndef _CTF_IMPL_H @@ -248,6 +249,11 @@ struct ctf_file { #define LCTF_CHILD 0x0002 /* CTF container is a child */ #define LCTF_RDWR 0x0004 /* CTF container is writable */ #define LCTF_DIRTY 0x0008 /* CTF container has been modified */ +/* + * The storage for this CTF container was allocated via ctf_data_alloc() + * and libctf should free it with ctf_data_free() on close. + */ +#define LCTF_FREE 0x0010 #define CTF_ELF_SCN_NAME ".SUNW_ctf" @@ -267,6 +273,7 @@ extern ctf_helem_t *ctf_hash_lookup(ctf_hash_t *, ctf_file_t *, const char *, size_t); extern uint_t ctf_hash_size(const ctf_hash_t *); extern void ctf_hash_destroy(ctf_hash_t *); +extern void ctf_hash_dump(const char *, ctf_hash_t *, ctf_file_t *); #define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev)) #define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next)) diff --git a/usr/src/common/ctf/ctf_open.c b/usr/src/common/ctf/ctf_open.c index 82b396e825..fe0644b1b0 100644 --- a/usr/src/common/ctf/ctf_open.c +++ b/usr/src/common/ctf/ctf_open.c @@ -26,6 +26,7 @@ */ /* * Copyright (c) 2015, Joyent, Inc. All rights reserved. + * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ #include <ctf_impl.h> @@ -936,6 +937,10 @@ ctf_close(ctf_file_t *fp) if (fp->ctf_strtab.cts_data != NULL) ctf_sect_munmap(&fp->ctf_strtab); } + if (fp->ctf_flags & LCTF_FREE) { + ctf_data_free((void *)fp->ctf_data.cts_data, + fp->ctf_data.cts_size); + } if (fp->ctf_data.cts_name != _CTF_NULLSTR && fp->ctf_data.cts_name != NULL) { diff --git a/usr/src/common/ctf/ctf_types.c b/usr/src/common/ctf/ctf_types.c index 6456f8a042..1fad60ddd4 100644 --- a/usr/src/common/ctf/ctf_types.c +++ b/usr/src/common/ctf/ctf_types.c @@ -26,6 +26,7 @@ */ /* * Copyright 2020 Joyent, Inc. + * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ #include <ctf_impl.h> @@ -369,8 +370,24 @@ ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len, cdp->cd_type, want_func_args); vname = NULL; break; - case CTF_K_STRUCT: case CTF_K_FORWARD: + switch (tp->ctt_type) { + case CTF_K_UNION: + ctf_decl_sprintf(&cd, "union "); + break; + case CTF_K_ENUM: + ctf_decl_sprintf(&cd, "enum "); + break; + case CTF_K_STRUCT: + default: + ctf_decl_sprintf(&cd, "struct "); + break; + } + if (qname != NULL) + ctf_decl_sprintf(&cd, "%s`", qname); + ctf_decl_sprintf(&cd, "%s", name); + break; + case CTF_K_STRUCT: ctf_decl_sprintf(&cd, "struct "); if (qname != NULL) ctf_decl_sprintf(&cd, "%s`", qname); diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index d6d16ece16..2418a2ee02 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -251,6 +251,7 @@ SUBDIRS += \ libxnet \ libzdoor \ libzfs \ + libzfsbootenv \ libzfs_core \ libzfs_jni \ libzonecfg \ @@ -614,7 +615,8 @@ krb5: gss_mechs/mech_krb5 libtecla libldap5 libads: libnsl libadt_jni: libbsm libadutils: libldap5 libresolv2 -libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl +libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl \ + libzfsbootenv libbsm: libinetutil libscf libsecdb libtsol libbunyan: libumem libcfgadm: libdevinfo @@ -719,6 +721,7 @@ libvscan: libscf libsecdb libzdoor: libc libzonecfg libcontract libzfs: libdevid libgen libuutil libavl libefi libidmap \ libumem libtsol libzfs_core libcryptoutil pkcs11 libmd libzutil +libzfsbootenv: libzfs libnvpair libzfs_core: libnvpair libzfs_jni: libdiskmgt libzfs libzutil libzonecfg: libuuid libsysevent libsec libbrand libpool libscf libproc \ diff --git a/usr/src/lib/libbe/Makefile.com b/usr/src/lib/libbe/Makefile.com index 969de425f3..ede66a0afc 100644 --- a/usr/src/lib/libbe/Makefile.com +++ b/usr/src/lib/libbe/Makefile.com @@ -52,7 +52,7 @@ INCS += -I$(SRCDIR) -I$(SRC)/cmd/boot/common -I$(SRC)/common/ficl CSTD= $(CSTD_GNU99) LDLIBS += -lficl-sys -lzfs -linstzones -luuid -lnvpair -lc -lgen -LDLIBS += -ldevinfo -lefi +LDLIBS += -ldevinfo -lefi -lzfsbootenv CPPFLAGS += $(INCS) CLOBBERFILES += $(LIBRARY) diff --git a/usr/src/lib/libbe/common/be_activate.c b/usr/src/lib/libbe/common/be_activate.c index 1c2454991d..6133f2ca59 100644 --- a/usr/src/lib/libbe/common/be_activate.c +++ b/usr/src/lib/libbe/common/be_activate.c @@ -47,6 +47,7 @@ #include <libbe.h> #include <libbe_priv.h> +#include <libzfsbootenv.h> char *mnttab = MNTTAB; @@ -92,6 +93,8 @@ be_activate(nvlist_t *be_attrs) { int ret = BE_SUCCESS; char *be_name = NULL; + be_nextboot_state_t nextboot; + boolean_t next_boot; /* Initialize libzfs handle */ if (!be_zfs_init()) @@ -114,7 +117,17 @@ be_activate(nvlist_t *be_attrs) return (BE_ERR_INVAL); } - ret = _be_activate(be_name); + if (nvlist_lookup_boolean_value(be_attrs, BE_ATTR_ACTIVE_NEXTBOOT, + &next_boot) == 0) { + if (next_boot) + nextboot = BE_NEXTBOOT_SET; + else + nextboot = BE_NEXTBOOT_UNSET; + } else { + nextboot = BE_NEXTBOOT_IGNORE; + } + + ret = _be_activate(be_name, nextboot); be_zfs_fini(); @@ -206,6 +219,7 @@ be_installboot(nvlist_t *be_attrs) * Description: This does the actual work described in be_activate. * Parameters: * be_name - pointer to the name of BE to activate. + * nextboot - flag to ignore, set or unset nextboot * * Return: * BE_SUCCESS - Success @@ -214,7 +228,7 @@ be_installboot(nvlist_t *be_attrs) * Public */ int -_be_activate(char *be_name) +_be_activate(char *be_name, be_nextboot_state_t nextboot) { be_transaction_data_t cb = { 0 }; zfs_handle_t *zhp = NULL; @@ -233,6 +247,9 @@ _be_activate(char *be_name) if (be_name == NULL) return (BE_ERR_INVAL); + if (nextboot == BE_NEXTBOOT_SET && getzoneid() != GLOBAL_ZONEID) + return (BE_ERR_INVAL); + /* Set obe_name to be_name in the cb structure */ cb.obe_name = be_name; @@ -288,11 +305,31 @@ _be_activate(char *be_name) } if (getzoneid() == GLOBAL_ZONEID) { - if ((ret = set_bootfs(be_nodes->be_rpool, - root_ds)) != BE_SUCCESS) { - be_print_err(gettext("be_activate: failed to set " - "bootfs pool property for %s\n"), root_ds); - goto done; + switch (nextboot) { + case BE_NEXTBOOT_SET: + if ((ret = lzbe_set_boot_device(be_nodes->be_rpool, + lzbe_add, root_ds)) != 0) { + be_print_err(gettext("be_activate: failed to " + "set nextboot for %s\n"), root_ds); + goto done; + } + break; + case BE_NEXTBOOT_UNSET: + if ((ret = lzbe_set_boot_device(be_nodes->be_rpool, + lzbe_add, "")) != 0) { + be_print_err(gettext("be_activate: failed to " + "clear nextboot for %s\n"), root_ds); + goto done; + } + break; + default: + if ((ret = set_bootfs(be_nodes->be_rpool, + root_ds)) != BE_SUCCESS) { + be_print_err(gettext("be_activate: failed to " + "set bootfs pool property for %s\n"), + root_ds); + goto done; + } } } @@ -415,7 +452,8 @@ be_activate_current_be(void) return (ret); } - if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) { + ret = _be_activate(bt.obe_name, BE_NEXTBOOT_IGNORE); + if (ret != BE_SUCCESS) { be_print_err(gettext("be_activate_current_be: failed to " "activate %s\n"), bt.obe_name); return (ret); diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c index 926b7260da..3e0833ea83 100644 --- a/usr/src/lib/libbe/common/be_list.c +++ b/usr/src/lib/libbe/common/be_list.c @@ -47,6 +47,7 @@ #include <libbe.h> #include <libbe_priv.h> +#include <libzfsbootenv.h> /* * Callback data used for zfs_iter calls. @@ -986,13 +987,8 @@ be_qsort_compare_datasets(const void *x, const void *y) * Private */ static int -be_get_node_data( - zfs_handle_t *zhp, - be_node_list_t *be_node, - char *be_name, - const char *rpool, - char *current_be, - char *be_ds) +be_get_node_data(zfs_handle_t *zhp, be_node_list_t *be_node, char *be_name, + const char *rpool, char *current_be, char *be_ds) { char prop_buf[MAXPATHLEN]; nvlist_t *userprops = NULL; @@ -1041,6 +1037,8 @@ be_get_node_data( be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED); if (getzoneid() == GLOBAL_ZONEID) { + char *nextboot; + if ((zphp = zpool_open(g_zfs, rpool)) == NULL) { be_print_err(gettext("be_get_node_data: failed to open " "pool (%s): %s\n"), rpool, @@ -1048,6 +1046,16 @@ be_get_node_data( return (zfs_err_to_be_err(g_zfs)); } + /* Set nextboot info */ + be_node->be_active_next = B_FALSE; + if (lzbe_get_boot_device(rpool, &nextboot) == 0) { + if (nextboot != NULL) { + if (strcmp(nextboot, be_ds) == 0) + be_node->be_active_next = B_TRUE; + free(nextboot); + } + } + (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN, NULL, B_FALSE); if (be_has_grub() && (be_default_grub_bootfs(rpool, diff --git a/usr/src/lib/libbe/common/libbe.h b/usr/src/lib/libbe/common/libbe.h index b670d86ce5..774fe1ef9f 100644 --- a/usr/src/lib/libbe/common/libbe.h +++ b/usr/src/lib/libbe/common/libbe.h @@ -67,6 +67,7 @@ extern "C" { #define BE_ATTR_ACTIVE "active" #define BE_ATTR_ACTIVE_ON_BOOT "active_boot" +#define BE_ATTR_ACTIVE_NEXTBOOT "active_nextboot" #define BE_ATTR_GLOBAL_ACTIVE "global_active" #define BE_ATTR_SPACE "space_used" #define BE_ATTR_DATASET "dataset" @@ -174,6 +175,7 @@ typedef struct be_snapshot_list { typedef struct be_node_list { boolean_t be_mounted; /* is BE currently mounted */ + boolean_t be_active_next; /* is this BE active on next boot */ boolean_t be_active_on_boot; /* is this BE active on boot */ boolean_t be_active; /* is this BE active currently */ boolean_t be_global_active; /* is zone's BE associated with */ diff --git a/usr/src/lib/libbe/common/libbe_priv.h b/usr/src/lib/libbe/common/libbe_priv.h index f2960c4f17..ace201577f 100644 --- a/usr/src/lib/libbe/common/libbe_priv.h +++ b/usr/src/lib/libbe/common/libbe_priv.h @@ -142,6 +142,12 @@ struct be_defaults { char be_deflt_bename_starts_with[ZFS_MAX_DATASET_NAME_LEN]; }; +typedef enum be_nextboot_state { + BE_NEXTBOOT_IGNORE = -1, + BE_NEXTBOOT_SET, + BE_NEXTBOOT_UNSET +} be_nextboot_state_t; + /* Library globals */ extern libzfs_handle_t *g_zfs; extern boolean_t do_print; @@ -201,7 +207,7 @@ int zfs_err_to_be_err(libzfs_handle_t *); int errno_to_be_err(int); /* be_activate.c */ -int _be_activate(char *); +int _be_activate(char *, be_nextboot_state_t); int be_activate_current_be(void); boolean_t be_is_active_on_boot(char *); diff --git a/usr/src/lib/libc/port/threads/pthr_attr.c b/usr/src/lib/libc/port/threads/pthr_attr.c index 7cfc970120..e5b3116eaf 100644 --- a/usr/src/lib/libc/port/threads/pthr_attr.c +++ b/usr/src/lib/libc/port/threads/pthr_attr.c @@ -529,16 +529,16 @@ pthread_attr_getname_np(pthread_attr_t *attr, char *buf, size_t len) /* * This function is a common BSD extension to pthread which is used to obtain * the attributes of a thread that might have changed after its creation, for - * example, it's stack address. + * example, its stack address. * * Note, there is no setattr analogue, nor do we desire to add one at this time. * Similarly there is no native threads API analogue (nor should we add one for * C11). * * The astute reader may note that there is a GNU version of this called - * pthread_getattr_np(). The two functions are similar, but subtley different in - * a rather important way. While the pthread_attr_get_np() expects to be given - * a pthread_attr_t that has had pthread_attr_init() called on in, + * pthread_getattr_np(). The two functions are similar, but subtly different in + * a rather important way. While pthread_attr_get_np() expects to be given + * a pthread_attr_t that has had pthread_attr_init() called on it, * pthread_getattr_np() does not. However, on GNU systems, where the function * originates, the pthread_attr_t is not opaque and thus it is entirely safe to * both call pthread_attr_init() and then call pthread_getattr_np() on the same @@ -556,7 +556,7 @@ pthread_attr_get_np(pthread_t tid, pthread_attr_t *attr) /* * To ensure that information about the target thread does not change or - * disappear while we're trying to interrogate it, we grab the uwlp + * disappear while we're trying to interrogate it, we grab the ulwp * lock. */ if (self->ul_lwpid == tid) { diff --git a/usr/src/lib/libc/port/threads/pthread.c b/usr/src/lib/libc/port/threads/pthread.c index dc0123361d..68c0fcbfa5 100644 --- a/usr/src/lib/libc/port/threads/pthread.c +++ b/usr/src/lib/libc/port/threads/pthread.c @@ -131,8 +131,8 @@ pthread_create(pthread_t *thread, const pthread_attr_t *attr, flag, &tid, ap->guardsize, ap->name); if (error == 0) { /* - * Record the original inheritence value for - * pthread_getattr_np(). We should always be able to find the + * Record the original inheritance value for + * pthread_attr_get_np(). We should always be able to find the * thread. */ (void) _thr_setinherit(tid, ap->inherit); diff --git a/usr/src/lib/libctf/common/ctf_convert.c b/usr/src/lib/libctf/common/ctf_convert.c index 9441aa6ed7..07f344ebaa 100644 --- a/usr/src/lib/libctf/common/ctf_convert.c +++ b/usr/src/lib/libctf/common/ctf_convert.c @@ -139,7 +139,7 @@ ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags, for (i = 0; i < NCONVERTS; i++) { fp = NULL; - err = ctf_converters[i](fd, elf, nthrs, flags, + err = ctf_converters[i](fd, elf, bsize, nthrs, flags, &fp, errbuf, errlen); if (err != ECTF_CONVNODEBUG) @@ -169,8 +169,8 @@ ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags, } ctf_file_t * -ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp, - char *errbuf, size_t errlen) +ctf_fdconvert(int fd, const char *label, uint_t bsize, uint_t nthrs, + uint_t flags, int *errp, char *errbuf, size_t errlen) { int err; Elf *elf; @@ -185,7 +185,8 @@ ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp, return (NULL); } - fp = ctf_elfconvert(fd, elf, label, nthrs, flags, errp, errbuf, errlen); + fp = ctf_elfconvert(fd, elf, label, bsize, nthrs, flags, errp, errbuf, + errlen); (void) elf_end(elf); return (fp); diff --git a/usr/src/lib/libctf/common/ctf_dwarf.c b/usr/src/lib/libctf/common/ctf_dwarf.c index 388cb20489..d91888d6c1 100644 --- a/usr/src/lib/libctf/common/ctf_dwarf.c +++ b/usr/src/lib/libctf/common/ctf_dwarf.c @@ -30,6 +30,7 @@ /* * Copyright 2020 Joyent, Inc. * Copyright 2020 Robert Mustacchi + * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ /* @@ -206,6 +207,8 @@ #include <dwarf.h> #include <libgen.h> #include <workq.h> +#include <thread.h> +#include <macros.h> #include <errno.h> #define DWARF_VERSION_TWO 2 @@ -254,12 +257,14 @@ typedef struct ctf_dwbitf { */ typedef struct ctf_die { Elf *cu_elf; /* shared libelf handle */ + int cu_fd; /* shared file descriptor */ char *cu_name; /* basename of the DIE */ ctf_merge_t *cu_cmh; /* merge handle */ ctf_list_t cu_vars; /* List of variables */ ctf_list_t cu_funcs; /* List of functions */ ctf_list_t cu_bitfields; /* Bit field members */ Dwarf_Debug cu_dwarf; /* libdwarf handle */ + mutex_t *cu_dwlock; /* libdwarf lock */ Dwarf_Die cu_cu; /* libdwarf compilation unit */ Dwarf_Off cu_cuoff; /* cu's offset */ Dwarf_Off cu_maxoff; /* maximum offset */ @@ -277,6 +282,7 @@ typedef struct ctf_die { ctf_id_t cu_longtid; /* id for a 'long' */ } ctf_cu_t; +static int ctf_dwarf_init_die(ctf_cu_t *); static int ctf_dwarf_offset(ctf_cu_t *, Dwarf_Die, Dwarf_Off *); static int ctf_dwarf_convert_die(ctf_cu_t *, Dwarf_Die); static int ctf_dwarf_convert_type(ctf_cu_t *, Dwarf_Die, ctf_id_t *, int); @@ -286,6 +292,13 @@ static int ctf_dwarf_function_count(ctf_cu_t *, Dwarf_Die, ctf_funcinfo_t *, static int ctf_dwarf_convert_fargs(ctf_cu_t *, Dwarf_Die, ctf_funcinfo_t *, ctf_id_t *); +#define DWARF_LOCK(cup) \ + if ((cup)->cu_dwlock != NULL) \ + mutex_enter((cup)->cu_dwlock) +#define DWARF_UNLOCK(cup) \ + if ((cup)->cu_dwlock != NULL) \ + mutex_exit((cup)->cu_dwlock) + /* * This is a generic way to set a CTF Conversion backend error depending on what * we were doing. Unless it was one of a specific set of errors that don't @@ -305,7 +318,8 @@ ctf_dwarf_error(ctf_cu_t *cup, ctf_file_t *cfp, int err, const char *fmt, ...) if (err == ENOMEM) return (err); - ret = snprintf(cup->cu_errbuf, rem, "die %s: ", cup->cu_name); + ret = snprintf(cup->cu_errbuf, rem, "die %s: ", + cup->cu_name != NULL ? cup->cu_name : "NULL"); if (ret < 0) goto err; off += ret; @@ -429,7 +443,10 @@ ctf_dwarf_attribute(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, int ret; Dwarf_Error derr; - if ((ret = dwarf_attr(die, name, attrp, &derr)) == DW_DLV_OK) + DWARF_LOCK(cup); + ret = dwarf_attr(die, name, attrp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) return (0); if (ret == DW_DLV_NO_ENTRY) { *attrp = NULL; @@ -441,6 +458,14 @@ ctf_dwarf_attribute(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, return (ECTF_CONVBKERR); } +static void +ctf_dwarf_dealloc(ctf_cu_t *cup, Dwarf_Ptr ptr, Dwarf_Unsigned type) +{ + DWARF_LOCK(cup); + dwarf_dealloc(cup->cu_dwarf, ptr, type); + DWARF_UNLOCK(cup); +} + static int ctf_dwarf_ref(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, Dwarf_Off *refp) { @@ -451,8 +476,11 @@ ctf_dwarf_ref(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, Dwarf_Off *refp) if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formref(attr, refp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formref(attr, refp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -474,8 +502,10 @@ ctf_dwarf_refdie(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, return (ret); off += cup->cu_cuoff; - if ((ret = dwarf_offdie(cup->cu_dwarf, off, diep, &derr)) != - DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_offdie(cup->cu_dwarf, off, diep, &derr); + DWARF_UNLOCK(cup); + if (ret != DW_DLV_OK) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to get die from offset %" DW_PR_DUu ": %s\n", off, dwarf_errmsg(derr)); @@ -496,8 +526,11 @@ ctf_dwarf_signed(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formsdata(attr, valp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formsdata(attr, valp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -518,8 +551,11 @@ ctf_dwarf_unsigned(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formudata(attr, valp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -540,8 +576,11 @@ ctf_dwarf_boolean(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formflag(attr, val, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_LOCK(cup); + ret = dwarf_formflag(attr, val, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } @@ -564,12 +603,15 @@ ctf_dwarf_string(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half name, char **strp) if ((ret = ctf_dwarf_attribute(cup, die, name, &attr)) != 0) return (ret); - if (dwarf_formstring(attr, &s, &derr) == DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_formstring(attr, &s, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) { if ((*strp = ctf_strdup(s)) == NULL) ret = ENOMEM; else ret = 0; - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ret); } @@ -598,17 +640,22 @@ ctf_dwarf_member_location(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Unsigned *valp) enum Dwarf_Form_Class class; if ((ret = ctf_dwarf_attribute(cup, die, DW_AT_data_member_location, - &attr)) != 0) + &attr)) != 0) { return (ret); + } - if (dwarf_whatform(attr, &form, &derr) != DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_whatform(attr, &form, &derr); + DWARF_UNLOCK(cup); + if (ret != DW_DLV_OK) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to get dwarf attribute for for member location: %s", dwarf_errmsg(derr)); - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ECTF_CONVBKERR); } + DWARF_LOCK(cup); class = dwarf_get_form_class(cup->cu_vers, DW_AT_data_member_location, cup->cu_addrsz, form); if (class == DW_FORM_CLASS_CONSTANT) { @@ -621,12 +668,14 @@ ctf_dwarf_member_location(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Unsigned *valp) * match, fall through to the normal path. */ if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (0); } if (dwarf_formsdata(attr, &sign, &derr) == DW_DLV_OK) { - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); if (sign < 0) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "encountered negative member data " @@ -638,26 +687,28 @@ ctf_dwarf_member_location(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Unsigned *valp) } if (dwarf_loclist(attr, &loc, &locnum, &derr) != DW_DLV_OK) { + DWARF_UNLOCK(cup); (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to obtain location list for member offset: %s", dwarf_errmsg(derr)); - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ECTF_CONVBKERR); } - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to parse location structure for member"); - dwarf_dealloc(cup->cu_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK); - dwarf_dealloc(cup->cu_dwarf, loc, DW_DLA_LOCDESC); + ctf_dwarf_dealloc(cup, loc->ld_s, DW_DLA_LOC_BLOCK); + ctf_dwarf_dealloc(cup, loc, DW_DLA_LOCDESC); return (ECTF_CONVBKERR); } *valp = loc->ld_s->lr_number; - dwarf_dealloc(cup->cu_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK); - dwarf_dealloc(cup->cu_dwarf, loc, DW_DLA_LOCDESC); + ctf_dwarf_dealloc(cup, loc->ld_s, DW_DLA_LOC_BLOCK); + ctf_dwarf_dealloc(cup, loc, DW_DLA_LOCDESC); return (0); } @@ -666,8 +717,12 @@ static int ctf_dwarf_offset(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Off *offsetp) { Dwarf_Error derr; + int ret; - if (dwarf_dieoffset(die, offsetp, &derr) == DW_DLV_OK) + DWARF_LOCK(cup); + ret = dwarf_dieoffset(die, offsetp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) return (0); (void) snprintf(cup->cu_errbuf, cup->cu_errlen, @@ -678,12 +733,14 @@ ctf_dwarf_offset(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Off *offsetp) /* simpler variant for debugging output */ static Dwarf_Off -ctf_die_offset(Dwarf_Die die) +ctf_die_offset(ctf_cu_t *cup, Dwarf_Die die) { Dwarf_Off off = -1; Dwarf_Error derr; + DWARF_LOCK(cup); (void) dwarf_dieoffset(die, &off, &derr); + DWARF_UNLOCK(cup); return (off); } @@ -691,8 +748,12 @@ static int ctf_dwarf_tag(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Half *tagp) { Dwarf_Error derr; + int ret; - if (dwarf_tag(die, tagp, &derr) == DW_DLV_OK) + DWARF_LOCK(cup); + ret = dwarf_tag(die, tagp, &derr); + DWARF_UNLOCK(cup); + if (ret == DW_DLV_OK) return (0); (void) snprintf(cup->cu_errbuf, cup->cu_errlen, @@ -708,7 +769,9 @@ ctf_dwarf_sib(ctf_cu_t *cup, Dwarf_Die base, Dwarf_Die *sibp) int ret; *sibp = NULL; + DWARF_LOCK(cup); ret = dwarf_siblingof(cup->cu_dwarf, base, sibp, &derr); + DWARF_UNLOCK(cup); if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY) return (0); @@ -725,7 +788,9 @@ ctf_dwarf_child(ctf_cu_t *cup, Dwarf_Die base, Dwarf_Die *childp) int ret; *childp = NULL; + DWARF_LOCK(cup); ret = dwarf_child(base, childp, &derr); + DWARF_UNLOCK(cup); if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY) return (0); @@ -1442,14 +1507,15 @@ ctf_dwarf_create_sou(ctf_cu_t *cup, Dwarf_Die die, ctf_id_t *idp, decl = 0; } - if (decl != 0) { + if (decl == B_TRUE) { base = ctf_add_forward(cup->cu_ctfp, isroot, name, kind); } else if (kind == CTF_K_STRUCT) { base = ctf_add_struct(cup->cu_ctfp, isroot, name); } else { base = ctf_add_union(cup->cu_ctfp, isroot, name); } - ctf_dprintf("added sou %s (%d) (%d)\n", name, kind, base); + ctf_dprintf("added sou %s (%d) (%ld) forward=%d\n", + name, kind, base, decl == B_TRUE); if (name != NULL) ctf_free(name, strlen(name) + 1); if (base == CTF_ERR) @@ -1545,7 +1611,9 @@ ctf_dwarf_array_upper_bound(ctf_cu_t *cup, Dwarf_Die range, ctf_arinfo_t *ar) } } - if (dwarf_whatform(attr, &form, &derr) != DW_DLV_OK) { + DWARF_LOCK(cup); + ret = dwarf_whatform(attr, &form, &derr); + if (ret != DW_DLV_OK) { (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "failed to get DW_AT_upper_bound attribute form: %s\n", dwarf_errmsg(derr)); @@ -1606,7 +1674,8 @@ ctf_dwarf_array_upper_bound(ctf_cu_t *cup, Dwarf_Die range, ctf_arinfo_t *ar) ret = ECTF_CONVBKERR; done: - dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR); + DWARF_UNLOCK(cup); + ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR); return (ret); } @@ -2219,11 +2288,13 @@ ctf_dwarf_convert_function(ctf_cu_t *cup, Dwarf_Die die) } ctf_dprintf("beginning work on function %s (die %llx)\n", - name, ctf_die_offset(die)); + name, ctf_die_offset(cup, die)); if ((ret = ctf_dwarf_boolean(cup, die, DW_AT_declaration, &b)) != 0) { - if (ret != ENOENT) + if (ret != ENOENT) { + ctf_free(name, strlen(name) + 1); return (ret); + } } else if (b != 0) { /* * GCC7 at least creates empty DW_AT_declarations for functions @@ -2233,7 +2304,8 @@ ctf_dwarf_convert_function(ctf_cu_t *cup, Dwarf_Die die) * DW_TAG_subprogram that is more complete. */ ctf_dprintf("ignoring declaration of function %s (die %llx)\n", - name, ctf_die_offset(die)); + name, ctf_die_offset(cup, die)); + ctf_free(name, strlen(name) + 1); return (0); } @@ -2347,7 +2419,7 @@ ctf_dwarf_convert_variable(ctf_cu_t *cup, Dwarf_Die die) if ((ret = ctf_dwarf_offset(cup, tdie, &offset)) != 0) return (ret); ctf_dprintf("die 0x%llx DW_AT_specification -> die 0x%llx\n", - ctf_die_offset(die), ctf_die_offset(tdie)); + ctf_die_offset(cup, die), ctf_die_offset(cup, tdie)); die = tdie; } else if (ret != ENOENT) { return (ret); @@ -2853,53 +2925,58 @@ ctf_dwarf_conv_weaks(ctf_cu_t *cup) return (ctf_symtab_iter(cup->cu_ctfp, ctf_dwarf_conv_weaks_cb, cup)); } -/* ARGSUSED */ static int ctf_dwarf_convert_one(void *arg, void *unused) { int ret; ctf_file_t *dedup; ctf_cu_t *cup = arg; + const char *name = cup->cu_name != NULL ? cup->cu_name : "NULL"; - ctf_dprintf("converting die: %s\n", cup->cu_name); - ctf_dprintf("max offset: %x\n", cup->cu_maxoff); VERIFY(cup != NULL); + if ((ret = ctf_dwarf_init_die(cup)) != 0) + return (ret); + + ctf_dprintf("converting die: %s - max offset: %x\n", name, + cup->cu_maxoff); + ret = ctf_dwarf_convert_die(cup, cup->cu_cu); - ctf_dprintf("ctf_dwarf_convert_die (%s) returned %d\n", cup->cu_name, + ctf_dprintf("ctf_dwarf_convert_die (%s) returned %d\n", name, ret); - if (ret != 0) { + if (ret != 0) return (ret); - } + if (ctf_update(cup->cu_ctfp) != 0) { return (ctf_dwarf_error(cup, cup->cu_ctfp, 0, "failed to update output ctf container")); } ret = ctf_dwarf_fixup_die(cup, B_FALSE); - ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cup->cu_name, + ctf_dprintf("ctf_dwarf_fixup_die (%s, FALSE) returned %d\n", name, ret); - if (ret != 0) { + if (ret != 0) return (ret); - } + if (ctf_update(cup->cu_ctfp) != 0) { return (ctf_dwarf_error(cup, cup->cu_ctfp, 0, "failed to update output ctf container")); } ret = ctf_dwarf_fixup_die(cup, B_TRUE); - ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cup->cu_name, + ctf_dprintf("ctf_dwarf_fixup_die (%s, TRUE) returned %d\n", name, ret); - if (ret != 0) { + if (ret != 0) return (ret); - } + if (ctf_update(cup->cu_ctfp) != 0) { return (ctf_dwarf_error(cup, cup->cu_ctfp, 0, "failed to update output ctf container")); } - if ((ret = ctf_dwarf_conv_funcvars(cup)) != 0) { + ctf_dprintf("ctf_dwarf_conv_funcvars (%s) returned %d\n", + name, ret); return (ctf_dwarf_error(cup, NULL, ret, "failed to convert strong functions and variables")); } @@ -2911,6 +2988,8 @@ ctf_dwarf_convert_one(void *arg, void *unused) if (cup->cu_doweaks == B_TRUE) { if ((ret = ctf_dwarf_conv_weaks(cup)) != 0) { + ctf_dprintf("ctf_dwarf_conv_weaks (%s) returned %d\n", + name, ret); return (ctf_dwarf_error(cup, NULL, ret, "failed to convert weak functions and variables")); } @@ -2921,30 +3000,26 @@ ctf_dwarf_convert_one(void *arg, void *unused) } } - ctf_phase_dump(cup->cu_ctfp, "pre-dwarf-dedup", cup->cu_name); + ctf_phase_dump(cup->cu_ctfp, "pre-dwarf-dedup", name); ctf_dprintf("adding inputs for dedup\n"); if ((ret = ctf_merge_add(cup->cu_cmh, cup->cu_ctfp)) != 0) { return (ctf_dwarf_error(cup, NULL, ret, "failed to add inputs for merge")); } - ctf_dprintf("starting dedup of %s\n", cup->cu_name); + ctf_dprintf("starting dedup of %s\n", name); if ((ret = ctf_merge_dedup(cup->cu_cmh, &dedup)) != 0) { return (ctf_dwarf_error(cup, NULL, ret, "failed to deduplicate die")); } + ctf_close(cup->cu_ctfp); cup->cu_ctfp = dedup; - ctf_phase_dump(cup->cu_ctfp, "post-dwarf-dedup", cup->cu_name); + ctf_phase_dump(cup->cu_ctfp, "post-dwarf-dedup", name); return (0); } -/* - * Note, we expect that if we're returning a ctf_file_t from one of the dies, - * say in the single node case, it's been saved and the entry here has been set - * to NULL, which ctf_close happily ignores. - */ static void ctf_dwarf_free_die(ctf_cu_t *cup) { @@ -2953,13 +3028,18 @@ ctf_dwarf_free_die(ctf_cu_t *cup) ctf_dwbitf_t *cdb, *ndb; ctf_dwmap_t *map; void *cookie; - Dwarf_Error derr; ctf_dprintf("Beginning to free die: %p\n", cup); + + VERIFY3P(cup->cu_elf, !=, NULL); cup->cu_elf = NULL; + ctf_dprintf("Trying to free name: %p\n", cup->cu_name); - if (cup->cu_name != NULL) + if (cup->cu_name != NULL) { ctf_free(cup->cu_name, strlen(cup->cu_name) + 1); + cup->cu_name = NULL; + } + ctf_dprintf("Trying to free merge handle: %p\n", cup->cu_cmh); if (cup->cu_cmh != NULL) { ctf_merge_fini(cup->cu_cmh); @@ -2990,35 +3070,25 @@ ctf_dwarf_free_die(ctf_cu_t *cup) ctf_free(cdb, sizeof (ctf_dwbitf_t)); } - ctf_dprintf("Trying to clean up dwarf_t: %p\n", cup->cu_dwarf); - if (cup->cu_dwarf != NULL) - (void) dwarf_finish(cup->cu_dwarf, &derr); - cup->cu_dwarf = NULL; - ctf_close(cup->cu_ctfp); + if (cup->cu_ctfp != NULL) { + ctf_close(cup->cu_ctfp); + cup->cu_ctfp = NULL; + } cookie = NULL; - while ((map = avl_destroy_nodes(&cup->cu_map, &cookie)) != NULL) { + while ((map = avl_destroy_nodes(&cup->cu_map, &cookie)) != NULL) ctf_free(map, sizeof (ctf_dwmap_t)); - } avl_destroy(&cup->cu_map); cup->cu_errbuf = NULL; -} - -static void -ctf_dwarf_free_dies(ctf_cu_t *cdies, int ndies) -{ - int i; - ctf_dprintf("Beginning to free dies\n"); - for (i = 0; i < ndies; i++) { - ctf_dwarf_free_die(&cdies[i]); + if (cup->cu_cu != NULL) { + ctf_dwarf_dealloc(cup, cup->cu_cu, DW_DLA_DIE); + cup->cu_cu = NULL; } - - ctf_free(cdies, sizeof (ctf_cu_t) * ndies); } static int -ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, int *ndies, +ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, uint_t *ndies, char *errbuf, size_t errlen) { int ret; @@ -3049,88 +3119,146 @@ ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, int *ndies, return (0); } +/* + * Fill out just enough of each ctf_cu_t for the conversion process to + * be able to finish the rest in a (potentially) multithreaded context. + */ static int -ctf_dwarf_init_die(int fd, Elf *elf, ctf_cu_t *cup, int ndie, char *errbuf, - size_t errlen) +ctf_dwarf_preinit_dies(int fd, Elf *elf, Dwarf_Debug dw, + mutex_t *dwlock, Dwarf_Error *derr, uint_t ndies, ctf_cu_t *cdies, + uint_t flags, char *errbuf, size_t errlen) { - int ret; Dwarf_Unsigned hdrlen, abboff, nexthdr; Dwarf_Half addrsz, vers; Dwarf_Unsigned offset = 0; - Dwarf_Error derr; + uint_t added = 0; + int ret, i = 0; - while ((ret = dwarf_next_cu_header(cup->cu_dwarf, &hdrlen, &vers, - &abboff, &addrsz, &nexthdr, &derr)) != DW_DLV_NO_ENTRY) { + while ((ret = dwarf_next_cu_header(dw, &hdrlen, &vers, &abboff, + &addrsz, &nexthdr, derr)) != DW_DLV_NO_ENTRY) { + Dwarf_Die cu; + ctf_cu_t *cup; char *name; - Dwarf_Die cu, child; - /* Based on the counting above, we should be good to go */ - VERIFY(ret == DW_DLV_OK); - if (ndie > 0) { - ndie--; - offset = nexthdr; - continue; + VERIFY3U(i, <, ndies); + + cup = &cdies[i++]; + + cup->cu_fd = fd; + cup->cu_elf = elf; + cup->cu_dwarf = dw; + cup->cu_errbuf = errbuf; + cup->cu_errlen = errlen; + cup->cu_dwarf = dw; + if (ndies > 1) { + /* + * Only need to lock calls into libdwarf if there are + * multiple CUs. + */ + cup->cu_dwlock = dwlock; + cup->cu_doweaks = B_FALSE; + } else { + cup->cu_doweaks = B_TRUE; } - /* - * Compilers are apparently inconsistent. Some emit no DWARF for - * empty files and others emit empty compilation unit. - */ cup->cu_voidtid = CTF_ERR; cup->cu_longtid = CTF_ERR; - cup->cu_elf = elf; + cup->cu_cuoff = offset; cup->cu_maxoff = nexthdr - 1; cup->cu_vers = vers; cup->cu_addrsz = addrsz; - cup->cu_ctfp = ctf_fdcreate(fd, &ret); - if (cup->cu_ctfp == NULL) - return (ret); - - avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t), - offsetof(ctf_dwmap_t, cdm_avl)); - cup->cu_errbuf = errbuf; - cup->cu_errlen = errlen; - bzero(&cup->cu_vars, sizeof (ctf_list_t)); - bzero(&cup->cu_funcs, sizeof (ctf_list_t)); - bzero(&cup->cu_bitfields, sizeof (ctf_list_t)); - - if ((ret = ctf_dwarf_die_elfenc(elf, cup, errbuf, - errlen)) != 0) - return (ret); - if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) + if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) { + ctf_dprintf("cu %d - no cu %d\n", i, ret); return (ret); - - if (cu == NULL) { - (void) snprintf(errbuf, errlen, - "file does not contain DWARF data"); - return (ECTF_CONVNODEBUG); } - if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0) - return (ret); - - if (child == NULL) { - (void) snprintf(errbuf, errlen, + if (cu == NULL) { + ctf_dprintf("cu %d - no cu data\n", i); + (void) snprintf(cup->cu_errbuf, cup->cu_errlen, "file does not contain DWARF data"); return (ECTF_CONVNODEBUG); } - cup->cu_cuoff = offset; - cup->cu_cu = child; - - if ((cup->cu_cmh = ctf_merge_init(fd, &ret)) == NULL) - return (ret); - if (ctf_dwarf_string(cup, cu, DW_AT_name, &name) == 0) { size_t len = strlen(name) + 1; char *b = basename(name); + cup->cu_name = strdup(b); ctf_free(name, len); + if (cup->cu_name == NULL) + return (ENOMEM); } - break; + + ret = ctf_dwarf_child(cup, cu, &cup->cu_cu); + dwarf_dealloc(cup->cu_dwarf, cu, DW_DLA_DIE); + if (ret != 0) { + ctf_dprintf("cu %d - no child '%s' %d\n", + i, cup->cu_name != NULL ? cup->cu_name : "NULL", + ret); + return (ret); + } + + if (cup->cu_cu == NULL) { + size_t len; + + ctf_dprintf("cu %d - no child data '%s' %d\n", + i, cup->cu_name != NULL ? cup->cu_name : "NULL", + ret); + if (cup->cu_name != NULL && + (len = strlen(cup->cu_name)) > 2 && + strncmp(".c", &cup->cu_name[len - 2], 2) == 0) { + /* + * Missing DEBUG data for a .c file, return an + * error unless this is permitted. + */ + if (!(flags & CTF_ALLOW_MISSING_DEBUG)) { + (void) snprintf( + cup->cu_errbuf, cup->cu_errlen, + "file %s is missing debug info", + cup->cu_name); + return (ECTF_CONVNODEBUG); + } + } + } else { + added++; + } + + ctf_dprintf("Pre-initialised cu %d - '%s'\n", i, + cup->cu_name != NULL ? cup->cu_name : "NULL"); + + offset = nexthdr; } + /* + * If none of the CUs had debug data, return an error. + */ + if (added == 0) + return (ECTF_CONVNODEBUG); + + return (0); +} + +static int +ctf_dwarf_init_die(ctf_cu_t *cup) +{ + int ret; + + cup->cu_ctfp = ctf_fdcreate(cup->cu_fd, &ret); + if (cup->cu_ctfp == NULL) + return (ret); + + avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t), + offsetof(ctf_dwmap_t, cdm_avl)); + + if ((ret = ctf_dwarf_die_elfenc(cup->cu_elf, cup, + cup->cu_errbuf, cup->cu_errlen)) != 0) { + return (ret); + } + + if ((cup->cu_cmh = ctf_merge_init(cup->cu_fd, &ret)) == NULL) + return (ret); + return (0); } @@ -3165,8 +3293,10 @@ c_source_has_debug(const char *file, ctf_cu_t *cus, size_t nr_cus) return (B_TRUE); for (size_t i = 0; i < nr_cus; i++) { - if (strcmp(basename, cus[i].cu_name) == 0) + if (cus[i].cu_name != NULL && + strcmp(basename, cus[i].cu_name) == 0) { return (B_TRUE); + } } return (B_FALSE); @@ -3239,7 +3369,7 @@ ctf_dwarf_check_missing(ctf_cu_t *cus, size_t nr_cus, Elf *elf, if (!c_source_has_debug(file, cus, nr_cus)) { (void) snprintf(errmsg, errlen, - "file %s is missing debug info\n", file); + "file %s is missing debug info", file); return (ECTF_CONVNODEBUG); } } @@ -3247,15 +3377,126 @@ ctf_dwarf_check_missing(ctf_cu_t *cus, size_t nr_cus, Elf *elf, return (0); } +static int +ctf_dwarf_convert_batch(uint_t start, uint_t end, int fd, uint_t nthrs, + workq_t *wqp, ctf_cu_t *cdies, ctf_file_t **fpp) +{ + ctf_file_t *fpprev = NULL; + uint_t i, added; + ctf_cu_t *cup; + int ret, err; + + ctf_dprintf("Processing CU batch %u - %u\n", start, end - 1); + + added = 0; + for (i = start; i < end; i++) { + cup = &cdies[i]; + if (cup->cu_cu == NULL) + continue; + ctf_dprintf("adding cu %s: %p, %x %x\n", + cup->cu_name != NULL ? cup->cu_name : "NULL", + cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff); + if (workq_add(wqp, cup) == -1) { + err = errno; + goto out; + } + added++; + } + + /* + * No debug data found in this batch, move on to the next. + * NB: ctf_dwarf_preinit_dies() has already checked that there is at + * least one CU with debug data present. + */ + if (added == 0) { + err = 0; + goto out; + } + + ctf_dprintf("Running conversion phase\n"); + + /* Run the conversions */ + ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, &err); + if (ret == WORKQ_ERROR) { + err = errno; + goto out; + } else if (ret == WORKQ_UERROR) { + ctf_dprintf("internal convert failed: %s\n", + ctf_errmsg(err)); + goto out; + } + + ctf_dprintf("starting merge phase\n"); + + ctf_merge_t *cmp = ctf_merge_init(fd, &err); + if (cmp == NULL) + goto out; + + if ((err = ctf_merge_set_nthreads(cmp, nthrs)) != 0) { + ctf_merge_fini(cmp); + goto out; + } + + /* + * If we have the result of a previous merge then add it as an input to + * the next one. + */ + if (*fpp != NULL) { + ctf_dprintf("adding previous merge CU\n"); + fpprev = *fpp; + *fpp = NULL; + if ((err = ctf_merge_add(cmp, fpprev)) != 0) { + ctf_merge_fini(cmp); + goto out; + } + } + + ctf_dprintf("adding CUs to merge\n"); + for (i = start; i < end; i++) { + cup = &cdies[i]; + if (cup->cu_cu == NULL) + continue; + if ((err = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) { + ctf_merge_fini(cmp); + *fpp = NULL; + goto out; + } + } + + ctf_dprintf("performing merge\n"); + err = ctf_merge_merge(cmp, fpp); + if (err != 0) { + ctf_dprintf("failed merge!\n"); + *fpp = NULL; + ctf_merge_fini(cmp); + goto out; + } + + ctf_merge_fini(cmp); + + ctf_dprintf("freeing CUs\n"); + for (i = start; i < end; i++) { + cup = &cdies[i]; + ctf_dprintf("freeing cu %d\n", i); + ctf_dwarf_free_die(cup); + } + +out: + ctf_close(fpprev); + return (err); +} + int -ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, uint_t flags, +ctf_dwarf_convert(int fd, Elf *elf, uint_t bsize, uint_t nthrs, uint_t flags, ctf_file_t **fpp, char *errbuf, size_t errlen) { - int err, ret, ndies, i; + int err, ret; + uint_t ndies, i; Dwarf_Debug dw; Dwarf_Error derr; ctf_cu_t *cdies = NULL, *cup; workq_t *wqp = NULL; + mutex_t dwlock = ERRORCHECKMUTEX; *fpp = NULL; @@ -3288,116 +3529,74 @@ ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, uint_t flags, if (ndies == 0) { (void) snprintf(errbuf, errlen, "file does not contain DWARF data\n"); + (void) dwarf_finish(dw, &derr); return (ECTF_CONVNODEBUG); } - (void) dwarf_finish(dw, &derr); cdies = ctf_alloc(sizeof (ctf_cu_t) * ndies); if (cdies == NULL) { + (void) dwarf_finish(dw, &derr); return (ENOMEM); } bzero(cdies, sizeof (ctf_cu_t) * ndies); - for (i = 0; i < ndies; i++) { - cup = &cdies[i]; - ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, - &cup->cu_dwarf, &derr); - if (ret != 0) { - ctf_free(cdies, sizeof (ctf_cu_t) * ndies); - (void) snprintf(errbuf, errlen, - "failed to initialize DWARF: %s\n", - dwarf_errmsg(derr)); - return (ECTF_CONVBKERR); - } - - err = ctf_dwarf_init_die(fd, elf, cup, i, errbuf, errlen); - if (err != 0) - goto out; - - cup->cu_doweaks = ndies > 1 ? B_FALSE : B_TRUE; + if ((err = ctf_dwarf_preinit_dies(fd, elf, dw, &dwlock, &derr, + ndies, cdies, flags, errbuf, errlen)) != 0) { + goto out; } if (!(flags & CTF_ALLOW_MISSING_DEBUG) && (err = ctf_dwarf_check_missing(cdies, ndies, - elf, errbuf, errlen)) != 0) - goto out; - - /* - * If we only have one compilation unit, there's no reason to use - * multiple threads, even if the user requested them. After all, they - * just gave us an upper bound. - */ - if (ndies == 1) - nthrs = 1; - - if (workq_init(&wqp, nthrs) == -1) { - err = errno; + elf, errbuf, errlen)) != 0) { goto out; } - for (i = 0; i < ndies; i++) { - cup = &cdies[i]; - ctf_dprintf("adding cu %s: %p, %x %x\n", cup->cu_name, - cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff); - if (workq_add(wqp, cup) == -1) { - err = errno; + /* Only one cu, no merge required */ + if (ndies == 1) { + cup = cdies; + + if ((err = ctf_dwarf_convert_one(cup, NULL)) != 0) goto out; - } + + *fpp = cup->cu_ctfp; + cup->cu_ctfp = NULL; + ctf_dwarf_free_die(cup); + goto success; } - ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, &err); - if (ret == WORKQ_ERROR) { + /* + * There's no need to have either more threads or a batch size larger + * than the total number of dies, even if the user requested them. + */ + nthrs = min(ndies, nthrs); + bsize = min(ndies, bsize); + + if (workq_init(&wqp, nthrs) == -1) { err = errno; goto out; - } else if (ret == WORKQ_UERROR) { - ctf_dprintf("internal convert failed: %s\n", - ctf_errmsg(err)); - goto out; } - ctf_dprintf("Determining next phase: have %d CUs\n", ndies); - if (ndies != 1) { - ctf_merge_t *cmp; - - cmp = ctf_merge_init(fd, &err); - if (cmp == NULL) - goto out; - - ctf_dprintf("setting threads\n"); - if ((err = ctf_merge_set_nthreads(cmp, nthrs)) != 0) { - ctf_merge_fini(cmp); - goto out; - } - - for (i = 0; i < ndies; i++) { - cup = &cdies[i]; - if ((err = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) { - ctf_merge_fini(cmp); - goto out; - } - } - - ctf_dprintf("performing merge\n"); - err = ctf_merge_merge(cmp, fpp); + /* + * In order to avoid exhausting memory limits when converting files + * with a large number of dies, we process them in batches. + */ + for (i = 0; i < ndies; i += bsize) { + err = ctf_dwarf_convert_batch(i, min(i + bsize, ndies), + fd, nthrs, wqp, cdies, fpp); if (err != 0) { - ctf_dprintf("failed merge!\n"); *fpp = NULL; - ctf_merge_fini(cmp); goto out; } - ctf_merge_fini(cmp); - err = 0; - ctf_dprintf("successfully converted!\n"); - } else { - err = 0; - *fpp = cdies->cu_ctfp; - cdies->cu_ctfp = NULL; - ctf_dprintf("successfully converted!\n"); } +success: + err = 0; + ctf_dprintf("successfully converted!\n"); + out: + (void) dwarf_finish(dw, &derr); workq_fini(wqp); - ctf_dwarf_free_dies(cdies, ndies); + ctf_free(cdies, sizeof (ctf_cu_t) * ndies); return (err); } diff --git a/usr/src/lib/libctf/common/ctf_merge.c b/usr/src/lib/libctf/common/ctf_merge.c index a18a2f46ef..a5888de8c0 100644 --- a/usr/src/lib/libctf/common/ctf_merge.c +++ b/usr/src/lib/libctf/common/ctf_merge.c @@ -129,10 +129,11 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, { ctf_merge_types_t *cmp = arg; ctf_merge_tinfo_t *cmt = cmp->cm_tmap; + uint_t kind; if (same == B_TRUE) { if (ctf_type_kind(ifp, iid) == CTF_K_FORWARD && - ctf_type_kind(ofp, oid) != CTF_K_FORWARD) { + (kind = ctf_type_kind(ofp, oid)) != CTF_K_FORWARD) { VERIFY(cmt[oid].cmt_map == 0); /* @@ -153,8 +154,8 @@ ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, cmt[oid].cmt_map = iid; cmt[oid].cmt_forward = B_TRUE; - ctf_dprintf("merge diff forward mapped %d->%d\n", oid, - iid); + ctf_dprintf("merge diff forward mapped %ld->%ld (%u)\n", + oid, iid, kind); return; } @@ -421,7 +422,7 @@ ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id) } static int -ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id) +ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id, uint_t kind) { int ret, flags; const ctf_type_t *tp; @@ -434,14 +435,7 @@ ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id) else flags = CTF_ADD_NONROOT; - /* - * ctf_add_forward tries to check to see if a given forward already - * exists in one of its hash tables. If we're here then we know that we - * have a forward in a container that isn't present in another. - * Therefore, we choose a token hash table to satisfy the API choice - * here. - */ - ret = ctf_add_forward(cmp->cm_out, flags, name, CTF_K_STRUCT); + ret = ctf_add_forward(cmp->cm_out, flags, name, kind); if (ret == CTF_ERR) return (CTF_ERR); @@ -494,19 +488,32 @@ ctf_merge_add_sou(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward) else suid = ctf_add_union(cmp->cm_out, flags, name); + ctf_dprintf("added sou \"%s\" as (%d) %d->%d\n", name, kind, id, suid); + if (suid == CTF_ERR) return (suid); - /* - * If this is a forward reference then its mapping should already - * exist. - */ if (forward == B_FALSE) { VERIFY(cmp->cm_tmap[id].cmt_map == 0); cmp->cm_tmap[id].cmt_map = suid; - ctf_dprintf("added sou \"%s\" as (%d) %d->%d\n", name, kind, id, - suid); } else { + /* + * If this is a forward reference then its mapping should + * already exist. + */ + if (cmp->cm_tmap[id].cmt_map != suid) { + ctf_dprintf( + "mismatch sou \"%s\" as (%d) %d->%d (exp %d)\n", + name, kind, id, suid, cmp->cm_tmap[id].cmt_map); + ctf_hash_dump("src structs", + &cmp->cm_src->ctf_structs, cmp->cm_src); + ctf_hash_dump("src unions", + &cmp->cm_src->ctf_unions, cmp->cm_src); + ctf_hash_dump("out structs", + &cmp->cm_out->ctf_structs, cmp->cm_out); + ctf_hash_dump("out unions", + &cmp->cm_out->ctf_unions, cmp->cm_out); + } VERIFY(cmp->cm_tmap[id].cmt_map == suid); } cmp->cm_tmap[id].cmt_fixup = B_TRUE; @@ -551,9 +558,27 @@ ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id) case CTF_K_FUNCTION: ret = ctf_merge_add_func(cmp, id); break; - case CTF_K_FORWARD: - ret = ctf_merge_add_forward(cmp, id); + case CTF_K_FORWARD: { + const ctf_type_t *tp; + uint_t kind; + + tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id); + + /* + * For forward declarations, ctt_type is the CTF_K_* + * kind for the tag. Older versions of the CTF tools may + * not have filled this in so if ctt_type is unknown or + * invalid, treat it as a struct. This mirrors the logic in + * ctf_bufopen(). + */ + + kind = tp->ctt_type; + if (kind == CTF_K_UNKNOWN || kind >= CTF_K_MAX) + kind = CTF_K_STRUCT; + + ret = ctf_merge_add_forward(cmp, id, kind); break; + } case CTF_K_STRUCT: case CTF_K_UNION: ret = ctf_merge_add_sou(cmp, id, B_FALSE); @@ -690,6 +715,7 @@ ctf_merge_common(ctf_merge_types_t *cmp) /* Pass 1 */ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) { if (cmp->cm_tmap[i].cmt_forward == B_TRUE) { + ctf_dprintf("Forward %d\n", i); ret = ctf_merge_add_sou(cmp, i, B_TRUE); if (ret != 0) { return (ret); @@ -1566,11 +1592,11 @@ ctf_dedup_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp, while (cmt[oid].cmt_missing == B_FALSE) oid = cmt[oid].cmt_map; cmt[iid].cmt_map = oid; - ctf_dprintf("%d->%d \n", iid, oid); + ctf_dprintf("dedup %d->%d \n", iid, oid); } else { VERIFY(cmt[iid].cmt_map == 0); cmt[iid].cmt_missing = B_TRUE; - ctf_dprintf("%d is missing\n", iid); + ctf_dprintf("dedup %d is missing\n", iid); } } diff --git a/usr/src/lib/libctf/common/libctf_impl.h b/usr/src/lib/libctf/common/libctf_impl.h index 5c88b9454d..0921a3ec1a 100644 --- a/usr/src/lib/libctf/common/libctf_impl.h +++ b/usr/src/lib/libctf/common/libctf_impl.h @@ -29,9 +29,9 @@ extern "C" { #endif -typedef int (*ctf_convert_f)(int, Elf *, uint_t, uint_t, +typedef int (*ctf_convert_f)(int, Elf *, uint_t, uint_t, uint_t, ctf_file_t **, char *, size_t); -extern int ctf_dwarf_convert(int, Elf *, uint_t, uint_t, +extern int ctf_dwarf_convert(int, Elf *, uint_t, uint_t, uint_t, ctf_file_t **, char *, size_t); /* diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h index bdc24ac6ef..f69cb861c4 100644 --- a/usr/src/lib/libzfs/common/libzfs.h +++ b/usr/src/lib/libzfs/common/libzfs.h @@ -823,6 +823,8 @@ extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **, * Label manipulation. */ extern int zpool_clear_label(int); +extern int zpool_set_bootenv(zpool_handle_t *, const nvlist_t *); +extern int zpool_get_bootenv(zpool_handle_t *, nvlist_t **); /* is this zvol valid for use as a dump device? */ extern int zvol_check_dump_config(char *); diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c index c3603884b6..e56628e2be 100644 --- a/usr/src/lib/libzfs/common/libzfs_pool.c +++ b/usr/src/lib/libzfs/common/libzfs_pool.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2017 by Delphix. All rights reserved. + * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright 2019 Joyent, Inc. * Copyright 2016 Nexenta Systems, Inc. * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> @@ -397,7 +397,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, * Assuming bootfs is a valid dataset name. */ static boolean_t -bootfs_name_valid(const char *pool, char *bootfs) +bootfs_name_valid(const char *pool, const char *bootfs) { int len = strlen(pool); if (bootfs[0] == '\0') @@ -4414,6 +4414,38 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, free(mntpnt); } +int +zpool_set_bootenv(zpool_handle_t *zhp, const nvlist_t *envmap) +{ + int error = lzc_set_bootenv(zhp->zpool_name, envmap); + if (error != 0) { + (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, + dgettext(TEXT_DOMAIN, + "error setting bootenv in pool '%s'"), zhp->zpool_name); + } + + return (error); +} + +int +zpool_get_bootenv(zpool_handle_t *zhp, nvlist_t **nvlp) +{ + nvlist_t *nvl; + int error; + + nvl = NULL; + error = lzc_get_bootenv(zhp->zpool_name, &nvl); + if (error != 0) { + (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, + dgettext(TEXT_DOMAIN, + "error getting bootenv in pool '%s'"), zhp->zpool_name); + } else { + *nvlp = nvl; + } + + return (error); +} + /* * Read the EFI label from the config, if a label does not exist then * pass back the error to the caller. If the caller has passed a non-NULL diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers index 5ad0f9d4eb..0174e1a3f3 100644 --- a/usr/src/lib/libzfs/common/mapfile-vers +++ b/usr/src/lib/libzfs/common/mapfile-vers @@ -205,6 +205,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_find_vdev; zpool_find_vdev_by_physpath; zpool_fru_set; + zpool_get_bootenv; zpool_get_config; zpool_get_errlog; zpool_get_features; @@ -244,6 +245,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { zpool_reguid; zpool_reopen; zpool_scan; + zpool_set_bootenv; zpool_set_prop; zpool_skip_pool; zpool_state_to_name; diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.c b/usr/src/lib/libzfs_core/common/libzfs_core.c index 31ffddd93f..471a5e5515 100644 --- a/usr/src/lib/libzfs_core/common/libzfs_core.c +++ b/usr/src/lib/libzfs_core/common/libzfs_core.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright (c) 2012, 2020 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. @@ -1372,3 +1372,21 @@ lzc_change_key(const char *fsname, uint64_t crypt_cmd, nvlist_t *props, nvlist_free(ioc_args); return (error); } + +/* + * Set the bootenv contents for the given pool. + */ +int +lzc_set_bootenv(const char *pool, const nvlist_t *env) +{ + return (lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, (nvlist_t *)env, NULL)); +} + +/* + * Get the contents of the bootenv of the given pool. + */ +int +lzc_get_bootenv(const char *pool, nvlist_t **outnvl) +{ + return (lzc_ioctl(ZFS_IOC_GET_BOOTENV, pool, NULL, outnvl)); +} diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.h b/usr/src/lib/libzfs_core/common/libzfs_core.h index 9b7721e7eb..4b408cbb63 100644 --- a/usr/src/lib/libzfs_core/common/libzfs_core.h +++ b/usr/src/lib/libzfs_core/common/libzfs_core.h @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2016 by Delphix. All rights reserved. + * Copyright (c) 2012, 2020 by Delphix. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. * Copyright (c) 2017 Datto Inc. @@ -118,6 +118,8 @@ int lzc_channel_program_nosync(const char *, const char *, uint64_t, int lzc_pool_checkpoint(const char *); int lzc_pool_checkpoint_discard(const char *); +int lzc_set_bootenv(const char *, const nvlist_t *); +int lzc_get_bootenv(const char *, nvlist_t **); #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzfs_core/common/mapfile-vers b/usr/src/lib/libzfs_core/common/mapfile-vers index 98516b66cc..bc4bc3631d 100644 --- a/usr/src/lib/libzfs_core/common/mapfile-vers +++ b/usr/src/lib/libzfs_core/common/mapfile-vers @@ -38,6 +38,13 @@ $mapfile_version 2 +SYMBOL_VERSION ILLUMOS_0.8 { + global: + + lzc_get_bootenv; + lzc_set_bootenv; +} ILLUMOS_0.7; + SYMBOL_VERSION ILLUMOS_0.7 { global: diff --git a/usr/src/lib/libzfsbootenv/Makefile b/usr/src/lib/libzfsbootenv/Makefile new file mode 100644 index 0000000000..82acb6ba44 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/Makefile @@ -0,0 +1,53 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.lib + +HDRS= libzfsbootenv.h + +HDRDIR= common + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install + +POFILE = libzfsbootenv.po +MSGFILES = `$(GREP) -l gettext $(HDRDIR)/*.[ch]` + +.KEEP_STATE: + +all install: install_h $(SUBDIRS) + +clean clobber: $(SUBDIRS) + +$(POFILE): pofile_MSGFILES + +install_h: $(ROOTHDRS) + +check: $(CHECKHDRS) + +_msg: $(MSGDOMAINPOFILE) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/Makefile.msg.targ +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libzfsbootenv/Makefile.com b/usr/src/lib/libzfsbootenv/Makefile.com new file mode 100644 index 0000000000..7b40b3839a --- /dev/null +++ b/usr/src/lib/libzfsbootenv/Makefile.com @@ -0,0 +1,42 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +LIBRARY= libzfsbootenv.a +VERS= .1 + +OBJECTS= \ + lzbe_device.o \ + lzbe_pair.o \ + lzbe_util.o + +include ../../Makefile.lib + +LIBS= $(DYNLIB) + +SRCDIR= ../common + +CSTD= $(CSTD_GNU99) + +LDLIBS += -lzfs -lnvpair -lc +CPPFLAGS += -I$(SRC)/uts/common/fs/zfs + + +CLOBBERFILES += $(LIBRARY) + +.KEEP_STATE: + +all: $(LIBS) + +include ../../Makefile.targ diff --git a/usr/src/lib/libzfsbootenv/amd64/Makefile b/usr/src/lib/libzfsbootenv/amd64/Makefile new file mode 100644 index 0000000000..dd76c2b252 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/amd64/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libzfsbootenv/common/libzfsbootenv.h b/usr/src/lib/libzfsbootenv/common/libzfsbootenv.h new file mode 100644 index 0000000000..9d0422fa56 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/libzfsbootenv.h @@ -0,0 +1,41 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#ifndef _LIBZFSBOOTENV_H +#define _LIBZFSBOOTENV_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum lzbe_flags { + lzbe_add, /* add data to existing nvlist */ + lzbe_replace /* replace current nvlist */ +} lzbe_flags_t; + +extern int lzbe_nvlist_get(const char *, const char *, void **); +extern int lzbe_nvlist_set(const char *, const char *, void *); +extern void lzbe_nvlist_free(void *); +extern int lzbe_add_pair(void *, const char *, const char *, void *, size_t); +extern int lzbe_remove_pair(void *, const char *); +extern int lzbe_set_boot_device(const char *, lzbe_flags_t, const char *); +extern int lzbe_get_boot_device(const char *, char **); +extern int lzbe_bootenv_print(const char *, const char *, FILE *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBZFSBOOTENV_H */ diff --git a/usr/src/lib/libzfsbootenv/common/lzbe_device.c b/usr/src/lib/libzfsbootenv/common/lzbe_device.c new file mode 100644 index 0000000000..670efd8b06 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/lzbe_device.c @@ -0,0 +1,164 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#include <sys/types.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> +#include <sys/zfs_bootenv.h> +#include <sys/vdev_impl.h> + +/* + * Store device name to zpool label bootenv area. + * This call will set bootenv version to VB_NVLIST, if bootenv currently + * does contain other version, then old data will be replaced. + */ +int +lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + char *descriptor; + uint64_t version; + int rv = -1; + + if (pool == NULL || *pool == '\0') + return (rv); + + if ((hdl = libzfs_init()) == NULL) + return (rv); + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + switch (flag) { + case lzbe_add: + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + /* + * We got the nvlist, check for version. + * if version is missing or is not VB_NVLIST, + * create new list. + */ + rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION, + &version); + if (rv == 0 && version == VB_NVLIST) + break; + + /* Drop this nvlist */ + fnvlist_free(nv); + } + /* FALLTHROUGH */ + case lzbe_replace: + nv = fnvlist_alloc(); + break; + default: + return (rv); + } + + /* version is mandatory */ + fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST); + + /* + * If device name is empty, remove boot device configuration. + */ + if ((device == NULL || *device == '\0')) { + if (nvlist_exists(nv, OS_BOOTONCE)) + fnvlist_remove(nv, OS_BOOTONCE); + } else { + /* + * Use device name directly if it does start with + * prefix "zfs:". Otherwise, add prefix and sufix. + */ + if (strncmp(device, "zfs:", 4) == 0) { + fnvlist_add_string(nv, OS_BOOTONCE, device); + } else { + descriptor = NULL; + if (asprintf(&descriptor, "zfs:%s:", device) > 0) + fnvlist_add_string(nv, OS_BOOTONCE, descriptor); + else + rv = ENOMEM; + free(descriptor); + } + } + + rv = zpool_set_bootenv(zphdl, nv); + if (rv != 0) + fprintf(stderr, "%s\n", libzfs_error_description(hdl)); + + fnvlist_free(nv); + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} + +/* + * Return boot device name from bootenv, if set. + */ +int +lzbe_get_boot_device(const char *pool, char **device) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + char *val; + int rv = -1; + + if (pool == NULL || *pool == '\0' || device == NULL) + return (rv); + + if ((hdl = libzfs_init()) == NULL) + return (rv); + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val); + if (rv == 0) { + /* + * zfs device descriptor is in form of "zfs:dataset:", + * we only do need dataset name. + */ + if (strncmp(val, "zfs:", 4) == 0) { + val += 4; + val = strdup(val); + if (val != NULL) { + size_t len = strlen(val); + + if (val[len - 1] == ':') + val[len - 1] = '\0'; + *device = val; + } else { + rv = ENOMEM; + } + } else { + rv = EINVAL; + } + } + nvlist_free(nv); + } + + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} diff --git a/usr/src/lib/libzfsbootenv/common/lzbe_pair.c b/usr/src/lib/libzfsbootenv/common/lzbe_pair.c new file mode 100644 index 0000000000..3a4f4ee7b7 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/lzbe_pair.c @@ -0,0 +1,348 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#include <sys/types.h> +#include <sys/sysmacros.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> +#include <sys/zfs_bootenv.h> +#include <sys/vdev_impl.h> + +/* + * Get or create nvlist. If key is not NULL, get nvlist from bootenv, + * otherwise return bootenv. + */ +int +lzbe_nvlist_get(const char *pool, const char *key, void **ptr) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + int rv = -1; + + if (pool == NULL || *pool == '\0') + return (rv); + + if ((hdl = libzfs_init()) == NULL) { + return (rv); + } + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + nvlist_t *nvl, *dup; + + if (key != NULL) { + rv = nvlist_lookup_nvlist(nv, key, &nvl); + if (rv == 0) { + rv = nvlist_dup(nvl, &dup, 0); + nvlist_free(nv); + if (rv == 0) + nv = dup; + else + nv = NULL; + } else { + nvlist_free(nv); + rv = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0); + } + } + *ptr = nv; + } + + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} + +int +lzbe_nvlist_set(const char *pool, const char *key, void *ptr) +{ + libzfs_handle_t *hdl; + zpool_handle_t *zphdl; + nvlist_t *nv; + uint64_t version; + int rv = -1; + + if (pool == NULL || *pool == '\0') + return (rv); + + if ((hdl = libzfs_init()) == NULL) { + return (rv); + } + + zphdl = zpool_open(hdl, pool); + if (zphdl == NULL) { + libzfs_fini(hdl); + return (rv); + } + + if (key != NULL) { + rv = zpool_get_bootenv(zphdl, &nv); + if (rv == 0) { + /* + * We got the nvlist, check for version. + * if version is missing or is not VB_NVLIST, + * create new list. + */ + rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION, + &version); + if (rv != 0 || version != VB_NVLIST) { + /* Drop this nvlist */ + fnvlist_free(nv); + /* Create and prepare new nvlist */ + nv = fnvlist_alloc(); + fnvlist_add_uint64(nv, BOOTENV_VERSION, + VB_NVLIST); + } + rv = nvlist_add_nvlist(nv, key, ptr); + if (rv == 0) + rv = zpool_set_bootenv(zphdl, nv); + nvlist_free(nv); + } + } else { + rv = zpool_set_bootenv(zphdl, ptr); + } + + zpool_close(zphdl); + libzfs_fini(hdl); + return (rv); +} + +/* + * free nvlist we got via lzbe_nvlist_get() + */ +void +lzbe_nvlist_free(void *ptr) +{ + nvlist_free(ptr); +} + +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" +}; + +static int +nvpair_type_from_name(const char *name) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(typenames); i++) { + if (strcmp(name, typenames[i]) == 0) + return (i); + } + return (0); +} + +/* + * Add pair defined by key, type and value into nvlist. + */ +int +lzbe_add_pair(void *ptr, const char *key, const char *type, void *value, + size_t size) +{ + nvlist_t *nv = ptr; + data_type_t dt; + int rv = 0; + + if (ptr == NULL || key == NULL || value == NULL) + return (rv); + + if (type == NULL) + type = "DATA_TYPE_STRING"; + dt = nvpair_type_from_name(type); + if (dt == DATA_TYPE_UNKNOWN) + return (EINVAL); + + switch (dt) { + case DATA_TYPE_BYTE: + if (size != sizeof (uint8_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_byte(nv, key, *(uint8_t *)value); + break; + + case DATA_TYPE_INT16: + if (size != sizeof (int16_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int16(nv, key, *(int16_t *)value); + break; + + case DATA_TYPE_UINT16: + if (size != sizeof (uint16_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint16(nv, key, *(uint16_t *)value); + break; + + case DATA_TYPE_INT32: + if (size != sizeof (int32_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int32(nv, key, *(int32_t *)value); + break; + + case DATA_TYPE_UINT32: + if (size != sizeof (uint32_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint32(nv, key, *(uint32_t *)value); + break; + + case DATA_TYPE_INT64: + if (size != sizeof (int64_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int64(nv, key, *(int64_t *)value); + break; + + case DATA_TYPE_UINT64: + if (size != sizeof (uint64_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint64(nv, key, *(uint64_t *)value); + break; + + case DATA_TYPE_STRING: + rv = nvlist_add_string(nv, key, value); + break; + + case DATA_TYPE_BYTE_ARRAY: + rv = nvlist_add_byte_array(nv, key, value, size); + break; + + case DATA_TYPE_INT16_ARRAY: + rv = nvlist_add_int16_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT16_ARRAY: + rv = nvlist_add_uint16_array(nv, key, value, size); + break; + + case DATA_TYPE_INT32_ARRAY: + rv = nvlist_add_int32_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT32_ARRAY: + rv = nvlist_add_uint32_array(nv, key, value, size); + break; + + case DATA_TYPE_INT64_ARRAY: + rv = nvlist_add_int64_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT64_ARRAY: + rv = nvlist_add_uint64_array(nv, key, value, size); + break; + + case DATA_TYPE_STRING_ARRAY: + rv = nvlist_add_string_array(nv, key, value, size); + break; + + case DATA_TYPE_NVLIST: + rv = nvlist_add_nvlist(nv, key, (nvlist_t *)value); + break; + + case DATA_TYPE_NVLIST_ARRAY: + rv = nvlist_add_nvlist_array(nv, key, value, size); + break; + + case DATA_TYPE_BOOLEAN_VALUE: + if (size != sizeof (boolean_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_boolean_value(nv, key, *(boolean_t *)value); + break; + + case DATA_TYPE_INT8: + if (size != sizeof (int8_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_int8(nv, key, *(int8_t *)value); + break; + + case DATA_TYPE_UINT8: + if (size != sizeof (uint8_t)) { + rv = EINVAL; + break; + } + rv = nvlist_add_uint8(nv, key, *(uint8_t *)value); + break; + + case DATA_TYPE_BOOLEAN_ARRAY: + rv = nvlist_add_boolean_array(nv, key, value, size); + break; + + case DATA_TYPE_INT8_ARRAY: + rv = nvlist_add_int8_array(nv, key, value, size); + break; + + case DATA_TYPE_UINT8_ARRAY: + rv = nvlist_add_uint8_array(nv, key, value, size); + break; + + default: + return (ENOTSUP); + } + + return (rv); +} + +int +lzbe_remove_pair(void *ptr, const char *key) +{ + + return (nvlist_remove_all(ptr, key)); +} diff --git a/usr/src/lib/libzfsbootenv/common/lzbe_util.c b/usr/src/lib/libzfsbootenv/common/lzbe_util.c new file mode 100644 index 0000000000..35e9854958 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/lzbe_util.c @@ -0,0 +1,39 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#include <sys/types.h> +#include <string.h> +#include <libzfs.h> +#include <libzfsbootenv.h> + +/* + * Output bootenv information. + */ +int +lzbe_bootenv_print(const char *pool, const char *nvlist, FILE *of) +{ + nvlist_t *nv; + int rv = -1; + + if (pool == NULL || *pool == '\0' || of == NULL) + return (rv); + + rv = lzbe_nvlist_get(pool, nvlist, (void **)&nv); + if (rv == 0) { + nvlist_print(of, nv); + nvlist_free(nv); + } + + return (rv); +} diff --git a/usr/src/lib/libzfsbootenv/common/mapfile-vers b/usr/src/lib/libzfsbootenv/common/mapfile-vers new file mode 100644 index 0000000000..62b394c997 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/common/mapfile-vers @@ -0,0 +1,44 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION ILLUMOSprivate { + global: + lzbe_add_pair; + lzbe_bootenv_print; + lzbe_get_boot_device; + lzbe_nvlist_free; + lzbe_nvlist_get; + lzbe_nvlist_set; + lzbe_remove_pair; + lzbe_set_boot_device; + local: + *; +}; diff --git a/usr/src/lib/libzfsbootenv/i386/Makefile b/usr/src/lib/libzfsbootenv/i386/Makefile new file mode 100644 index 0000000000..ba9853a5a5 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/i386/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libzfsbootenv/sparc/Makefile b/usr/src/lib/libzfsbootenv/sparc/Makefile new file mode 100644 index 0000000000..ba9853a5a5 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/sparc/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libzfsbootenv/sparcv9/Makefile b/usr/src/lib/libzfsbootenv/sparcv9/Makefile new file mode 100644 index 0000000000..dd76c2b252 --- /dev/null +++ b/usr/src/lib/libzfsbootenv/sparcv9/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Toomas Soome <tsoome@me.com> +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/man/man1m/beadm.1m b/usr/src/man/man1m/beadm.1m index 29d470a392..250f4b4400 100644 --- a/usr/src/man/man1m/beadm.1m +++ b/usr/src/man/man1m/beadm.1m @@ -1,7 +1,7 @@ '\" te .\" Copyright 2013 Nexenta Systems, Inc. All rights reserved. .\" Copyright 2016 Toomas Soome <tsoome@me.com> -.TH BEADM 1M "Feb 21, 2016" +.TH BEADM 1M "May 2, 2020" .SH NAME beadm \- utility for managing zfs boot environments .SH SYNOPSIS @@ -45,7 +45,7 @@ beadm \- utility for managing zfs boot environments .LP .nf -\fBbeadm\fR \fBactivate\fR [\fB-v\fR] \fIbeName\fR +\fBbeadm\fR \fBactivate\fR [\fB-v\fR] [\fB-t\fR | \fB-T\fR] \fIbeName\fR .fi .LP @@ -338,7 +338,9 @@ The 'Active' field indicates whether the boot environment is active now, represented by 'N'; active on reboot, represented by 'R'; or both, represented -by 'NR'. In non-global zone the 'Active' field also indicates whether the +by 'NR'. If temporary next boot configuration is used, the boot environment +is marked with 'T'. +In non-global zone the 'Active' field also indicates whether the boot environment has a non-active parent BE, represented by 'x'; is active on boot in a non-active parent BE, represented by 'b'. Activate, rollback and snapshot operations for boot environments from non-active global parent @@ -539,7 +541,7 @@ Verbose mode. Displays verbose error messages from \fBbeadm\fR. .sp .ne 2 .na -\fBbeadm\fR \fBactivate\fR [\fB-v\fR] \fIbeName\fR +\fBbeadm\fR \fBactivate\fR [\fB-v\fR] [\fB-t\fR | \fB-T\fR] \fIbeName\fR .ad .sp .6 .RS 4n @@ -547,6 +549,29 @@ Makes beName the active BE on next reboot. .sp .ne 2 .na +\fB-t\fR +.ad +.sp .6 +.RS 4n +Sets temporary, one time activation. For next boot, the \fIbeName\fR +is used for boot, and the temporary activation is removed. +When temporary activation is removed, the next boot will use zfs dataset +specified in boot pool \fIbootfs\fR property. +.RE + +.sp +.ne 2 +.na +\fB-T\fR +.ad +.sp .6 +.RS 4n +Removes temporary next boot configuration of \fIbeName\fR. +.RE + +.sp +.ne 2 +.na \fB-v\fR .ad .sp .6 @@ -793,6 +818,32 @@ BE5;215b8387-4968-627c-d2d0-f4a011414bab;NR;/;7786206208;static;1221004384 .fi .in -2 .sp +.LP +\fBExample 19\fR: Create and list temporary activation. + +.sp +.in +2 +.nf +\fB# beadm list\fR +BE Active Mountpoint Space Policy Created +-- ------ ---------- ----- ------ ------- +BE2 - - 4,85M static 2016-10-30 14:29 +BE3 - - 4,84M static 2016-10-30 18:53 +BE4 - - 5,54M static 2016-10-30 23:53 +BE5 NR / 12,6G static 2016-11-06 01:46 +\fB# beadm create BE6\fR +\fB# beadm activate -t BE6\fR +\fB# beadm list\fR +BE Active Mountpoint Space Policy Created +-- ------ ---------- ----- ------ ------- +BE2 - - 4,85M static 2016-10-30 14:29 +BE3 - - 4,84M static 2016-10-30 18:53 +BE4 - - 5,54M static 2016-10-30 23:53 +BE5 NR / 60,0K static 2016-11-06 01:46 +BE6 T - 12,6G static 2016-11-06 12:56 +.fi +.in -2 +.sp .SH EXIT STATUS The following exit values are returned: diff --git a/usr/src/man/man3c/pthread_attr_get_np.3c b/usr/src/man/man3c/pthread_attr_get_np.3c index 9da50ab171..2b166d0ef4 100644 --- a/usr/src/man/man3c/pthread_attr_get_np.3c +++ b/usr/src/man/man3c/pthread_attr_get_np.3c @@ -11,7 +11,7 @@ .\" .\" Copyright 2016 Joyent, Inc. .\" -.Dd November 8, 2020 +.Dd November 10, 2020 .Dt PTHREAD_ATTR_GET_NP 3C .Os .Sh NAME @@ -100,13 +100,11 @@ For more information on the guard size of a thread and its purpose, see .Sh RETURN VALUES Upon successful completion, the .Fn pthread_attr_get_np -and -.Fn pthread_getattr_np -functions return +function returns .Sy 0 . Otherwise, an error number is returned to indicate the error. .Sh EXAMPLES -The following program demonstrates how to use these functions to get +The following program demonstrates how to use this function to get the location and stack size of a newly created thread. .Bd -literal #include <assert.h> diff --git a/usr/src/man/man3ext/Makefile b/usr/src/man/man3ext/Makefile index 0f10a18e54..89dd766f6f 100644 --- a/usr/src/man/man3ext/Makefile +++ b/usr/src/man/man3ext/Makefile @@ -20,7 +20,6 @@ MANSECT= 3ext MANFILES= NOTE.3ext \ SUNW_C_GetMechSession.3ext \ - auto_ef.3ext \ crypt.3ext \ demangle.3ext \ ecb_crypt.3ext \ @@ -63,11 +62,6 @@ MANLINKS= DES_FAILED.3ext \ SHA512Update.3ext \ SUNW_C_KeyToObject.3ext \ _NOTE.3ext \ - auto_ef_file.3ext \ - auto_ef_free.3ext \ - auto_ef_get_encoding.3ext \ - auto_ef_get_score.3ext \ - auto_ef_str.3ext \ cbc_crypt.3ext \ cplus_demangle.3ext \ crypt_close.3ext \ @@ -132,12 +126,6 @@ _NOTE.3ext := LINKSRC = NOTE.3ext SUNW_C_KeyToObject.3ext := LINKSRC = SUNW_C_GetMechSession.3ext -auto_ef_file.3ext := LINKSRC = auto_ef.3ext -auto_ef_free.3ext := LINKSRC = auto_ef.3ext -auto_ef_get_encoding.3ext := LINKSRC = auto_ef.3ext -auto_ef_get_score.3ext := LINKSRC = auto_ef.3ext -auto_ef_str.3ext := LINKSRC = auto_ef.3ext - crypt_close.3ext := LINKSRC = crypt.3ext des_crypt.3ext := LINKSRC = crypt.3ext des_encrypt.3ext := LINKSRC = crypt.3ext diff --git a/usr/src/man/man3ext/auto_ef.3ext b/usr/src/man/man3ext/auto_ef.3ext deleted file mode 100644 index 82537dd139..0000000000 --- a/usr/src/man/man3ext/auto_ef.3ext +++ /dev/null @@ -1,202 +0,0 @@ -'\" te -.\" Copyright (c) 1983 Regents of the University of California. All rights reserved. The Berkeley software License Agreement specifies the terms and conditions for redistribution. Copyright (c) 2003, Sun Microsystems, Inc. All Rights Reserved -.TH AUTO_EF 3EXT "Sep 22, 2003" -.SH NAME -auto_ef, auto_ef_file, auto_ef_str, auto_ef_free, auto_ef_get_encoding, -auto_ef_get_score \- auto encoding finder functions -.SH SYNOPSIS -.LP -.nf -cc [ \fIflag \fR... ] \fIfile\fR... \fB-lauto_ef\fR [ \fIlibrary\fR... ] -#include <auto_ef.h> - -\fBsize_t\fR \fBauto_ef_file\fR(\fBauto_ef_t **\fR\fIinfo\fR, \fBconst char *\fR\fIfilename\fR, \fBint\fR \fIflags\fR); -.fi - -.LP -.nf -\fBsize_t\fR \fBauto_ef_str\fR(\fBauto_ef_t **\fR\fIinfo\fR, \fBconst char *\fR\fIbuffer\fR, \fBsize_t\fR \fIbufsize\fR, - \fBint\fR \fIflags\fR); -.fi - -.LP -.nf -\fBvoid\fR \fBauto_ef_free\fR(\fBauto_ef_t *\fR\fIinfo\fR); -.fi - -.LP -.nf -\fBchar *\fR\fBauto_ef_get_encoding\fR(\fBauto_ef_t\fR \fIinfo\fR); -.fi - -.LP -.nf -\fBdouble\fR \fBauto_ef_get_score\fR(\fBauto_ef_t\fR \fIinfo\fR); -.fi - -.SH DESCRIPTION -.sp -.LP -Auto encoding finder provides functions that find the encoding of given file or -string. -.sp -.LP -The \fBauto_ef_file()\fR function examines text in the file specified with -\fIfilename\fR and returns information on possible encodings. -.sp -.LP -The \fIinfo\fR argument is a pointer to a pointer to an \fBauto_ef_t\fR, the -location at which the pointer to the \fBauto_ef_t\fR array is stored upon -return. -.sp -.LP -The \fIflags\fR argument specifies the level of examination. Currently only one -set of flags, exclusive each other, is available: \fBAE_LEVEL_0\fR, -\fBAE_LEVEL_1\fR, \fBAE_LEVEL_2\fR, and \fBAE_LEVEL_3\fR. The \fBAE_LEVEL_0\fR -level is fastest but the result can be less accurate. The \fBAE_LEVEL_3\fR -level produces best result but can be slow. If the \fIflags\fR argument is -unspecified, the default is \fBAE_LEVEL_0\fR. When another flag or set of flags -are defined in the future, use the inclusive-bitwise OR operation to specify -multiple flags. -.sp -.LP -Information about encodings are stored in data type\fBauto_ef_t\fR in the order -of possibility with the most possible encoding stored first. To examine the -information, use the \fBauto_ef_get_encoding()\fR and \fBauto_ef_get_score()\fR -access functions. For a list of encodings with which \fBauto_ef_file()\fR can -examine text, see \fBauto_ef\fR(3EXT). -.sp -.LP -If \fBauto_ef_file()\fR cannot determine the encoding of text, it returns 0 and -stores \fINULL\fR at the location pointed by \fIinfo\fR. -.sp -.LP -The \fBauto_ef_get_encoding()\fR function returns the name of the encoding. The -returned string is vaild until until the location pointed to by \fIinfo\fR is -freed with \fBauto_ef_free()\fR. Applications should not use \fBfree\fR(3C) to -free the pointer returned by \fBauto_ef_get_encoding()\fR. -.sp -.LP -The \fBauto_ef_get_score()\fR function returns the score of this encoding in -the range between 0.0 and 1.0. -.sp -.LP -The \fBauto_ef_str()\fR function is identical to \fBauto_ef_file()\fR, except -that it examines text in the buffer specified by \fIbuffer\fR with a maximum -size of \fIbufsize\fR bytes, instead of text in a file. -.sp -.LP -The \fBauto_ef_free()\fR function frees the area allocated by -\fBauto_ef_file()\fR or by \fBauto_ef_str()\fR, taking as its argument the -pointer stored at the location pointed to by \fIinfo\fR. -.SH RETURN VALUES -.sp -.LP -Upon successful completion, the \fBauto_ef_file()\fR and \fBauto_ef_str()\fR -functions return the number of possible encodings for which information is -stored. Otherwise, \(mi1 is returned. -.sp -.LP -The \fBauto_ef_get_encoding()\fR function returns the string that stores the -encoding name. -.sp -.LP -the \fBauto_ef_get_score()\fR function returns the score value for encoding the -name with the examined text data. -.SH ERRORS -.sp -.LP -The \fBauto_ef_file()\fR and \fBauto_ef_str()\fR will fail if: -.sp -.ne 2 -.na -\fB\fBEACCES\fR\fR -.ad -.RS 10n -Search permission is denied on a component of the path prefix, the file exists -and the permissions specified by mode are denied, the file does not exist and -write permission is denied for the parent directory of the file to be created, -or \fBlibauto_ef\fR cannot find the internal hashtable. -.RE - -.sp -.ne 2 -.na -\fB\fBEINTR\fR\fR -.ad -.RS 10n -A signal was caught during the execution. -.RE - -.sp -.ne 2 -.na -\fB\fBENOMEM\fR\fR -.ad -.RS 10n -Failed to allocate area to store the result. -.RE - -.sp -.ne 2 -.na -\fB\fBEMFILE\fR\fR -.ad -.RS 10n -Too many files descriptors are currently open in the calling process. -.RE - -.sp -.ne 2 -.na -\fB\fBENFILE\fR\fR -.ad -.RS 10n -Too many files are currently open in the system. -.RE - -.SH EXAMPLES -.LP -\fBExample 1 \fRSpecify the array index to examine stored information. -.sp -.LP -Since \fBauto_ef_file()\fR stores the array whose elements hold information on -each possible encoding, the following example specifies the array index to -examine the stored information. - -.sp -.in +2 -.nf -#include <auto_ef.h> -auto_ef_t *array_info; -size_t number; -char *encoding; - -number = auto_ef_file(&array_info, filename, flags); -encoding = auto_ef_get_encoding(array_info[0]); -auto_ef_free(array_info); -.fi -.in -2 - -.SH ATTRIBUTES -.sp -.LP -See \fBattributes\fR(5) for descriptions of the following attributes: -.sp - -.sp -.TS -box; -c | c -l | l . -ATTRIBUTE TYPE ATTRIBUTE VALUE -_ -Interface Stability Stable -_ -MT-Level MT-Safe -.TE - -.SH SEE ALSO -.sp -.LP -\fBauto_ef\fR(3EXT), \fBlibauto_ef\fR(3LIB), \fBattributes\fR(5) diff --git a/usr/src/pkg/manifests/developer-build-onbld.mf b/usr/src/pkg/manifests/developer-build-onbld.mf index 53a9df7eb8..cfedad950b 100644 --- a/usr/src/pkg/manifests/developer-build-onbld.mf +++ b/usr/src/pkg/manifests/developer-build-onbld.mf @@ -376,6 +376,7 @@ file path=opt/onbld/man/man1onbld/cddlchk.1onbld file path=opt/onbld/man/man1onbld/check_rtime.1onbld file path=opt/onbld/man/man1onbld/checkpaths.1onbld file path=opt/onbld/man/man1onbld/cstyle.1onbld +file path=opt/onbld/man/man1onbld/ctfconvert.1onbld file path=opt/onbld/man/man1onbld/cw.1onbld file path=opt/onbld/man/man1onbld/find_elf.1onbld file path=opt/onbld/man/man1onbld/findunref.1onbld diff --git a/usr/src/pkg/manifests/system-file-system-zfs.mf b/usr/src/pkg/manifests/system-file-system-zfs.mf index 69ef355916..71d4781811 100644 --- a/usr/src/pkg/manifests/system-file-system-zfs.mf +++ b/usr/src/pkg/manifests/system-file-system-zfs.mf @@ -85,11 +85,13 @@ file path=lib/libzutil.so.1 file path=sbin/zfs mode=0555 file path=sbin/zpool mode=0555 file path=usr/lib/$(ARCH64)/libzfs_jni.so.1 +file path=usr/lib/$(ARCH64)/libzfsbootenv.so.1 file path=usr/lib/$(ARCH64)/libzpool.so.1 file path=usr/lib/devfsadm/linkmod/SUNW_zfs_link.so group=sys file path=usr/lib/fs/zfs/bootinstall mode=0555 file path=usr/lib/fs/zfs/fstyp.so.1 mode=0555 file path=usr/lib/libzfs_jni.so.1 +file path=usr/lib/libzfsbootenv.so.1 $(i386_ONLY)file path=usr/lib/libzpool.so.1 file path=usr/lib/mdb/kvm/$(ARCH64)/zfs.so group=sys mode=0555 file path=usr/lib/mdb/proc/$(ARCH64)/libzpool.so group=sys mode=0555 @@ -138,6 +140,7 @@ link path=usr/lib/$(ARCH64)/libzfs_core.so \ link path=usr/lib/$(ARCH64)/libzfs_core.so.1 \ target=../../../lib/$(ARCH64)/libzfs_core.so.1 link path=usr/lib/$(ARCH64)/libzfs_jni.so target=libzfs_jni.so.1 +link path=usr/lib/$(ARCH64)/libzfsbootenv.so target=libzfsbootenv.so.1 link path=usr/lib/$(ARCH64)/libzpool.so target=libzpool.so.1 link path=usr/lib/$(ARCH64)/libzutil.so \ target=../../../lib/$(ARCH64)/libzutil.so.1 @@ -150,6 +153,7 @@ link path=usr/lib/libzfs.so.1 target=../../lib/libzfs.so.1 link path=usr/lib/libzfs_core.so target=../../lib/libzfs_core.so.1 link path=usr/lib/libzfs_core.so.1 target=../../lib/libzfs_core.so.1 link path=usr/lib/libzfs_jni.so target=libzfs_jni.so.1 +link path=usr/lib/libzfsbootenv.so target=libzfsbootenv.so.1 $(i386_ONLY)link path=usr/lib/libzpool.so target=libzpool.so.1 link path=usr/lib/libzutil.so target=../../lib/libzutil.so.1 link path=usr/lib/libzutil.so.1 target=../../lib/libzutil.so.1 diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf index 8a316a1163..6ad8abf015 100644 --- a/usr/src/pkg/manifests/system-header.mf +++ b/usr/src/pkg/manifests/system-header.mf @@ -510,6 +510,7 @@ $(sparc_ONLY)file path=usr/include/libv12n.h file path=usr/include/libw.h file path=usr/include/libzfs.h file path=usr/include/libzfs_core.h +file path=usr/include/libzfsbootenv.h file path=usr/include/libzoneinfo.h file path=usr/include/limits.h file path=usr/include/linenum.h diff --git a/usr/src/pkg/manifests/system-library.man3ext.inc b/usr/src/pkg/manifests/system-library.man3ext.inc index 12e6950a7e..9be2d4df16 100644 --- a/usr/src/pkg/manifests/system-library.man3ext.inc +++ b/usr/src/pkg/manifests/system-library.man3ext.inc @@ -16,7 +16,6 @@ file path=usr/share/man/man3ext/NOTE.3ext file path=usr/share/man/man3ext/SUNW_C_GetMechSession.3ext -file path=usr/share/man/man3ext/auto_ef.3ext file path=usr/share/man/man3ext/crypt.3ext file path=usr/share/man/man3ext/demangle.3ext file path=usr/share/man/man3ext/ecb_crypt.3ext @@ -59,11 +58,6 @@ link path=usr/share/man/man3ext/SHA512Update.3ext target=sha2.3ext link path=usr/share/man/man3ext/SUNW_C_KeyToObject.3ext \ target=SUNW_C_GetMechSession.3ext link path=usr/share/man/man3ext/_NOTE.3ext target=NOTE.3ext -link path=usr/share/man/man3ext/auto_ef_file.3ext target=auto_ef.3ext -link path=usr/share/man/man3ext/auto_ef_free.3ext target=auto_ef.3ext -link path=usr/share/man/man3ext/auto_ef_get_encoding.3ext target=auto_ef.3ext -link path=usr/share/man/man3ext/auto_ef_get_score.3ext target=auto_ef.3ext -link path=usr/share/man/man3ext/auto_ef_str.3ext target=auto_ef.3ext link path=usr/share/man/man3ext/cbc_crypt.3ext target=ecb_crypt.3ext link path=usr/share/man/man3ext/cplus_demangle.3ext target=demangle.3ext link path=usr/share/man/man3ext/crypt_close.3ext target=crypt.3ext diff --git a/usr/src/test/test-runner/cmd/run b/usr/src/test/test-runner/cmd/run index 8b9129f4e2..fe0af912f6 100644 --- a/usr/src/test/test-runner/cmd/run +++ b/usr/src/test/test-runner/cmd/run @@ -15,6 +15,7 @@ # Copyright (c) 2012, 2016 by Delphix. All rights reserved. # Copyright (c) 2017, Chris Fraire <cfraire@me.com>. # Copyright 2019 Joyent, Inc. +# Copyright 2020 OmniOS Community Edition (OmniOSce) Association. # from __future__ import print_function @@ -114,13 +115,13 @@ class Result(object): self.result = 'KILLED' Result.runresults['KILLED'] += 1 retcode = 2; - elif self.returncode is 0: + elif self.returncode == 0: self.result = 'PASS' Result.runresults['PASS'] += 1 - elif self.returncode is 3 or self.returncode is 4: + elif self.returncode == 3 or self.returncode == 4: self.result = 'SKIP' Result.runresults['SKIP'] += 1 - elif self.returncode is not 0: + elif self.returncode != 0: self.result = 'FAIL' Result.runresults['FAIL'] += 1 retcode = 1; @@ -335,7 +336,7 @@ class Cmd(object): # line summary will only be printed in the logfile for failures. if not options.quiet: logger.info('%s%s%s' % (msga, pad, msgb)) - elif self.result.result is not 'PASS': + elif self.result.result != 'PASS': logger.info('%s%s%s' % (msga, pad, msgb)) else: logger.debug('%s%s%s' % (msga, pad, msgb)) @@ -431,7 +432,7 @@ class Test(Cmd): cont = True if len(pretest.pathname): pretest.run(options) - cont = pretest.result.result is 'PASS' + cont = pretest.result.result == 'PASS' pretest.log(logger, options) if cont: @@ -506,7 +507,7 @@ class TestGroup(Test): self.tests[:] = [f for f in self.tests if verify_file(os.path.join(self.pathname, f))] - return len(self.tests) is not 0 + return len(self.tests) != 0 def run(self, logger, options): """ @@ -524,7 +525,7 @@ class TestGroup(Test): cont = True if len(pretest.pathname): pretest.run(options) - cont = pretest.result.result is 'PASS' + cont = pretest.result.result == 'PASS' pretest.log(logger, options) for fname in self.tests: @@ -742,7 +743,7 @@ class TestRun(object): testlogger = logging.getLogger(__name__) testlogger.setLevel(logging.DEBUG) - if options.cmd is not 'wrconfig': + if options.cmd != 'wrconfig': try: old = os.umask(0) os.makedirs(self.outputdir, mode=0o777) @@ -780,12 +781,12 @@ class TestRun(object): self.testgroups[testgroup].run(self.logger, options) def summary(self): - if Result.total is 0: + if Result.total == 0: return print('\nResults Summary') for key in list(Result.runresults.keys()): - if Result.runresults[key] is not 0: + if Result.runresults[key] != 0: print('%s\t% 4d' % (key, Result.runresults[key])) m, s = divmod(time() - self.starttime, 60) @@ -841,7 +842,7 @@ def verify_user(user, logger): p = Popen(testcmd) p.wait() - if p.returncode is not 0: + if p.returncode != 0: logger.info("Warning: user '%s' cannot use passwordless sudo.", user) return False else: @@ -877,16 +878,16 @@ def fail(retstr, ret=1): def options_cb(option, opt_str, value, parser): path_options = ['runfile', 'outputdir', 'template'] - if option.dest is 'runfile' and '-w' in parser.rargs or \ - option.dest is 'template' and '-c' in parser.rargs: + if option.dest == 'runfile' and '-w' in parser.rargs or \ + option.dest == 'template' and '-c' in parser.rargs: fail('-c and -w are mutually exclusive.') if opt_str in parser.rargs: fail('%s may only be specified once.' % opt_str) - if option.dest is 'runfile': + if option.dest == 'runfile': parser.values.cmd = 'rdconfig' - if option.dest is 'template': + if option.dest == 'template': parser.values.cmd = 'wrconfig' setattr(parser.values, option.dest, value) @@ -946,11 +947,11 @@ def main(): options = parse_args() testrun = TestRun(options) - if options.cmd is 'runtests': + if options.cmd == 'runtests': find_tests(testrun, options) - elif options.cmd is 'rdconfig': + elif options.cmd == 'rdconfig': testrun.read(testrun.logger, options) - elif options.cmd is 'wrconfig': + elif options.cmd == 'wrconfig': find_tests(testrun, options) testrun.write(options) exit(0) diff --git a/usr/src/test/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c b/usr/src/test/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c index b469a96982..1636875557 100644 --- a/usr/src/test/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c +++ b/usr/src/test/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c @@ -28,7 +28,9 @@ #include <unistd.h> #include <sys/nvpair.h> +#include <sys/vdev_impl.h> #include <sys/zfs_ioctl.h> +#include <sys/zfs_bootenv.h> /* * Test the nvpair inputs for the non-legacy zfs ioctl commands. @@ -108,6 +110,9 @@ static unsigned ioc_skip[] = { ZFS_IOC_EVENTS_NEXT, ZFS_IOC_EVENTS_CLEAR, ZFS_IOC_EVENTS_SEEK, + ZFS_IOC_NEXTBOOT, + ZFS_IOC_JAIL, + ZFS_IOC_UNJAIL, #else /* This is still a legacy ioctl in illumos */ ZFS_IOC_POOL_REOPEN, @@ -706,6 +711,25 @@ zfs_destroy(const char *dataset) } static void +test_get_bootenv(const char *pool) +{ + IOC_INPUT_TEST(ZFS_IOC_GET_BOOTENV, pool, NULL, NULL, 0); +} + +static void +test_set_bootenv(const char *pool) +{ + nvlist_t *required = fnvlist_alloc(); + + fnvlist_add_uint64(required, "version", VB_RAW); + fnvlist_add_string(required, GRUB_ENVMAP, "test"); + + IOC_INPUT_TEST_WILD(ZFS_IOC_SET_BOOTENV, pool, required, NULL, 0); + + nvlist_free(required); +} + +static void zfs_ioc_input_tests(const char *pool) { char filepath[] = "/tmp/ioc_test_file_XXXXXX"; @@ -789,6 +813,9 @@ zfs_ioc_input_tests(const char *pool) test_vdev_initialize(pool); test_vdev_trim(pool); + test_set_bootenv(pool); + test_get_bootenv(pool); + /* * cleanup */ @@ -834,9 +861,12 @@ zfs_ioc_input_tests(const char *pool) } enum zfs_ioc_ref { +#ifdef __FreeBSD__ + ZFS_IOC_BASE = 0, +#else ZFS_IOC_BASE = ('Z' << 8), - LINUX_IOC_BASE = ('Z' << 8) + 0x80, - FREEBSD_IOC_BASE = ('Z' << 8) + 0xC0, +#endif + ZFS_IOC_PLATFORM_BASE = ZFS_IOC_BASE + 0x80, }; /* @@ -846,100 +876,114 @@ enum zfs_ioc_ref { boolean_t validate_ioc_values(void) { - return ( - ZFS_IOC_BASE + 0 == ZFS_IOC_POOL_CREATE && - ZFS_IOC_BASE + 1 == ZFS_IOC_POOL_DESTROY && - ZFS_IOC_BASE + 2 == ZFS_IOC_POOL_IMPORT && - ZFS_IOC_BASE + 3 == ZFS_IOC_POOL_EXPORT && - ZFS_IOC_BASE + 4 == ZFS_IOC_POOL_CONFIGS && - ZFS_IOC_BASE + 5 == ZFS_IOC_POOL_STATS && - ZFS_IOC_BASE + 6 == ZFS_IOC_POOL_TRYIMPORT && - ZFS_IOC_BASE + 7 == ZFS_IOC_POOL_SCAN && - ZFS_IOC_BASE + 8 == ZFS_IOC_POOL_FREEZE && - ZFS_IOC_BASE + 9 == ZFS_IOC_POOL_UPGRADE && - ZFS_IOC_BASE + 10 == ZFS_IOC_POOL_GET_HISTORY && - ZFS_IOC_BASE + 11 == ZFS_IOC_VDEV_ADD && - ZFS_IOC_BASE + 12 == ZFS_IOC_VDEV_REMOVE && - ZFS_IOC_BASE + 13 == ZFS_IOC_VDEV_SET_STATE && - ZFS_IOC_BASE + 14 == ZFS_IOC_VDEV_ATTACH && - ZFS_IOC_BASE + 15 == ZFS_IOC_VDEV_DETACH && - ZFS_IOC_BASE + 16 == ZFS_IOC_VDEV_SETPATH && - ZFS_IOC_BASE + 17 == ZFS_IOC_VDEV_SETFRU && - ZFS_IOC_BASE + 18 == ZFS_IOC_OBJSET_STATS && - ZFS_IOC_BASE + 19 == ZFS_IOC_OBJSET_ZPLPROPS && - ZFS_IOC_BASE + 20 == ZFS_IOC_DATASET_LIST_NEXT && - ZFS_IOC_BASE + 21 == ZFS_IOC_SNAPSHOT_LIST_NEXT && - ZFS_IOC_BASE + 22 == ZFS_IOC_SET_PROP && - ZFS_IOC_BASE + 23 == ZFS_IOC_CREATE && - ZFS_IOC_BASE + 24 == ZFS_IOC_DESTROY && - ZFS_IOC_BASE + 25 == ZFS_IOC_ROLLBACK && - ZFS_IOC_BASE + 26 == ZFS_IOC_RENAME && - ZFS_IOC_BASE + 27 == ZFS_IOC_RECV && - ZFS_IOC_BASE + 28 == ZFS_IOC_SEND && - ZFS_IOC_BASE + 29 == ZFS_IOC_INJECT_FAULT && - ZFS_IOC_BASE + 30 == ZFS_IOC_CLEAR_FAULT && - ZFS_IOC_BASE + 31 == ZFS_IOC_INJECT_LIST_NEXT && - ZFS_IOC_BASE + 32 == ZFS_IOC_ERROR_LOG && - ZFS_IOC_BASE + 33 == ZFS_IOC_CLEAR && - ZFS_IOC_BASE + 34 == ZFS_IOC_PROMOTE && - ZFS_IOC_BASE + 35 == ZFS_IOC_SNAPSHOT && - ZFS_IOC_BASE + 36 == ZFS_IOC_DSOBJ_TO_DSNAME && - ZFS_IOC_BASE + 37 == ZFS_IOC_OBJ_TO_PATH && - ZFS_IOC_BASE + 38 == ZFS_IOC_POOL_SET_PROPS && - ZFS_IOC_BASE + 39 == ZFS_IOC_POOL_GET_PROPS && - ZFS_IOC_BASE + 40 == ZFS_IOC_SET_FSACL && - ZFS_IOC_BASE + 41 == ZFS_IOC_GET_FSACL && - ZFS_IOC_BASE + 42 == ZFS_IOC_SHARE && - ZFS_IOC_BASE + 43 == ZFS_IOC_INHERIT_PROP && - ZFS_IOC_BASE + 44 == ZFS_IOC_SMB_ACL && - ZFS_IOC_BASE + 45 == ZFS_IOC_USERSPACE_ONE && - ZFS_IOC_BASE + 46 == ZFS_IOC_USERSPACE_MANY && - ZFS_IOC_BASE + 47 == ZFS_IOC_USERSPACE_UPGRADE && - ZFS_IOC_BASE + 48 == ZFS_IOC_HOLD && - ZFS_IOC_BASE + 49 == ZFS_IOC_RELEASE && - ZFS_IOC_BASE + 50 == ZFS_IOC_GET_HOLDS && - ZFS_IOC_BASE + 51 == ZFS_IOC_OBJSET_RECVD_PROPS && - ZFS_IOC_BASE + 52 == ZFS_IOC_VDEV_SPLIT && - ZFS_IOC_BASE + 53 == ZFS_IOC_NEXT_OBJ && - ZFS_IOC_BASE + 54 == ZFS_IOC_DIFF && - ZFS_IOC_BASE + 55 == ZFS_IOC_TMP_SNAPSHOT && - ZFS_IOC_BASE + 56 == ZFS_IOC_OBJ_TO_STATS && - ZFS_IOC_BASE + 57 == ZFS_IOC_SPACE_WRITTEN && - ZFS_IOC_BASE + 58 == ZFS_IOC_SPACE_SNAPS && - ZFS_IOC_BASE + 59 == ZFS_IOC_DESTROY_SNAPS && - ZFS_IOC_BASE + 60 == ZFS_IOC_POOL_REGUID && - ZFS_IOC_BASE + 61 == ZFS_IOC_POOL_REOPEN && - ZFS_IOC_BASE + 62 == ZFS_IOC_SEND_PROGRESS && - ZFS_IOC_BASE + 63 == ZFS_IOC_LOG_HISTORY && - ZFS_IOC_BASE + 64 == ZFS_IOC_SEND_NEW && - ZFS_IOC_BASE + 65 == ZFS_IOC_SEND_SPACE && - ZFS_IOC_BASE + 66 == ZFS_IOC_CLONE && - ZFS_IOC_BASE + 67 == ZFS_IOC_BOOKMARK && - ZFS_IOC_BASE + 68 == ZFS_IOC_GET_BOOKMARKS && - ZFS_IOC_BASE + 69 == ZFS_IOC_DESTROY_BOOKMARKS && + boolean_t result = B_TRUE; + +#define CHECK(expr) do { \ + if (!(expr)) { \ + result = B_FALSE; \ + fprintf(stderr, "(%s) === FALSE\n", #expr); \ + } \ +} while (0) + + CHECK(ZFS_IOC_BASE + 0 == ZFS_IOC_POOL_CREATE); + CHECK(ZFS_IOC_BASE + 1 == ZFS_IOC_POOL_DESTROY); + CHECK(ZFS_IOC_BASE + 2 == ZFS_IOC_POOL_IMPORT); + CHECK(ZFS_IOC_BASE + 3 == ZFS_IOC_POOL_EXPORT); + CHECK(ZFS_IOC_BASE + 4 == ZFS_IOC_POOL_CONFIGS); + CHECK(ZFS_IOC_BASE + 5 == ZFS_IOC_POOL_STATS); + CHECK(ZFS_IOC_BASE + 6 == ZFS_IOC_POOL_TRYIMPORT); + CHECK(ZFS_IOC_BASE + 7 == ZFS_IOC_POOL_SCAN); + CHECK(ZFS_IOC_BASE + 8 == ZFS_IOC_POOL_FREEZE); + CHECK(ZFS_IOC_BASE + 9 == ZFS_IOC_POOL_UPGRADE); + CHECK(ZFS_IOC_BASE + 10 == ZFS_IOC_POOL_GET_HISTORY); + CHECK(ZFS_IOC_BASE + 11 == ZFS_IOC_VDEV_ADD); + CHECK(ZFS_IOC_BASE + 12 == ZFS_IOC_VDEV_REMOVE); + CHECK(ZFS_IOC_BASE + 13 == ZFS_IOC_VDEV_SET_STATE); + CHECK(ZFS_IOC_BASE + 14 == ZFS_IOC_VDEV_ATTACH); + CHECK(ZFS_IOC_BASE + 15 == ZFS_IOC_VDEV_DETACH); + CHECK(ZFS_IOC_BASE + 16 == ZFS_IOC_VDEV_SETPATH); + CHECK(ZFS_IOC_BASE + 17 == ZFS_IOC_VDEV_SETFRU); + CHECK(ZFS_IOC_BASE + 18 == ZFS_IOC_OBJSET_STATS); + CHECK(ZFS_IOC_BASE + 19 == ZFS_IOC_OBJSET_ZPLPROPS); + CHECK(ZFS_IOC_BASE + 20 == ZFS_IOC_DATASET_LIST_NEXT); + CHECK(ZFS_IOC_BASE + 21 == ZFS_IOC_SNAPSHOT_LIST_NEXT); + CHECK(ZFS_IOC_BASE + 22 == ZFS_IOC_SET_PROP); + CHECK(ZFS_IOC_BASE + 23 == ZFS_IOC_CREATE); + CHECK(ZFS_IOC_BASE + 24 == ZFS_IOC_DESTROY); + CHECK(ZFS_IOC_BASE + 25 == ZFS_IOC_ROLLBACK); + CHECK(ZFS_IOC_BASE + 26 == ZFS_IOC_RENAME); + CHECK(ZFS_IOC_BASE + 27 == ZFS_IOC_RECV); + CHECK(ZFS_IOC_BASE + 28 == ZFS_IOC_SEND); + CHECK(ZFS_IOC_BASE + 29 == ZFS_IOC_INJECT_FAULT); + CHECK(ZFS_IOC_BASE + 30 == ZFS_IOC_CLEAR_FAULT); + CHECK(ZFS_IOC_BASE + 31 == ZFS_IOC_INJECT_LIST_NEXT); + CHECK(ZFS_IOC_BASE + 32 == ZFS_IOC_ERROR_LOG); + CHECK(ZFS_IOC_BASE + 33 == ZFS_IOC_CLEAR); + CHECK(ZFS_IOC_BASE + 34 == ZFS_IOC_PROMOTE); + CHECK(ZFS_IOC_BASE + 35 == ZFS_IOC_SNAPSHOT); + CHECK(ZFS_IOC_BASE + 36 == ZFS_IOC_DSOBJ_TO_DSNAME); + CHECK(ZFS_IOC_BASE + 37 == ZFS_IOC_OBJ_TO_PATH); + CHECK(ZFS_IOC_BASE + 38 == ZFS_IOC_POOL_SET_PROPS); + CHECK(ZFS_IOC_BASE + 39 == ZFS_IOC_POOL_GET_PROPS); + CHECK(ZFS_IOC_BASE + 40 == ZFS_IOC_SET_FSACL); + CHECK(ZFS_IOC_BASE + 41 == ZFS_IOC_GET_FSACL); + CHECK(ZFS_IOC_BASE + 42 == ZFS_IOC_SHARE); + CHECK(ZFS_IOC_BASE + 43 == ZFS_IOC_INHERIT_PROP); + CHECK(ZFS_IOC_BASE + 44 == ZFS_IOC_SMB_ACL); + CHECK(ZFS_IOC_BASE + 45 == ZFS_IOC_USERSPACE_ONE); + CHECK(ZFS_IOC_BASE + 46 == ZFS_IOC_USERSPACE_MANY); + CHECK(ZFS_IOC_BASE + 47 == ZFS_IOC_USERSPACE_UPGRADE); + CHECK(ZFS_IOC_BASE + 48 == ZFS_IOC_HOLD); + CHECK(ZFS_IOC_BASE + 49 == ZFS_IOC_RELEASE); + CHECK(ZFS_IOC_BASE + 50 == ZFS_IOC_GET_HOLDS); + CHECK(ZFS_IOC_BASE + 51 == ZFS_IOC_OBJSET_RECVD_PROPS); + CHECK(ZFS_IOC_BASE + 52 == ZFS_IOC_VDEV_SPLIT); + CHECK(ZFS_IOC_BASE + 53 == ZFS_IOC_NEXT_OBJ); + CHECK(ZFS_IOC_BASE + 54 == ZFS_IOC_DIFF); + CHECK(ZFS_IOC_BASE + 55 == ZFS_IOC_TMP_SNAPSHOT); + CHECK(ZFS_IOC_BASE + 56 == ZFS_IOC_OBJ_TO_STATS); + CHECK(ZFS_IOC_BASE + 57 == ZFS_IOC_SPACE_WRITTEN); + CHECK(ZFS_IOC_BASE + 58 == ZFS_IOC_SPACE_SNAPS); + CHECK(ZFS_IOC_BASE + 59 == ZFS_IOC_DESTROY_SNAPS); + CHECK(ZFS_IOC_BASE + 60 == ZFS_IOC_POOL_REGUID); + CHECK(ZFS_IOC_BASE + 61 == ZFS_IOC_POOL_REOPEN); + CHECK(ZFS_IOC_BASE + 62 == ZFS_IOC_SEND_PROGRESS); + CHECK(ZFS_IOC_BASE + 63 == ZFS_IOC_LOG_HISTORY); + CHECK(ZFS_IOC_BASE + 64 == ZFS_IOC_SEND_NEW); + CHECK(ZFS_IOC_BASE + 65 == ZFS_IOC_SEND_SPACE); + CHECK(ZFS_IOC_BASE + 66 == ZFS_IOC_CLONE); + CHECK(ZFS_IOC_BASE + 67 == ZFS_IOC_BOOKMARK); + CHECK(ZFS_IOC_BASE + 68 == ZFS_IOC_GET_BOOKMARKS); + CHECK(ZFS_IOC_BASE + 69 == ZFS_IOC_DESTROY_BOOKMARKS); #ifndef __sun - ZFS_IOC_BASE + 71 == ZFS_IOC_RECV_NEW && + CHECK(ZFS_IOC_BASE + 71 == ZFS_IOC_RECV_NEW); #endif - ZFS_IOC_BASE + 70 == ZFS_IOC_POOL_SYNC && - ZFS_IOC_BASE + 71 == ZFS_IOC_CHANNEL_PROGRAM && - ZFS_IOC_BASE + 72 == ZFS_IOC_LOAD_KEY && - ZFS_IOC_BASE + 73 == ZFS_IOC_UNLOAD_KEY && - ZFS_IOC_BASE + 74 == ZFS_IOC_CHANGE_KEY && - ZFS_IOC_BASE + 75 == ZFS_IOC_REMAP && - ZFS_IOC_BASE + 76 == ZFS_IOC_POOL_CHECKPOINT && + CHECK(ZFS_IOC_BASE + 70 == ZFS_IOC_POOL_SYNC); + CHECK(ZFS_IOC_BASE + 71 == ZFS_IOC_CHANNEL_PROGRAM); + CHECK(ZFS_IOC_BASE + 72 == ZFS_IOC_LOAD_KEY); + CHECK(ZFS_IOC_BASE + 73 == ZFS_IOC_UNLOAD_KEY); + CHECK(ZFS_IOC_BASE + 74 == ZFS_IOC_CHANGE_KEY); + CHECK(ZFS_IOC_BASE + 75 == ZFS_IOC_REMAP); + CHECK(ZFS_IOC_BASE + 76 == ZFS_IOC_POOL_CHECKPOINT); #ifndef __sun - ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_DISCARD_CHECKPOINT && - LINUX_IOC_BASE + 1 == ZFS_IOC_EVENTS_NEXT && - LINUX_IOC_BASE + 2 == ZFS_IOC_EVENTS_CLEAR && - LINUX_IOC_BASE + 3 == ZFS_IOC_EVENTS_SEEK); + CHECK(ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_DISCARD_CHECKPOINT); + CHECK(ZFS_IOC_PLATFORM_BASE + 1 == ZFS_IOC_EVENTS_NEXT); + CHECK(ZFS_IOC_PLATFORM_BASE + 2 == ZFS_IOC_EVENTS_CLEAR); + CHECK(ZFS_IOC_PLATFORM_BASE + 3 == ZFS_IOC_EVENTS_SEEK); #else - ZFS_IOC_BASE + 77 == ZFS_IOC_POOL_DISCARD_CHECKPOINT && - ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_INITIALIZE && - ZFS_IOC_BASE + 79 == ZFS_IOC_POOL_TRIM && - ZFS_IOC_BASE + 80 == ZFS_IOC_REDACT && - ZFS_IOC_BASE + 81 == ZFS_IOC_GET_BOOKMARK_PROPS); + CHECK(ZFS_IOC_BASE + 77 == ZFS_IOC_POOL_DISCARD_CHECKPOINT); + CHECK(ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_INITIALIZE); + CHECK(ZFS_IOC_BASE + 79 == ZFS_IOC_POOL_TRIM); + CHECK(ZFS_IOC_BASE + 80 == ZFS_IOC_REDACT); + CHECK(ZFS_IOC_BASE + 81 == ZFS_IOC_GET_BOOKMARK_PROPS); #endif + CHECK(ZFS_IOC_PLATFORM_BASE + 7 == ZFS_IOC_SET_BOOTENV); + CHECK(ZFS_IOC_PLATFORM_BASE + 8 == ZFS_IOC_GET_BOOTENV); + +#undef CHECK + + return (result); } int diff --git a/usr/src/tools/scripts/Makefile b/usr/src/tools/scripts/Makefile index 85ed6fb789..d6cd4cbfda 100644 --- a/usr/src/tools/scripts/Makefile +++ b/usr/src/tools/scripts/Makefile @@ -24,6 +24,7 @@ # Copyright 2010, Richard Lowe # # Copyright 2020 Joyent, Inc. +# Copyright 2020 OmniOS Community Edition (OmniOSce) Association. SHELL=/usr/bin/ksh93 @@ -83,6 +84,7 @@ MAN1ONBLDFILES= \ cddlchk.1onbld \ checkpaths.1onbld \ check_rtime.1onbld \ + ctfconvert.1onbld \ cstyle.1onbld \ find_elf.1onbld \ flg.flp.1onbld \ diff --git a/usr/src/tools/scripts/ctfconvert.1onbld b/usr/src/tools/scripts/ctfconvert.1onbld new file mode 100644 index 0000000000..3a75cc0b16 --- /dev/null +++ b/usr/src/tools/scripts/ctfconvert.1onbld @@ -0,0 +1,116 @@ +.\" +.\" This file and its contents are supplied under the terms of the +.\" Common Development and Distribution License ("CDDL"), version 1.0. +.\" You may only use this file in accordance with the terms of version +.\" 1.0 of the CDDL. +.\" +.\" A full copy of the text of the CDDL should have accompanied this +.\" source. A copy of the CDDL is also available via the Internet at +.\" http://www.illumos.org/license/CDDL. +.\" +.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association. +.\" +.Dd October 23, 2020 +.Dt ctfconvert 1ONBLD +.Os +.Sh NAME +.Nm ctfconvert +.Nd Convert ELF object debug data to CTF container +.Sh SYNOPSIS +.Nm +.Op Fl ikm +.Op Fl b Ar batchsize +.Op Fl j Ar threads +.Op Fl l Ar label | Fl L Ar labelenv +.Op Fl o Ar outfile +.Ar ELF_object +.Sh DESCRIPTION +The +.Nm +utility converts debug information found within +.Ar ELF_object +to CTF data and adds this to a CTF container. +.Ar ELF_object +can be a single object file or a fully linked object such as a shared library +or binary. +.Nm +currently supports input debug data in DWARFv2 or DWARFv4 format. +Unless the +.Fl o +option is present, this is done in-place; the original file being modified +with a new CTF container being added. +For in-place processing, unless the +.Fl k +option is present, the source file will be removed if an error occurs. +.Sh OPTIONS +The following options are supported: +.Bl -tag -width Ar +.It Fl i +Ignore files not built partially from C sources. +.Nm +will usually check +.Ar ELF_object +to see if at least one of the input files was a +.Sq .c +file, and exit with an error if not. +The +.Fl i +option overrides this check and allows processing to continue. +.It Fl k +When processing a file in-place and an error occurs, keep the input file +rather than deleting it. +.It Fl m +Allow +.Ar ELF_object +to have missing debug data. +By default, +.Nm +requires that each C source compilation unit in +.Ar ELF_object +contains debug data, and will exit with an error if this is not the case. +The +.Fl m +option relaxes this restriction allowing processing of such files. +Note that if the file contains no debug data in any of the compilation units +then this flag will cause +.Nm +to exit successfully without taking any action, and can mask missing CTF data. +.It Fl b Ar batchsize +Batch-process this many compilation units from the source file at once (default +256). This helps to reduce memory usage when processing large objects which +were built from many source files. +.It Fl j Ar threads +Use this many threads to perform the merge (default 4). +.It Fl l Ar label +Set the output container's label to the specified value. +.It Fl L Ar labelenv +Set the output container's label to the value of the specified environment +variable. +.It Fl o Ar outfile +Write the new object with added CTF ta to the specified output file, rather +than updating the input in-place. +.El +.Sh OPERANDS +The following operands are supported: +.Bl -tag -width Ar +.It Ar object_file +The source object file to process. +.El +.Sh EXIT STATUS +.Bl -inset +.It Sy 0 +.Dl Execution completed successfully. +.It Sy 1 +.Dl A fatal error occurred. +.It Sy 2 +.Dl Invalid command line options were specified. +.El +.Sh INTERFACE STABILITY +The command line interface of +.Nm +is +.Sy Uncommitted . +.Sh SEE ALSO +.Xr ctfdiff 1 , +.Xr ctfdump 1 , +.Xr ctf 4 diff --git a/usr/src/uts/common/fs/zfs/sys/vdev.h b/usr/src/uts/common/fs/zfs/sys/vdev.h index b8c2ee5c9e..b839ed2359 100644 --- a/usr/src/uts/common/fs/zfs/sys/vdev.h +++ b/usr/src/uts/common/fs/zfs/sys/vdev.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2017 by Delphix. All rights reserved. + * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright (c) 2017, Intel Corporation. * Copyright (c) 2019, Datto Inc. All rights reserved. */ @@ -180,6 +180,8 @@ extern void vdev_uberblock_load(vdev_t *, struct uberblock *, nvlist_t **); extern void vdev_config_generate_stats(vdev_t *vd, nvlist_t *nv); extern void vdev_label_write(zio_t *zio, vdev_t *vd, int l, abd_t *buf, uint64_t offset, uint64_t size, zio_done_func_t *done, void *private, int flags); +extern int vdev_label_read_bootenv(vdev_t *, nvlist_t *); +extern int vdev_label_write_bootenv(vdev_t *, nvlist_t *); typedef enum { VDEV_LABEL_CREATE, /* create/add a new device */ diff --git a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h index fc0f90c8c9..4e42247345 100644 --- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h +++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h @@ -20,8 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2018 by Delphix. All rights reserved. - * Copyright (c) 2011, 2019 by Delphix. All rights reserved. + * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright (c) 2017, Intel Corporation. * Copyright 2019 Joyent, Inc. * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> @@ -412,7 +411,7 @@ struct vdev { #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) @@ -439,9 +438,38 @@ 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. + * It is used by the GRUB bootloader used on Linux to store the + * contents of the grubenv file. The file is stored as raw ASCII, + * and is protected by an embedded checksum. By default, GRUB will + * check if the boot filesystem supports storing the environment data + * in a special location, and if so, will invoke filesystem specific + * logic to retrieve it. This can be overriden by a variable, should + * the user so desire. + */ + 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 */ diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_bootenv.h b/usr/src/uts/common/fs/zfs/sys/zfs_bootenv.h new file mode 100644 index 0000000000..703a1c8fa6 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/zfs_bootenv.h @@ -0,0 +1,52 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Toomas Soome <tsoome@me.com> + */ + +#ifndef _ZFS_BOOTENV_H +#define _ZFS_BOOTENV_H + +/* + * Define macros for label bootenv nvlist pair keys. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define BOOTENV_VERSION "version" + +#define BE_ILLUMOS_VENDOR "illumos" +#define BE_FREEBSD_VENDOR "freebsd" +#define BE_GRUB_VENDOR "grub" + +#define BOOTENV_OS BE_ILLUMOS_VENDOR + +#define GRUB_ENVMAP BE_GRUB_VENDOR ":" "envmap" + +#define FREEBSD_BOOTONCE BE_FREEBSD_VENDOR ":" "bootonce" +#define FREEBSD_BOOTONCE_USED BE_FREEBSD_VENDOR ":" "bootonce-used" +#define ILLUMOS_BOOTONCE BE_ILLUMOS_VENDOR ":" "bootonce" +#define ILLUMOS_BOOTONCE_USED BE_ILLUMOS_VENDOR ":" "bootonce-used" +#define FREEBSD_NVSTORE BE_FREEBSD_VENDOR ":" "nvstore" +#define ILLUMOS_NVSTORE BE_ILLUMOS_VENDOR ":" "nvstore" + +#define OS_BOOTONCE BOOTENV_OS ":" "bootonce" +#define OS_BOOTONCE_USED BOOTENV_OS ":" "bootonce-used" +#define OS_NVSTORE BOOTENV_OS ":" "nvstore" + +#ifdef __cplusplus +} +#endif + +#endif /* _ZFS_BOOTENV_H */ diff --git a/usr/src/uts/common/fs/zfs/vdev.c b/usr/src/uts/common/fs/zfs/vdev.c index 37c93f60cc..99ad0cd27d 100644 --- a/usr/src/uts/common/fs/zfs/vdev.c +++ b/usr/src/uts/common/fs/zfs/vdev.c @@ -1469,7 +1469,7 @@ vdev_probe(vdev_t *vd, zio_t *zio) for (int l = 1; l < VDEV_LABELS; l++) { zio_nowait(zio_read_phys(pio, vd, vdev_label_offset(vd->vdev_psize, l, - offsetof(vdev_label_t, vl_pad2)), VDEV_PAD_SIZE, + offsetof(vdev_label_t, vl_be)), VDEV_PAD_SIZE, abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE), ZIO_CHECKSUM_OFF, vdev_probe_done, vps, ZIO_PRIORITY_SYNC_READ, vps->vps_flags, B_TRUE)); diff --git a/usr/src/uts/common/fs/zfs/vdev_label.c b/usr/src/uts/common/fs/zfs/vdev_label.c index 6235b06f17..b683c3694b 100644 --- a/usr/src/uts/common/fs/zfs/vdev_label.c +++ b/usr/src/uts/common/fs/zfs/vdev_label.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2018 by Delphix. All rights reserved. + * Copyright (c) 2012, 2020 by Delphix. All rights reserved. * Copyright (c) 2017, Intel Corporation. * Copyright 2020 Joyent, Inc. */ @@ -150,6 +150,8 @@ #include <sys/dsl_scan.h> #include <sys/abd.h> #include <sys/fs/zfs.h> +#include <sys/byteorder.h> +#include <sys/zfs_bootenv.h> /* * Basic routines to read and write from a vdev label. @@ -940,7 +942,7 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason) nvlist_t *label; vdev_phys_t *vp; abd_t *vp_abd; - abd_t *pad2; + abd_t *bootenv; uberblock_t *ub; abd_t *ub_abd; zio_t *zio; @@ -1101,8 +1103,8 @@ vdev_label_init(vdev_t *vd, uint64_t crtxg, vdev_labeltype_t reason) ub->ub_txg = 0; /* Initialize the 2nd padding area. */ - pad2 = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); - abd_zero(pad2, VDEV_PAD_SIZE); + bootenv = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); + abd_zero(bootenv, VDEV_PAD_SIZE); /* * Write everything in parallel. @@ -1121,8 +1123,8 @@ retry: * Zero out the 2nd padding area where it might have * left over data from previous filesystem format. */ - vdev_label_write(zio, vd, l, pad2, - offsetof(vdev_label_t, vl_pad2), + vdev_label_write(zio, vd, l, bootenv, + offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE, NULL, NULL, flags); vdev_label_write(zio, vd, l, ub_abd, @@ -1138,7 +1140,7 @@ retry: } nvlist_free(label); - abd_free(pad2); + abd_free(bootenv); abd_free(ub_abd); abd_free(vp_abd); @@ -1162,6 +1164,212 @@ retry: } /* + * Done callback for vdev_label_read_bootenv_impl. If this is the first + * callback to finish, store our abd in the callback pointer. Otherwise, we + * just free our abd and return. + */ +static void +vdev_label_read_bootenv_done(zio_t *zio) +{ + zio_t *rio = zio->io_private; + abd_t **cbp = rio->io_private; + + ASSERT3U(zio->io_size, ==, VDEV_PAD_SIZE); + + if (zio->io_error == 0) { + mutex_enter(&rio->io_lock); + if (*cbp == NULL) { + /* Will free this buffer in vdev_label_read_bootenv. */ + *cbp = zio->io_abd; + } else { + abd_free(zio->io_abd); + } + mutex_exit(&rio->io_lock); + } else { + abd_free(zio->io_abd); + } +} + +static void +vdev_label_read_bootenv_impl(zio_t *zio, vdev_t *vd, int flags) +{ + for (int c = 0; c < vd->vdev_children; c++) + vdev_label_read_bootenv_impl(zio, vd->vdev_child[c], flags); + + /* + * We just use the first label that has a correct checksum; the + * bootloader should have rewritten them all to be the same on boot, + * and any changes we made since boot have been the same across all + * labels. + */ + if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) { + for (int l = 0; l < VDEV_LABELS; l++) { + vdev_label_read(zio, vd, l, + abd_alloc_linear(VDEV_PAD_SIZE, B_FALSE), + offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE, + vdev_label_read_bootenv_done, zio, flags); + } + } +} + +int +vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *bootenv) +{ + nvlist_t *config; + spa_t *spa = rvd->vdev_spa; + abd_t *abd = NULL; + int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL | + ZIO_FLAG_SPECULATIVE | ZIO_FLAG_TRYHARD; + + ASSERT(bootenv); + ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); + + zio_t *zio = zio_root(spa, NULL, &abd, flags); + vdev_label_read_bootenv_impl(zio, rvd, flags); + int err = zio_wait(zio); + + if (abd != NULL) { + char *buf; + vdev_boot_envblock_t *vbe = abd_to_buf(abd); + + vbe->vbe_version = ntohll(vbe->vbe_version); + switch (vbe->vbe_version) { + case VB_RAW: + /* + * if we have textual data in vbe_bootenv, create nvlist + * with key "envmap". + */ + fnvlist_add_uint64(bootenv, BOOTENV_VERSION, VB_RAW); + vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0'; + fnvlist_add_string(bootenv, GRUB_ENVMAP, + vbe->vbe_bootenv); + break; + + case VB_NVLIST: + err = nvlist_unpack(vbe->vbe_bootenv, + sizeof (vbe->vbe_bootenv), &config, 0); + if (err == 0) { + fnvlist_merge(bootenv, config); + nvlist_free(config); + break; + } + /* FALLTHROUGH */ + default: + /* Check for FreeBSD zfs bootonce command string */ + buf = abd_to_buf(abd); + if (*buf == '\0') { + fnvlist_add_uint64(bootenv, BOOTENV_VERSION, + VB_NVLIST); + break; + } + fnvlist_add_string(bootenv, FREEBSD_BOOTONCE, buf); + } + + /* + * abd was allocated in vdev_label_read_bootenv_impl() + */ + abd_free(abd); + /* + * If we managed to read any successfully, + * return success. + */ + return (0); + } + return (err); +} + +int +vdev_label_write_bootenv(vdev_t *vd, nvlist_t *env) +{ + zio_t *zio; + spa_t *spa = vd->vdev_spa; + vdev_boot_envblock_t *bootenv; + int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL; + int error; + size_t nvsize; + char *nvbuf; + + error = nvlist_size(env, &nvsize, NV_ENCODE_XDR); + if (error != 0) + return (SET_ERROR(error)); + + if (nvsize >= sizeof (bootenv->vbe_bootenv)) { + return (SET_ERROR(E2BIG)); + } + + ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); + + error = ENXIO; + for (int c = 0; c < vd->vdev_children; c++) { + int child_err; + + child_err = vdev_label_write_bootenv(vd->vdev_child[c], env); + /* + * As long as any of the disks managed to write all of their + * labels successfully, return success. + */ + if (child_err == 0) + error = child_err; + } + + if (!vd->vdev_ops->vdev_op_leaf || vdev_is_dead(vd) || + !vdev_writeable(vd)) { + return (error); + } + ASSERT3U(sizeof (*bootenv), ==, VDEV_PAD_SIZE); + abd_t *abd = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE); + abd_zero(abd, VDEV_PAD_SIZE); + + bootenv = abd_borrow_buf_copy(abd, VDEV_PAD_SIZE); + nvbuf = bootenv->vbe_bootenv; + nvsize = sizeof (bootenv->vbe_bootenv); + + bootenv->vbe_version = fnvlist_lookup_uint64(env, BOOTENV_VERSION); + switch (bootenv->vbe_version) { + case VB_RAW: + if (nvlist_lookup_string(env, GRUB_ENVMAP, &nvbuf) == 0) { + (void) strlcpy(bootenv->vbe_bootenv, nvbuf, nvsize); + } + error = 0; + break; + + case VB_NVLIST: + error = nvlist_pack(env, &nvbuf, &nvsize, NV_ENCODE_XDR, + KM_SLEEP); + break; + + default: + error = EINVAL; + break; + } + + if (error == 0) { + bootenv->vbe_version = htonll(bootenv->vbe_version); + abd_return_buf_copy(abd, bootenv, VDEV_PAD_SIZE); + } else { + abd_free(abd); + return (SET_ERROR(error)); + } + +retry: + zio = zio_root(spa, NULL, NULL, flags); + for (int l = 0; l < VDEV_LABELS; l++) { + vdev_label_write(zio, vd, l, abd, + offsetof(vdev_label_t, vl_be), + VDEV_PAD_SIZE, NULL, NULL, flags); + } + + error = zio_wait(zio); + if (error != 0 && !(flags & ZIO_FLAG_TRYHARD)) { + flags |= ZIO_FLAG_TRYHARD; + goto retry; + } + + abd_free(abd); + return (error); +} + +/* * ========================================================================== * uberblock load/sync * ========================================================================== diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index 76c7170d38..77d9b48982 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -3608,6 +3608,58 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) } /* + * This ioctl is used to set the bootenv configuration on the current + * pool. This configuration is stored in the second padding area of the label, + * and it is used by the bootloader(s) to store bootloader and/or system + * specific data. + * The data is stored as nvlist data stream, and is protected by + * an embedded checksum. + * The version can have two possible values: + * VB_RAW: nvlist should have key GRUB_ENVMAP, value DATA_TYPE_STRING. + * VB_NVLIST: nvlist with arbitrary <key, value> pairs. + */ +static const zfs_ioc_key_t zfs_keys_set_bootenv[] = { + {"version", DATA_TYPE_UINT64, 0}, + {"<keys>", DATA_TYPE_ANY, ZK_OPTIONAL | ZK_WILDCARDLIST}, +}; + +static int +zfs_ioc_set_bootenv(const char *name, nvlist_t *innvl, + nvlist_t *outnvl __unused) +{ + int error; + spa_t *spa; + + if ((error = spa_open(name, &spa, FTAG)) != 0) + return (error); + spa_vdev_state_enter(spa, SCL_ALL); + error = vdev_label_write_bootenv(spa->spa_root_vdev, innvl); + (void) spa_vdev_state_exit(spa, NULL, 0); + spa_close(spa, FTAG); + return (error); +} + +static const zfs_ioc_key_t zfs_keys_get_bootenv[] = { + /* no nvl keys */ +}; + +static int +zfs_ioc_get_bootenv(const char *name, nvlist_t *innvl __unused, + nvlist_t *outnvl) +{ + spa_t *spa; + int error; + + if ((error = spa_open(name, &spa, FTAG)) != 0) + return (error); + spa_vdev_state_enter(spa, SCL_ALL); + error = vdev_label_read_bootenv(spa->spa_root_vdev, outnvl); + (void) spa_vdev_state_exit(spa, NULL, 0); + spa_close(spa, FTAG); + return (error); +} + +/* * The dp_config_rwlock must not be held when calling this, because the * unmount may need to write out data. * @@ -6813,6 +6865,16 @@ zfs_ioctl_init(void) POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, zfs_keys_pool_trim, ARRAY_SIZE(zfs_keys_pool_trim)); + zfs_ioctl_register("set_bootenv", ZFS_IOC_SET_BOOTENV, + zfs_ioc_set_bootenv, zfs_secpolicy_config, POOL_NAME, + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE, + zfs_keys_set_bootenv, ARRAY_SIZE(zfs_keys_set_bootenv)); + + zfs_ioctl_register("get_bootenv", ZFS_IOC_GET_BOOTENV, + zfs_ioc_get_bootenv, zfs_secpolicy_none, POOL_NAME, + POOL_CHECK_SUSPENDED, B_FALSE, B_TRUE, + zfs_keys_get_bootenv, ARRAY_SIZE(zfs_keys_get_bootenv)); + /* IOCTLS that use the legacy function signature */ zfs_ioctl_register_legacy("pool_freeze", ZFS_IOC_POOL_FREEZE, diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h index 819905a8d9..73050319b9 100644 --- a/usr/src/uts/common/sys/fs/zfs.h +++ b/usr/src/uts/common/sys/fs/zfs.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2018 by Delphix. All rights reserved. + * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2020 Joyent, Inc. @@ -1173,7 +1173,14 @@ typedef struct ddt_histogram { * /dev/zfs ioctl numbers. */ typedef enum zfs_ioc { + /* + * Core features - 81/128 numbers reserved. + */ +#ifdef __FreeBSD__ + ZFS_IOC_FIRST = 0, +#else ZFS_IOC_FIRST = ('Z' << 8), +#endif ZFS_IOC = ZFS_IOC_FIRST, ZFS_IOC_POOL_CREATE = ZFS_IOC_FIRST, /* 0x5a00 */ ZFS_IOC_POOL_DESTROY, /* 0x5a01 */ @@ -1257,6 +1264,19 @@ typedef enum zfs_ioc { ZFS_IOC_POOL_TRIM, /* 0x5a50 */ ZFS_IOC_REDACT, /* 0x5a51 */ ZFS_IOC_GET_BOOKMARK_PROPS, /* 0x5a52 */ + + /* + * Per-platform (Optional) - 8/128 numbers reserved. + */ + ZFS_IOC_PLATFORM = ZFS_IOC_FIRST + 0x80, + ZFS_IOC_EVENTS_NEXT, /* 0x81 (Linux) */ + ZFS_IOC_EVENTS_CLEAR, /* 0x82 (Linux) */ + ZFS_IOC_EVENTS_SEEK, /* 0x83 (Linux) */ + ZFS_IOC_NEXTBOOT, /* 0x84 (FreeBSD) */ + ZFS_IOC_JAIL, /* 0x85 (FreeBSD) */ + ZFS_IOC_UNJAIL, /* 0x86 (FreeBSD) */ + ZFS_IOC_SET_BOOTENV, /* 0x87 */ + ZFS_IOC_GET_BOOTENV, /* 0x88 */ ZFS_IOC_LAST } zfs_ioc_t; diff --git a/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c b/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c index e49f583772..61a50b418c 100644 --- a/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c +++ b/usr/src/uts/i86pc/io/vmm/io/vpmtmr.c @@ -78,6 +78,7 @@ vpmtmr_init(struct vm *vm) struct bintime bt; vpmtmr = malloc(sizeof(struct vpmtmr), M_VPMTMR, M_WAITOK | M_ZERO); + vpmtmr->vm = vm; vpmtmr->baseuptime = sbinuptime(); vpmtmr->baseval = 0; @@ -87,9 +88,35 @@ vpmtmr_init(struct vm *vm) return (vpmtmr); } +static int +vpmtmr_detach_ioport(struct vpmtmr *vpmtmr) +{ + if (vpmtmr->io_cookie != NULL) { + ioport_handler_t old_func; + void *old_arg; + int err; + + err = vm_ioport_detach(vpmtmr->vm, &vpmtmr->io_cookie, + &old_func, &old_arg); + if (err != 0) { + return (err); + } + + ASSERT3P(old_func, ==, vpmtmr_handler); + ASSERT3P(old_arg, ==, vpmtmr); + ASSERT3P(vpmtmr->io_cookie, ==, NULL); + vpmtmr->io_port = 0; + } + return (0); +} + void vpmtmr_cleanup(struct vpmtmr *vpmtmr) { + int err; + + err = vpmtmr_detach_ioport(vpmtmr); + VERIFY3P(err, ==, 0); free(vpmtmr, M_VPMTMR); } @@ -101,23 +128,13 @@ vpmtmr_set_location(struct vm *vm, uint16_t ioport) int err; if (vpmtmr->io_cookie != NULL) { - ioport_handler_t old_func; - void *old_arg; - if (vpmtmr->io_port == ioport) { /* already attached in the right place */ return (0); } - err = vm_ioport_detach(vm, &vpmtmr->io_cookie, &old_func, - &old_arg); - if (err != 0) { - return (err); - } - - ASSERT3P(old_func, ==, vpmtmr_handler); - ASSERT3P(old_arg, ==, vpmtmr); - vpmtmr->io_port = 0; + err = vpmtmr_detach_ioport(vpmtmr); + VERIFY3P(err, ==, 0); } err = vm_ioport_attach(vm, ioport, vpmtmr_handler, vpmtmr, &vpmtmr->io_cookie); diff --git a/usr/src/uts/i86pc/io/vmm/vmm.c b/usr/src/uts/i86pc/io/vmm/vmm.c index 23a4fecf7b..1821a96fd7 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm.c +++ b/usr/src/uts/i86pc/io/vmm/vmm.c @@ -501,9 +501,7 @@ vm_init(struct vm *vm, bool create) if (create) vm->vrtc = vrtc_init(vm); - if (create) { - vm_inout_init(vm, &vm->ioports); - } + vm_inout_init(vm, &vm->ioports); CPU_ZERO(&vm->active_cpus); CPU_ZERO(&vm->debug_cpus); @@ -606,15 +604,19 @@ vm_cleanup(struct vm *vm, bool destroy) if (vm->iommu != NULL) iommu_destroy_domain(vm->iommu); - if (destroy) { - vm_inout_cleanup(vm, &vm->ioports); - } + /* + * Devices which attach their own ioport hooks should be cleaned up + * first so they can tear down those registrations. + */ + vpmtmr_cleanup(vm->vpmtmr); + + vm_inout_cleanup(vm, &vm->ioports); if (destroy) vrtc_cleanup(vm->vrtc); else vrtc_reset(vm->vrtc); - vpmtmr_cleanup(vm->vpmtmr); + vatpit_cleanup(vm->vatpit); vhpet_cleanup(vm->vhpet); vatpic_cleanup(vm->vatpic); diff --git a/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c b/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c index 0ac5e21fd0..31f6ea75b5 100644 --- a/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c +++ b/usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c @@ -993,11 +993,19 @@ vie_emulate_movs(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa) */ error = vie_mmio_read(vie, vm, vcpuid, gpa, &val, opsize); - if (error) - goto done; - vm_copyout(vm, vcpuid, &val, copyinfo, opsize); - vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo)); + if (error == 0) { + vm_copyout(vm, vcpuid, &val, copyinfo, opsize); + } + /* + * Regardless of whether the MMIO read was successful or + * not, the copy resources must be cleaned up. + */ + vm_copy_teardown(vm, vcpuid, copyinfo, + nitems(copyinfo)); + if (error != 0) { + goto done; + } } else { /* * Case (4): read from and write to mmio. @@ -2022,7 +2030,8 @@ vie_emulate_mmio(struct vie *vie, struct vm *vm, int vcpuid) } static int -vie_emulate_inout_port(struct vie *vie, struct vm *vm, int vcpuid) +vie_emulate_inout_port(struct vie *vie, struct vm *vm, int vcpuid, + uint32_t *eax) { uint32_t mask, val; bool in; @@ -2032,18 +2041,19 @@ vie_emulate_inout_port(struct vie *vie, struct vm *vm, int vcpuid) in = (vie->inout.flags & INOUT_IN) != 0; if (!in) { - val = vie->inout.eax & mask; + val = *eax & mask; } if (vie->inout_req_state != VR_DONE) { err = vm_ioport_access(vm, vcpuid, in, vie->inout.port, vie->inout.bytes, &val); + val &= mask; } else { /* * This port access was handled in userspace and the result was * injected in to be handled now. */ - val = vie->inout_req_val; + val = vie->inout_req_val & mask; vie->inout_req_state = VR_NONE; err = 0; } @@ -2057,11 +2067,7 @@ vie_emulate_inout_port(struct vie *vie, struct vm *vm, int vcpuid) } if (in) { - val &= mask; - val |= (vie->inout.eax & ~mask); - err = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, val); - KASSERT(err == 0, ("emulate_ioport: error %d setting guest " - "rax register", err)); + *eax = (*eax & ~mask) | val; } return (0); } @@ -2156,7 +2162,7 @@ vie_emulate_inout_str(struct vie *vie, struct vm *vm, int vcpuid) vm_copyin(vm, vcpuid, copyinfo, &vie->inout.eax, bytes); } - err = vie_emulate_inout_port(vie, vm, vcpuid); + err = vie_emulate_inout_port(vie, vm, vcpuid, &vie->inout.eax); if (err == 0 && in) { vm_copyout(vm, vcpuid, &vie->inout.eax, copyinfo, bytes); @@ -2217,7 +2223,16 @@ vie_emulate_inout(struct vie *vie, struct vm *vm, int vcpuid) return (EINVAL); } - err = vie_emulate_inout_port(vie, vm, vcpuid); + err = vie_emulate_inout_port(vie, vm, vcpuid, &vie->inout.eax); + if (err == 0 && (vie->inout.flags & INOUT_IN) != 0) { + /* + * With the inX access now a success, the result needs + * to be stored in the guest %rax. + */ + err = vm_set_register(vm, vcpuid, VM_REG_GUEST_RAX, + vie->inout.eax); + VERIFY0(err); + } } else { vie->status &= ~VIES_REPEAT; err = vie_emulate_inout_str(vie, vm, vcpuid); |