]> git.proxmox.com Git - mirror_zfs.git/blobdiff - lib/libzfs/libzfs_pool.c
OpenZFS 6876 - Stack corruption after importing a pool with a too-long name
[mirror_zfs.git] / lib / libzfs / libzfs_pool.c
index 1b8f3b63a82a0822f8a037a0810fa57c03b40129..2484ddc12a58b131855227bf77c45ea99080ada5 100644 (file)
@@ -20,9 +20,9 @@
  */
 
 /*
+ * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2014 by Delphix. All rights reserved.
  */
 
 #include <ctype.h>
@@ -235,22 +235,12 @@ zpool_pool_state_to_name(pool_state_t state)
        return (gettext("UNKNOWN"));
 }
 
-/*
- * API compatibility wrapper around zpool_get_prop_literal
- */
-int
-zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
-    zprop_source_t *srctype)
-{
-       return (zpool_get_prop_literal(zhp, prop, buf, len, srctype, B_FALSE));
-}
-
 /*
  * Get a zpool property value for 'prop' and return the value in
  * a pre-allocated buffer.
  */
 int
-zpool_get_prop_literal(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
+zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
     size_t len, zprop_source_t *srctype, boolean_t literal)
 {
        uint64_t intval;
@@ -283,9 +273,7 @@ zpool_get_prop_literal(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
                                (void) strlcpy(buf,
                                    zpool_get_prop_string(zhp, prop, &src),
                                    len);
-                               if (srctype != NULL)
-                                       *srctype = src;
-                               return (0);
+                               break;
                        }
                        /* FALLTHROUGH */
                default:
@@ -316,7 +304,7 @@ zpool_get_prop_literal(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
                case ZPOOL_PROP_ALLOCATED:
                case ZPOOL_PROP_FREE:
                case ZPOOL_PROP_FREEING:
-               case ZPOOL_PROP_EXPANDSZ:
+               case ZPOOL_PROP_LEAKED:
                case ZPOOL_PROP_ASHIFT:
                        if (literal)
                                (void) snprintf(buf, len, "%llu",
@@ -325,15 +313,48 @@ zpool_get_prop_literal(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
                                (void) zfs_nicenum(intval, buf, len);
                        break;
 
+               case ZPOOL_PROP_EXPANDSZ:
+                       if (intval == 0) {
+                               (void) strlcpy(buf, "-", len);
+                       } else if (literal) {
+                               (void) snprintf(buf, len, "%llu",
+                                   (u_longlong_t)intval);
+                       } else {
+                               (void) zfs_nicenum(intval, buf, len);
+                       }
+                       break;
+
                case ZPOOL_PROP_CAPACITY:
-                       (void) snprintf(buf, len, "%llu%%",
-                           (u_longlong_t)intval);
+                       if (literal) {
+                               (void) snprintf(buf, len, "%llu",
+                                   (u_longlong_t)intval);
+                       } else {
+                               (void) snprintf(buf, len, "%llu%%",
+                                   (u_longlong_t)intval);
+                       }
+                       break;
+
+               case ZPOOL_PROP_FRAGMENTATION:
+                       if (intval == UINT64_MAX) {
+                               (void) strlcpy(buf, "-", len);
+                       } else if (literal) {
+                               (void) snprintf(buf, len, "%llu",
+                                   (u_longlong_t)intval);
+                       } else {
+                               (void) snprintf(buf, len, "%llu%%",
+                                   (u_longlong_t)intval);
+                       }
                        break;
 
                case ZPOOL_PROP_DEDUPRATIO:
-                       (void) snprintf(buf, len, "%llu.%02llux",
-                           (u_longlong_t)(intval / 100),
-                           (u_longlong_t)(intval % 100));
+                       if (literal)
+                               (void) snprintf(buf, len, "%llu.%02llu",
+                                   (u_longlong_t)(intval / 100),
+                                   (u_longlong_t)(intval % 100));
+                       else
+                               (void) snprintf(buf, len, "%llu.%02llux",
+                                   (u_longlong_t)(intval / 100),
+                                   (u_longlong_t)(intval % 100));
                        break;
 
                case ZPOOL_PROP_HEALTH:
@@ -420,10 +441,10 @@ pool_uses_efi(nvlist_t *config)
 boolean_t
 zpool_is_bootable(zpool_handle_t *zhp)
 {
-       char bootfs[ZPOOL_MAXNAMELEN];
+       char bootfs[ZFS_MAX_DATASET_NAME_LEN];
 
        return (zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, bootfs,
-           sizeof (bootfs), NULL) == 0 && strncmp(bootfs, "-",
+           sizeof (bootfs), NULL, B_FALSE) == 0 && strncmp(bootfs, "-",
            sizeof (bootfs)) != 0);
 }
 
@@ -459,10 +480,9 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                prop = zpool_name_to_prop(propname);
                if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
                        int err;
-                       zfeature_info_t *feature;
                        char *fname = strchr(propname, '@') + 1;
 
-                       err = zfeature_lookup_name(fname, &feature);
+                       err = zfeature_lookup_name(fname, NULL);
                        if (err != 0) {
                                ASSERT3U(err, ==, ENOENT);
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -691,6 +711,15 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                                goto error;
                        }
                        break;
+               case ZPOOL_PROP_TNAME:
+                       if (!flags.create) {
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "property '%s' can only be set at "
+                                   "creation time"), propname);
+                               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                               goto error;
+                       }
+                       break;
                }
        }
 
@@ -845,7 +874,7 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
 
                if (entry->pl_prop != ZPROP_INVAL &&
                    zpool_get_prop(zhp, entry->pl_prop, buf, sizeof (buf),
-                   NULL) == 0) {
+                   NULL, B_FALSE) == 0) {
                        if (strlen(buf) > entry->pl_width)
                                entry->pl_width = strlen(buf);
                }
@@ -877,14 +906,14 @@ zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf,
         */
        if (supported) {
                int ret;
-               zfeature_info_t *fi;
+               spa_feature_t fid;
 
-               ret = zfeature_lookup_name(feature, &fi);
+               ret = zfeature_lookup_name(feature, &fid);
                if (ret != 0) {
                        (void) strlcpy(buf, "-", len);
                        return (ENOTSUP);
                }
-               feature = fi->fi_guid;
+               feature = spa_feature_table[fid].fi_guid;
        }
 
        if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
@@ -1118,12 +1147,9 @@ zpool_open(libzfs_handle_t *hdl, const char *pool)
 void
 zpool_close(zpool_handle_t *zhp)
 {
-       if (zhp->zpool_config)
-               nvlist_free(zhp->zpool_config);
-       if (zhp->zpool_old_config)
-               nvlist_free(zhp->zpool_old_config);
-       if (zhp->zpool_props)
-               nvlist_free(zhp->zpool_props);
+       nvlist_free(zhp->zpool_config);
+       nvlist_free(zhp->zpool_old_config);
+       nvlist_free(zhp->zpool_props);
        free(zhp);
 }
 
@@ -1187,8 +1213,8 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
                    zfs_prop_to_name(ZFS_PROP_ZONED), &zonestr) == 0) &&
                    strcmp(zonestr, "on") == 0);
 
-               if ((zc_fsprops = zfs_valid_proplist(hdl,
-                   ZFS_TYPE_FILESYSTEM, fsprops, zoned, NULL, msg)) == NULL) {
+               if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM,
+                   fsprops, zoned, NULL, NULL, msg)) == NULL) {
                        goto create_failed;
                }
                if (!zc_props &&
@@ -1227,6 +1253,21 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
                            "lvm device"));
                        return (zfs_error(hdl, EZFS_BADDEV, msg));
 
+               case ERANGE:
+                       /*
+                        * This happens if the record size is smaller or larger
+                        * than the allowed size range, or not a power of 2.
+                        *
+                        * NOTE: although zfs_valid_proplist is called earlier,
+                        * this case may have slipped through since the
+                        * pool does not exist yet and it is therefore
+                        * impossible to read properties e.g. max blocksize
+                        * from the pool.
+                        */
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "record size invalid"));
+                       return (zfs_error(hdl, EZFS_BADPROP, msg));
+
                case EOVERFLOW:
                        /*
                         * This occurs when one of the devices is below
@@ -1350,8 +1391,7 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "device '%s' contains an EFI label and "
                                    "cannot be used on root pools."),
-                                   zpool_vdev_name(hdl, NULL, spares[s],
-                                   B_FALSE));
+                                   zpool_vdev_name(hdl, NULL, spares[s], 0));
                                return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg));
                        }
                }
@@ -1646,8 +1686,7 @@ zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
 
        ret = zpool_import_props(hdl, config, newname, props,
            ZFS_IMPORT_NORMAL);
-       if (props)
-               nvlist_free(props);
+       nvlist_free(props);
        return (ret);
 }
 
@@ -1672,7 +1711,7 @@ print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv,
                return;
 
        for (c = 0; c < children; c++) {
-               vname = zpool_vdev_name(hdl, NULL, child[c], B_TRUE);
+               vname = zpool_vdev_name(hdl, NULL, child[c], VDEV_NAME_TYPE_ID);
                print_vdev_tree(hdl, vname, child[c], indent + 2);
                free(vname);
        }
@@ -1740,7 +1779,7 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
                thename = origname;
        }
 
-       if (props) {
+       if (props != NULL) {
                uint64_t version;
                prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
 
@@ -1748,12 +1787,13 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
                    &version) == 0);
 
                if ((props = zpool_valid_proplist(hdl, origname,
-                   props, version, flags, errbuf)) == NULL) {
+                   props, version, flags, errbuf)) == NULL)
                        return (-1);
-               } else if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
+               if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
                        nvlist_free(props);
                        return (-1);
                }
+               nvlist_free(props);
        }
 
        (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
@@ -1762,11 +1802,11 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
            &zc.zc_guid) == 0);
 
        if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) {
-               nvlist_free(props);
+               zcmd_free_nvlists(&zc);
                return (-1);
        }
        if (zcmd_alloc_dst_nvlist(hdl, &zc, zc.zc_nvlist_conf_size * 2) != 0) {
-               nvlist_free(props);
+               zcmd_free_nvlists(&zc);
                return (-1);
        }
 
@@ -1782,6 +1822,9 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
                error = errno;
 
        (void) zcmd_read_dst_nvlist(hdl, &zc, &nv);
+
+       zcmd_free_nvlists(&zc);
+
        zpool_get_rewind_policy(config, &policy);
 
        if (error) {
@@ -1864,7 +1907,12 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
                            "one or more devices are already in use\n"));
                        (void) zfs_error(hdl, EZFS_BADDEV, desc);
                        break;
-
+               case ENAMETOOLONG:
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "new name of at least one dataset is longer than "
+                           "the maximum allowable length"));
+                       (void) zfs_error(hdl, EZFS_NAMETOOLONG, desc);
+                       break;
                default:
                        (void) zpool_standard_error(hdl, error, desc);
                        zpool_explain_recover(hdl,
@@ -1893,9 +1941,6 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
                return (0);
        }
 
-       zcmd_free_nvlists(&zc);
-       nvlist_free(props);
-
        return (ret);
 }
 
@@ -2167,7 +2212,7 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
 
        verify(nvlist_alloc(&search, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 
-       guid = strtoull(path, &end, 10);
+       guid = strtoull(path, &end, 0);
        if (guid != 0 && *end == '\0') {
                verify(nvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid) == 0);
        } else if (zpool_vdev_is_interior(path)) {
@@ -2660,7 +2705,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
        verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
            ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0);
 
-       if ((newname = zpool_vdev_name(NULL, NULL, child[0], B_FALSE)) == NULL)
+       if ((newname = zpool_vdev_name(NULL, NULL, child[0], 0)) == NULL)
                return (-1);
 
        /*
@@ -2753,10 +2798,11 @@ zpool_vdev_attach(zpool_handle_t *zhp,
 
        case EDOM:
                /*
-                * The new device has a different alignment requirement.
+                * The new device has a different optimal sector size.
                 */
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "devices have different sector alignment"));
+                   "new device has a different optimal sector size; use the "
+                   "option '-o ashift=N' to override the optimal size"));
                (void) zfs_error(hdl, EZFS_BADDEV, msg);
                break;
 
@@ -2850,11 +2896,11 @@ find_vdev_entry(zpool_handle_t *zhp, nvlist_t **mchild, uint_t mchildren,
        for (mc = 0; mc < mchildren; mc++) {
                uint_t sc;
                char *mpath = zpool_vdev_name(zhp->zpool_hdl, zhp,
-                   mchild[mc], B_FALSE);
+                   mchild[mc], 0);
 
                for (sc = 0; sc < schildren; sc++) {
                        char *spath = zpool_vdev_name(zhp->zpool_hdl, zhp,
-                           schild[sc], B_FALSE);
+                           schild[sc], 0);
                        boolean_t result = (strcmp(mpath, spath) == 0);
 
                        free(spath);
@@ -2915,8 +2961,7 @@ zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot,
            &children) != 0) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                    "Source pool is missing vdev tree"));
-               if (zc_props)
-                       nvlist_free(zc_props);
+               nvlist_free(zc_props);
                return (-1);
        }
 
@@ -3064,10 +3109,8 @@ out:
                free(varray);
        }
        zcmd_free_nvlists(&zc);
-       if (zc_props)
-               nvlist_free(zc_props);
-       if (newconfig)
-               nvlist_free(newconfig);
+       nvlist_free(zc_props);
+       nvlist_free(newconfig);
        if (freelist) {
                nvlist_free(*newroot);
                *newroot = NULL;
@@ -3268,6 +3311,7 @@ zpool_reopen(zpool_handle_t *zhp)
        return (zpool_standard_error(hdl, errno, msg));
 }
 
+#if defined(__sun__) || defined(__sun)
 /*
  * Convert from a devid string to a path.
  */
@@ -3291,8 +3335,10 @@ devid_to_path(char *devid_str)
        if (ret != 0)
                return (NULL);
 
-       if ((path = strdup(list[0].devname)) == NULL)
-               return (NULL);
+       /*
+        * In a case the strdup() fails, we will just return NULL below.
+        */
+       path = strdup(list[0].devname);
 
        devid_free_nmlist(list);
 
@@ -3343,13 +3389,14 @@ set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
 
        (void) ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SETPATH, &zc);
 }
+#endif /* sun */
 
 /*
  * Remove partition suffix from a vdev path.  Partition suffixes may take three
  * forms: "-partX", "pX", or "X", where X is a string of digits.  The second
  * case only occurs when the suffix is preceded by a digit, i.e. "md0p0" The
  * third case only occurs when preceded by a string matching the regular
- * expression "^[hs]d[a-z]+", i.e. a scsi or ide disk.
+ * expression "^([hsv]|xv)d[a-z]+", i.e. a scsi, ide, virtio or xen disk.
  */
 static char *
 strip_partition(libzfs_handle_t *hdl, char *path)
@@ -3362,8 +3409,11 @@ strip_partition(libzfs_handle_t *hdl, char *path)
        } else if ((part = strrchr(tmp, 'p')) &&
            part > tmp + 1 && isdigit(*(part-1))) {
                d = part + 1;
-       } else if ((tmp[0] == 'h' || tmp[0] == 's') && tmp[1] == 'd') {
+       } else if ((tmp[0] == 'h' || tmp[0] == 's' || tmp[0] == 'v') &&
+           tmp[1] == 'd') {
                for (d = &tmp[2]; isalpha(*d); part = ++d);
+       } else if (strncmp("xvd", tmp, 3) == 0) {
+               for (d = &tmp[3]; isalpha(*d); part = ++d);
        }
        if (part && d && *d != '\0') {
                for (; isdigit(*d); d++);
@@ -3392,23 +3442,43 @@ strip_partition(libzfs_handle_t *hdl, char *path)
  */
 char *
 zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
-    boolean_t verbose)
+    int name_flags)
 {
-       char *path, *devid, *type;
+       char *path, *type, *env;
        uint64_t value;
        char buf[PATH_BUF_LEN];
        char tmpbuf[PATH_BUF_LEN];
-       vdev_stat_t *vs;
-       uint_t vsc;
 
-       if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
-           &value) == 0) {
-               verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
-                   &value) == 0);
-               (void) snprintf(buf, sizeof (buf), "%llu",
-                   (u_longlong_t)value);
+       env = getenv("ZPOOL_VDEV_NAME_PATH");
+       if (env && (strtoul(env, NULL, 0) > 0 ||
+           !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
+               name_flags |= VDEV_NAME_PATH;
+
+       env = getenv("ZPOOL_VDEV_NAME_GUID");
+       if (env && (strtoul(env, NULL, 0) > 0 ||
+           !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
+               name_flags |= VDEV_NAME_GUID;
+
+       env = getenv("ZPOOL_VDEV_NAME_FOLLOW_LINKS");
+       if (env && (strtoul(env, NULL, 0) > 0 ||
+           !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
+               name_flags |= VDEV_NAME_FOLLOW_LINKS;
+
+       if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, &value) == 0 ||
+           name_flags & VDEV_NAME_GUID) {
+               nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value);
+               (void) snprintf(buf, sizeof (buf), "%llu", (u_longlong_t)value);
                path = buf;
        } else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
+#if defined(__sun__) || defined(__sun)
+               /*
+                * Live VDEV path updates to a kernel VDEV during a
+                * zpool_vdev_name lookup are not supported on Linux.
+                */
+               char *devid;
+               vdev_stat_t *vs;
+               uint_t vsc;
+
                /*
                 * If the device is dead (faulted, offline, etc) then don't
                 * bother opening it.  Otherwise we may be forcing the user to
@@ -3446,12 +3516,23 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
                        if (newdevid)
                                devid_str_free(newdevid);
                }
+#endif /* sun */
+
+               if (name_flags & VDEV_NAME_FOLLOW_LINKS) {
+                       char *rp = realpath(path, NULL);
+                       if (rp) {
+                               strlcpy(buf, rp, sizeof (buf));
+                               path = buf;
+                               free(rp);
+                       }
+               }
 
                /*
                 * For a block device only use the name.
                 */
                verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
-               if (strcmp(type, VDEV_TYPE_DISK) == 0) {
+               if ((strcmp(type, VDEV_TYPE_DISK) == 0) &&
+                   !(name_flags & VDEV_NAME_PATH)) {
                        path = strrchr(path, '/');
                        path++;
                }
@@ -3459,8 +3540,8 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
                /*
                 * Remove the partition from the path it this is a whole disk.
                 */
-               if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
-                   &value) == 0 && value) {
+               if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value)
+                   == 0 && value && !(name_flags & VDEV_NAME_PATH)) {
                        return (strip_partition(hdl, path));
                }
        } else {
@@ -3470,7 +3551,6 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
                 * If it's a raidz device, we need to stick in the parity level.
                 */
                if (strcmp(path, VDEV_TYPE_RAIDZ) == 0) {
-
                        verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
                            &value) == 0);
                        (void) snprintf(buf, sizeof (buf), "%s%llu", path,
@@ -3482,9 +3562,8 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
                 * We identify each top-level vdev by using a <type-id>
                 * naming convention.
                 */
-               if (verbose) {
+               if (name_flags & VDEV_NAME_TYPE_ID) {
                        uint64_t id;
-
                        verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
                            &id) == 0);
                        (void) snprintf(tmpbuf, sizeof (tmpbuf), "%s-%llu",
@@ -3497,9 +3576,9 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
 }
 
 static int
-zbookmark_compare(const void *a, const void *b)
+zbookmark_mem_compare(const void *a, const void *b)
 {
-       return (memcmp(a, b, sizeof (zbookmark_t)));
+       return (memcmp(a, b, sizeof (zbookmark_phys_t)));
 }
 
 /*
@@ -3511,7 +3590,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
 {
        zfs_cmd_t zc = {"\0"};
        uint64_t count;
-       zbookmark_t *zb = NULL;
+       zbookmark_phys_t *zb = NULL;
        int i;
 
        /*
@@ -3524,7 +3603,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
        if (count == 0)
                return (0);
        if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
-           count * sizeof (zbookmark_t))) == (uintptr_t)NULL)
+           count * sizeof (zbookmark_phys_t))) == (uintptr_t)NULL)
                return (-1);
        zc.zc_nvlist_dst_size = count;
        (void) strcpy(zc.zc_name, zhp->zpool_name);
@@ -3533,11 +3612,14 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
                    &zc) != 0) {
                        free((void *)(uintptr_t)zc.zc_nvlist_dst);
                        if (errno == ENOMEM) {
+                               void *dst;
+
                                count = zc.zc_nvlist_dst_size;
-                               if ((zc.zc_nvlist_dst = (uintptr_t)
-                                   zfs_alloc(zhp->zpool_hdl, count *
-                                   sizeof (zbookmark_t))) == (uintptr_t)NULL)
+                               dst = zfs_alloc(zhp->zpool_hdl, count *
+                                   sizeof (zbookmark_phys_t));
+                               if (dst == NULL)
                                        return (-1);
+                               zc.zc_nvlist_dst = (uintptr_t)dst;
                        } else {
                                return (-1);
                        }
@@ -3553,11 +3635,11 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
         * _not_ copied as part of the process.  So we point the start of our
         * array appropriate and decrement the total number of elements.
         */
-       zb = ((zbookmark_t *)(uintptr_t)zc.zc_nvlist_dst) +
+       zb = ((zbookmark_phys_t *)(uintptr_t)zc.zc_nvlist_dst) +
            zc.zc_nvlist_dst_size;
        count -= zc.zc_nvlist_dst_size;
 
-       qsort(zb, count, sizeof (zbookmark_t), zbookmark_compare);
+       qsort(zb, count, sizeof (zbookmark_phys_t), zbookmark_mem_compare);
 
        verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0);
 
@@ -3737,22 +3819,24 @@ zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover,
        return (0);
 }
 
-#define        HIS_BUF_LEN     (128*1024)
-
 /*
  * Retrieve the command history of a pool.
  */
 int
 zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
 {
-       char buf[HIS_BUF_LEN];
+       char *buf;
+       int buflen = 128 * 1024;
        uint64_t off = 0;
        nvlist_t **records = NULL;
        uint_t numrecords = 0;
        int err, i;
 
+       buf = malloc(buflen);
+       if (buf == NULL)
+               return (ENOMEM);
        do {
-               uint64_t bytes_read = sizeof (buf);
+               uint64_t bytes_read = buflen;
                uint64_t leftover;
 
                if ((err = get_history(zhp, buf, &off, &bytes_read)) != 0)
@@ -3766,10 +3850,23 @@ zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
                    &leftover, &records, &numrecords)) != 0)
                        break;
                off -= leftover;
+               if (leftover == bytes_read) {
+                       /*
+                        * no progress made, because buffer is not big enough
+                        * to hold this record; resize and retry.
+                        */
+                       buflen *= 2;
+                       free(buf);
+                       buf = malloc(buflen);
+                       if (buf == NULL)
+                               return (ENOMEM);
+               }
 
                /* CONSTCOND */
        } while (1);
 
+       free(buf);
+
        if (!err) {
                verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
                verify(nvlist_add_nvlist_array(*nvhisp, ZPOOL_HIST_RECORD,
@@ -3783,27 +3880,28 @@ zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
 }
 
 /*
- * Retrieve the next event.  If there is a new event available 'nvp' will
- * contain a newly allocated nvlist and 'dropped' will be set to the number
- * of missed events since the last call to this function.  When 'nvp' is
- * set to NULL it indicates no new events are available.  In either case
- * the function returns 0 and it is up to the caller to free 'nvp'.  In
- * the case of a fatal error the function will return a non-zero value.
- * When the function is called in blocking mode it will not return until
- * a new event is available.
+ * Retrieve the next event given the passed 'zevent_fd' file descriptor.
+ * If there is a new event available 'nvp' will contain a newly allocated
+ * nvlist and 'dropped' will be set to the number of missed events since
+ * the last call to this function.  When 'nvp' is set to NULL it indicates
+ * no new events are available.  In either case the function returns 0 and
+ * it is up to the caller to free 'nvp'.  In the case of a fatal error the
+ * function will return a non-zero value.  When the function is called in
+ * blocking mode (the default, unless the ZEVENT_NONBLOCK flag is passed),
+ * it will not return until a new event is available.
  */
 int
 zpool_events_next(libzfs_handle_t *hdl, nvlist_t **nvp,
-    int *dropped, int block, int cleanup_fd)
+    int *dropped, unsigned flags, int zevent_fd)
 {
        zfs_cmd_t zc = {"\0"};
        int error = 0;
 
        *nvp = NULL;
        *dropped = 0;
-       zc.zc_cleanup_fd = cleanup_fd;
+       zc.zc_cleanup_fd = zevent_fd;
 
-       if (!block)
+       if (flags & ZEVENT_NONBLOCK)
                zc.zc_guid = ZEVENT_NONBLOCK;
 
        if (zcmd_alloc_dst_nvlist(hdl, &zc, ZEVENT_SIZE) != 0)
@@ -3818,7 +3916,7 @@ retry:
                        goto out;
                case ENOENT:
                        /* Blocking error case should not occur */
-                       if (block)
+                       if (!(flags & ZEVENT_NONBLOCK))
                                error = zpool_standard_error_fmt(hdl, errno,
                                    dgettext(TEXT_DOMAIN, "cannot get event"));
 
@@ -3913,7 +4011,7 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
        zfs_cmd_t zc = {"\0"};
        boolean_t mounted = B_FALSE;
        char *mntpnt = NULL;
-       char dsname[MAXNAMELEN];
+       char dsname[ZFS_MAX_DATASET_NAME_LEN];
 
        if (dsobj == 0) {
                /* special case for the MOS */
@@ -4021,30 +4119,7 @@ find_start_block(nvlist_t *config)
        return (MAXOFFSET_T);
 }
 
-int
-zpool_label_disk_wait(char *path, int timeout)
-{
-       struct stat64 statbuf;
-       int i;
-
-       /*
-        * Wait timeout miliseconds for a newly created device to be available
-        * from the given path.  There is a small window when a /dev/ device
-        * will exist and the udev link will not, so we must wait for the
-        * symlink.  Depending on the udev rules this may take a few seconds.
-        */
-       for (i = 0; i < timeout; i++) {
-               usleep(1000);
-
-               errno = 0;
-               if ((stat64(path, &statbuf) == 0) && (errno == 0))
-                       return (0);
-       }
-
-       return (ENOENT);
-}
-
-int
+static int
 zpool_label_disk_check(char *path)
 {
        struct dk_gpt *vtoc;
@@ -4069,6 +4144,32 @@ zpool_label_disk_check(char *path)
        return (0);
 }
 
+/*
+ * Generate a unique partition name for the ZFS member.  Partitions must
+ * have unique names to ensure udev will be able to create symlinks under
+ * /dev/disk/by-partlabel/ for all pool members.  The partition names are
+ * of the form <pool>-<unique-id>.
+ */
+static void
+zpool_label_name(char *label_name, int label_size)
+{
+       uint64_t id = 0;
+       int fd;
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd > 0) {
+               if (read(fd, &id, sizeof (id)) != sizeof (id))
+                       id = 0;
+
+               close(fd);
+       }
+
+       if (id == 0)
+               id = (((uint64_t)rand()) << 32) | (uint64_t)rand();
+
+       snprintf(label_name, label_size, "zfs-%016llx", (u_longlong_t) id);
+}
+
 /*
  * Label an individual disk.  The name provided is the short name,
  * stripped of any leading /dev path.
@@ -4159,7 +4260,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
         * can get, in the absence of V_OTHER.
         */
        vtoc->efi_parts[0].p_tag = V_USR;
-       (void) strcpy(vtoc->efi_parts[0].p_name, "zfs");
+       zpool_label_name(vtoc->efi_parts[0].p_name, EFI_PART_NAME_LEN);
 
        vtoc->efi_parts[8].p_start = slice_size + start_block;
        vtoc->efi_parts[8].p_size = resv;
@@ -4183,12 +4284,11 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
        (void) close(fd);
        efi_free(vtoc);
 
-       /* Wait for the first expected partition to appear. */
-
        (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
        (void) zfs_append_partition(path, MAXPATHLEN);
 
-       rval = zpool_label_disk_wait(path, 3000);
+       /* Wait to udev to signal use the device has settled. */
+       rval = zpool_label_disk_wait(path, DISK_LABEL_WAIT);
        if (rval) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to "
                    "detect device partitions on '%s': %d"), path, rval);