summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/Makefile5
-rw-r--r--usr/src/lib/libbe/Makefile.com2
-rw-r--r--usr/src/lib/libbe/common/be_activate.c54
-rw-r--r--usr/src/lib/libbe/common/be_list.c22
-rw-r--r--usr/src/lib/libbe/common/libbe.h2
-rw-r--r--usr/src/lib/libbe/common/libbe_priv.h8
-rw-r--r--usr/src/lib/libc/port/threads/pthr_attr.c10
-rw-r--r--usr/src/lib/libc/port/threads/pthread.c4
-rw-r--r--usr/src/lib/libctf/common/ctf_convert.c9
-rw-r--r--usr/src/lib/libctf/common/ctf_dwarf.c623
-rw-r--r--usr/src/lib/libctf/common/ctf_merge.c70
-rw-r--r--usr/src/lib/libctf/common/libctf_impl.h4
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h2
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c36
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers2
-rw-r--r--usr/src/lib/libzfs_core/common/libzfs_core.c20
-rw-r--r--usr/src/lib/libzfs_core/common/libzfs_core.h4
-rw-r--r--usr/src/lib/libzfs_core/common/mapfile-vers7
-rw-r--r--usr/src/lib/libzfsbootenv/Makefile53
-rw-r--r--usr/src/lib/libzfsbootenv/Makefile.com42
-rw-r--r--usr/src/lib/libzfsbootenv/amd64/Makefile19
-rw-r--r--usr/src/lib/libzfsbootenv/common/libzfsbootenv.h41
-rw-r--r--usr/src/lib/libzfsbootenv/common/lzbe_device.c164
-rw-r--r--usr/src/lib/libzfsbootenv/common/lzbe_pair.c348
-rw-r--r--usr/src/lib/libzfsbootenv/common/lzbe_util.c39
-rw-r--r--usr/src/lib/libzfsbootenv/common/mapfile-vers44
-rw-r--r--usr/src/lib/libzfsbootenv/i386/Makefile18
-rw-r--r--usr/src/lib/libzfsbootenv/sparc/Makefile18
-rw-r--r--usr/src/lib/libzfsbootenv/sparcv9/Makefile19
29 files changed, 1420 insertions, 269 deletions
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)