]> git.proxmox.com Git - mirror_zfs.git/blobdiff - cmd/zpool/zpool_main.c
Fix GCC 12 compilation errors
[mirror_zfs.git] / cmd / zpool / zpool_main.c
index e23604b3d81c3463a3e5cf5749874c4a4ec57956..0b55bf21f448d7f73a9519b65bce7ab5e253b3ee 100644 (file)
@@ -6,7 +6,7 @@
  * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
+ * or https://opensource.org/licenses/CDDL-1.0.
  * See the License for the specific language governing permissions
  * and limitations under the License.
  *
@@ -32,6 +32,8 @@
  * Copyright (c) 2017, Intel Corporation.
  * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
  * Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
+ * Copyright (c) 2021, Klara Inc.
+ * Copyright [2021] Hewlett Packard Enterprise Development LP
  */
 
 #include <assert.h>
@@ -48,7 +50,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <strings.h>
 #include <time.h>
 #include <unistd.h>
 #include <pwd.h>
@@ -210,7 +211,7 @@ enum iostat_type {
  * of all the nvlists a flag requires.  Also specifies the order in
  * which data gets printed in zpool iostat.
  */
-static const char *vsx_type_to_nvlist[IOS_COUNT][13] = {
+static const char *vsx_type_to_nvlist[IOS_COUNT][15] = {
        [IOS_L_HISTO] = {
            ZPOOL_CONFIG_VDEV_TOT_R_LAT_HISTO,
            ZPOOL_CONFIG_VDEV_TOT_W_LAT_HISTO,
@@ -222,6 +223,7 @@ static const char *vsx_type_to_nvlist[IOS_COUNT][13] = {
            ZPOOL_CONFIG_VDEV_ASYNC_W_LAT_HISTO,
            ZPOOL_CONFIG_VDEV_SCRUB_LAT_HISTO,
            ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO,
+           ZPOOL_CONFIG_VDEV_REBUILD_LAT_HISTO,
            NULL},
        [IOS_LATENCY] = {
            ZPOOL_CONFIG_VDEV_TOT_R_LAT_HISTO,
@@ -229,6 +231,7 @@ static const char *vsx_type_to_nvlist[IOS_COUNT][13] = {
            ZPOOL_CONFIG_VDEV_DISK_R_LAT_HISTO,
            ZPOOL_CONFIG_VDEV_DISK_W_LAT_HISTO,
            ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO,
+           ZPOOL_CONFIG_VDEV_REBUILD_LAT_HISTO,
            NULL},
        [IOS_QUEUES] = {
            ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE,
@@ -237,6 +240,7 @@ static const char *vsx_type_to_nvlist[IOS_COUNT][13] = {
            ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE,
            ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE,
            ZPOOL_CONFIG_VDEV_TRIM_ACTIVE_QUEUE,
+           ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE,
            NULL},
        [IOS_RQ_HISTO] = {
            ZPOOL_CONFIG_VDEV_SYNC_IND_R_HISTO,
@@ -251,6 +255,8 @@ static const char *vsx_type_to_nvlist[IOS_COUNT][13] = {
            ZPOOL_CONFIG_VDEV_AGG_SCRUB_HISTO,
            ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO,
            ZPOOL_CONFIG_VDEV_AGG_TRIM_HISTO,
+           ZPOOL_CONFIG_VDEV_IND_REBUILD_HISTO,
+           ZPOOL_CONFIG_VDEV_AGG_REBUILD_HISTO,
            NULL},
 };
 
@@ -329,6 +335,7 @@ static zpool_command_t command_table[] = {
 #define        VDEV_ALLOC_CLASS_LOGS   "logs"
 
 static zpool_command_t *current_command;
+static zfs_type_t current_prop_type = (ZFS_TYPE_POOL | ZFS_TYPE_VDEV);
 static char history_str[HIS_MAX_RECORD_LEN];
 static boolean_t log_history = B_TRUE;
 static uint_t timestamp_fmt = NODATE;
@@ -428,10 +435,9 @@ get_usage(zpool_help_t idx)
        case HELP_WAIT:
                return (gettext("\twait [-Hp] [-T d|u] [-t <activity>[,...]] "
                    "<pool> [interval]\n"));
+       default:
+               __builtin_unreachable();
        }
-
-       abort();
-       /* NOTREACHED */
 }
 
 static void
@@ -465,7 +471,7 @@ zpool_collect_leaves(zpool_handle_t *zhp, nvlist_t *nvroot, nvlist_t *res)
  * Callback routine that will print out a pool property value.
  */
 static int
-print_prop_cb(int prop, void *cb)
+print_pool_prop_cb(int prop, void *cb)
 {
        FILE *fp = cb;
 
@@ -484,12 +490,35 @@ print_prop_cb(int prop, void *cb)
        return (ZPROP_CONT);
 }
 
+/*
+ * Callback routine that will print out a vdev property value.
+ */
+static int
+print_vdev_prop_cb(int prop, void *cb)
+{
+       FILE *fp = cb;
+
+       (void) fprintf(fp, "\t%-19s  ", vdev_prop_to_name(prop));
+
+       if (vdev_prop_readonly(prop))
+               (void) fprintf(fp, "  NO   ");
+       else
+               (void) fprintf(fp, " YES   ");
+
+       if (vdev_prop_values(prop) == NULL)
+               (void) fprintf(fp, "-\n");
+       else
+               (void) fprintf(fp, "%s\n", vdev_prop_values(prop));
+
+       return (ZPROP_CONT);
+}
+
 /*
  * Display usage message.  If we're inside a command, display only the usage for
  * that command.  Otherwise, iterate over the entire command table and display
  * a complete usage message.
  */
-static void
+static __attribute__((noreturn)) void
 usage(boolean_t requested)
 {
        FILE *fp = requested ? stdout : stderr;
@@ -514,25 +543,33 @@ usage(boolean_t requested)
        }
 
        if (current_command != NULL &&
+           current_prop_type != (ZFS_TYPE_POOL | ZFS_TYPE_VDEV) &&
            ((strcmp(current_command->name, "set") == 0) ||
            (strcmp(current_command->name, "get") == 0) ||
            (strcmp(current_command->name, "list") == 0))) {
 
-               (void) fprintf(fp,
+               (void) fprintf(fp, "%s",
                    gettext("\nthe following properties are supported:\n"));
 
                (void) fprintf(fp, "\n\t%-19s  %s   %s\n\n",
                    "PROPERTY", "EDIT", "VALUES");
 
                /* Iterate over all properties */
-               (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE,
-                   ZFS_TYPE_POOL);
+               if (current_prop_type == ZFS_TYPE_POOL) {
+                       (void) zprop_iter(print_pool_prop_cb, fp, B_FALSE,
+                           B_TRUE, current_prop_type);
 
-               (void) fprintf(fp, "\t%-19s   ", "feature@...");
-               (void) fprintf(fp, "YES   disabled | enabled | active\n");
+                       (void) fprintf(fp, "\t%-19s   ", "feature@...");
+                       (void) fprintf(fp, "YES   "
+                           "disabled | enabled | active\n");
 
-               (void) fprintf(fp, gettext("\nThe feature@ properties must be "
-                   "appended with a feature name.\nSee zpool-features(5).\n"));
+                       (void) fprintf(fp, gettext("\nThe feature@ properties "
+                           "must be appended with a feature name.\n"
+                           "See zpool-features(7).\n"));
+               } else if (current_prop_type == ZFS_TYPE_VDEV) {
+                       (void) zprop_iter(print_vdev_prop_cb, fp, B_FALSE,
+                           B_TRUE, current_prop_type);
+               }
        }
 
        /*
@@ -674,7 +711,7 @@ print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent,
 
        for (c = 0; c < children; c++) {
                uint64_t is_log = B_FALSE, is_hole = B_FALSE;
-               char *class = "";
+               char *class = (char *)"";
 
                (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE,
                    &is_hole);
@@ -686,7 +723,7 @@ print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent,
                (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
                    &is_log);
                if (is_log)
-                       class = VDEV_ALLOC_BIAS_LOG;
+                       class = (char *)VDEV_ALLOC_BIAS_LOG;
                (void) nvlist_lookup_string(child[c],
                    ZPOOL_CONFIG_ALLOCATION_BIAS, &class);
                if (strcmp(match, class) != 0)
@@ -767,7 +804,7 @@ prop_list_contains_feature(nvlist_t *proplist)
  * Add a property pair (name, string-value) into a property nvlist.
  */
 static int
-add_prop_list(const char *propname, char *propval, nvlist_t **props,
+add_prop_list(const char *propname, const char *propval, nvlist_t **props,
     boolean_t poolprop)
 {
        zpool_prop_t prop = ZPOOL_PROP_INVAL;
@@ -786,13 +823,14 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
 
        if (poolprop) {
                const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION);
-               const char *fname =
+               const char *cname =
                    zpool_prop_to_name(ZPOOL_PROP_COMPATIBILITY);
 
                if ((prop = zpool_name_to_prop(propname)) == ZPOOL_PROP_INVAL &&
-                   !zpool_prop_feature(propname)) {
+                   (!zpool_prop_feature(propname) &&
+                   !zpool_prop_vdev(propname))) {
                        (void) fprintf(stderr, gettext("property '%s' is "
-                           "not a valid pool property\n"), propname);
+                           "not a valid pool or vdev property\n"), propname);
                        return (2);
                }
 
@@ -811,20 +849,23 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
                }
 
                /*
-                * compatibility property and version should not be specified
-                * at the same time.
+                * if version is specified, only "legacy" compatibility
+                * may be requested
                 */
                if ((prop == ZPOOL_PROP_COMPATIBILITY &&
+                   strcmp(propval, ZPOOL_COMPAT_LEGACY) != 0 &&
                    nvlist_exists(proplist, vname)) ||
                    (prop == ZPOOL_PROP_VERSION &&
-                   nvlist_exists(proplist, fname))) {
-                       (void) fprintf(stderr, gettext("'compatibility' and "
-                           "'version' properties cannot be specified "
-                           "together\n"));
+                   nvlist_exists(proplist, cname) &&
+                   strcmp(fnvlist_lookup_string(proplist, cname),
+                   ZPOOL_COMPAT_LEGACY) != 0)) {
+                       (void) fprintf(stderr, gettext("when 'version' is "
+                           "specified, the 'compatibility' feature may only "
+                           "be set to '" ZPOOL_COMPAT_LEGACY "'\n"));
                        return (2);
                }
 
-               if (zpool_prop_feature(propname))
+               if (zpool_prop_feature(propname) || zpool_prop_vdev(propname))
                        normnm = propname;
                else
                        normnm = zpool_prop_to_name(prop);
@@ -864,8 +905,8 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
  * Set a default property pair (name, string-value) in a property nvlist
  */
 static int
-add_prop_list_default(const char *propname, char *propval, nvlist_t **props,
-    boolean_t poolprop)
+add_prop_list_default(const char *propname, const char *propval,
+    nvlist_t **props)
 {
        char *pval;
 
@@ -1065,7 +1106,7 @@ zpool_do_add(int argc, char **argv)
                                free(vname);
                        }
                }
-               /* And finaly the spares */
+               /* And finally the spares */
                if (nvlist_lookup_nvlist_array(poolnvroot, ZPOOL_CONFIG_SPARES,
                    &sparechild, &sparechildren) == 0 && sparechildren > 0) {
                        hadspare = B_TRUE;
@@ -1155,6 +1196,7 @@ zpool_do_remove(int argc, char **argv)
                return (1);
 
        if (stop && noop) {
+               zpool_close(zhp);
                (void) fprintf(stderr, gettext("stop request ignored\n"));
                return (0);
        }
@@ -1211,6 +1253,26 @@ zpool_do_remove(int argc, char **argv)
        return (ret);
 }
 
+/*
+ * Return 1 if a vdev is active (being used in a pool)
+ * Return 0 if a vdev is inactive (offlined or faulted, or not in active pool)
+ *
+ * This is useful for checking if a disk in an active pool is offlined or
+ * faulted.
+ */
+static int
+vdev_is_active(char *vdev_path)
+{
+       int fd;
+       fd = open(vdev_path, O_EXCL);
+       if (fd < 0) {
+               return (1);   /* cant open O_EXCL - disk is active */
+       }
+
+       close(fd);
+       return (0);   /* disk is inactive in the pool */
+}
+
 /*
  * zpool labelclear [-f] <vdev>
  *
@@ -1320,9 +1382,23 @@ zpool_do_labelclear(int argc, char **argv)
        case POOL_STATE_ACTIVE:
        case POOL_STATE_SPARE:
        case POOL_STATE_L2CACHE:
+               /*
+                * We allow the user to call 'zpool offline -f'
+                * on an offlined disk in an active pool. We can check if
+                * the disk is online by calling vdev_is_active().
+                */
+               if (force && !vdev_is_active(vdev))
+                       break;
+
                (void) fprintf(stderr, gettext(
-                   "%s is a member (%s) of pool \"%s\"\n"),
+                   "%s is a member (%s) of pool \"%s\""),
                    vdev, zpool_pool_state_to_name(state), name);
+
+               if (force) {
+                       (void) fprintf(stderr, gettext(
+                           ". Offline the disk first to clear its label."));
+               }
+               printf("\n");
                ret = 1;
                goto errout;
 
@@ -1425,7 +1501,7 @@ zpool_do_create(int argc, char **argv)
                            ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
                                goto errout;
                        if (add_prop_list_default(zpool_prop_to_name(
-                           ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
+                           ZPOOL_PROP_CACHEFILE), "none", &props))
                                goto errout;
                        break;
                case 'm':
@@ -1503,7 +1579,7 @@ zpool_do_create(int argc, char **argv)
                            ZPOOL_PROP_TNAME), optarg, &props, B_TRUE))
                                goto errout;
                        if (add_prop_list_default(zpool_prop_to_name(
-                           ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
+                           ZPOOL_PROP_CACHEFILE), "none", &props))
                                goto errout;
                        tname = optarg;
                        break;
@@ -1674,6 +1750,7 @@ zpool_do_create(int argc, char **argv)
                 *    - enable_pool_features (ie: no '-d' or '-o version')
                 *    - it's supported by the kernel module
                 *    - it's in the requested feature set
+                *  - warn if it's enabled but not in compat
                 */
                for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
                        char propname[MAXPATHLEN];
@@ -1684,9 +1761,19 @@ zpool_do_create(int argc, char **argv)
                            "feature@%s", feat->fi_uname);
 
                        if (!nvlist_lookup_string(props, propname, &propval)) {
-                               if (strcmp(propval, ZFS_FEATURE_DISABLED) == 0)
+                               if (strcmp(propval,
+                                   ZFS_FEATURE_DISABLED) == 0) {
                                        (void) nvlist_remove_all(props,
                                            propname);
+                               } else if (strcmp(propval,
+                                   ZFS_FEATURE_ENABLED) == 0 &&
+                                   !requested_features[i]) {
+                                       (void) fprintf(stderr, gettext(
+                                           "Warning: feature \"%s\" enabled "
+                                           "but is not in specified "
+                                           "'compatibility' feature set.\n"),
+                                           feat->fi_uname);
+                               }
                        } else if (
                            enable_pool_features &&
                            feat->fi_zfs_mod_supported &&
@@ -1705,8 +1792,8 @@ zpool_do_create(int argc, char **argv)
                            tname ? tname : poolname, ZFS_TYPE_FILESYSTEM);
                        if (pool != NULL) {
                                if (zfs_mount(pool, NULL, 0) == 0) {
-                                       ret = zfs_shareall(pool);
-                                       zfs_commit_all_shares();
+                                       ret = zfs_share(pool, NULL);
+                                       zfs_commit_shares(NULL);
                                }
                                zfs_close(pool);
                        }
@@ -1879,7 +1966,7 @@ zpool_do_export(int argc, char **argv)
                }
 
                return (for_each_pool(argc, argv, B_TRUE, NULL,
-                   B_FALSE, zpool_export_one, &cb));
+                   ZFS_TYPE_POOL, B_FALSE, zpool_export_one, &cb));
        }
 
        /* check arguments */
@@ -1888,8 +1975,8 @@ zpool_do_export(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       ret = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, zpool_export_one,
-           &cb);
+       ret = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
+           B_FALSE, zpool_export_one, &cb);
 
        return (ret);
 }
@@ -1902,40 +1989,21 @@ static int
 max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max,
     int name_flags)
 {
-       char *name;
-       nvlist_t **child;
-       uint_t c, children;
-       int ret;
-
-       name = zpool_vdev_name(g_zfs, zhp, nv, name_flags);
-       if (strlen(name) + depth > max)
-               max = strlen(name) + depth;
+       static const char *const subtypes[] =
+           {ZPOOL_CONFIG_SPARES, ZPOOL_CONFIG_L2CACHE, ZPOOL_CONFIG_CHILDREN};
 
+       char *name = zpool_vdev_name(g_zfs, zhp, nv, name_flags);
+       max = MAX(strlen(name) + depth, max);
        free(name);
 
-       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
-           &child, &children) == 0) {
-               for (c = 0; c < children; c++)
-                       if ((ret = max_width(zhp, child[c], depth + 2,
-                           max, name_flags)) > max)
-                               max = ret;
-       }
-
-       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
-           &child, &children) == 0) {
-               for (c = 0; c < children; c++)
-                       if ((ret = max_width(zhp, child[c], depth + 2,
-                           max, name_flags)) > max)
-                               max = ret;
-       }
-
-       if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
-           &child, &children) == 0) {
-               for (c = 0; c < children; c++)
-                       if ((ret = max_width(zhp, child[c], depth + 2,
-                           max, name_flags)) > max)
-                               max = ret;
-       }
+       nvlist_t **child;
+       uint_t children;
+       for (size_t i = 0; i < ARRAY_SIZE(subtypes); ++i)
+               if (nvlist_lookup_nvlist_array(nv, subtypes[i],
+                   &child, &children) == 0)
+                       for (uint_t c = 0; c < children; ++c)
+                               max = MAX(max_width(zhp, child[c], depth + 2,
+                                   max, name_flags), max);
 
        return (max);
 }
@@ -2003,15 +2071,13 @@ typedef struct status_cbdata {
 } status_cbdata_t;
 
 /* Return 1 if string is NULL, empty, or whitespace; return 0 otherwise. */
-static int
-is_blank_str(char *str)
+static boolean_t
+is_blank_str(const char *str)
 {
-       while (str != NULL && *str != '\0') {
+       for (; str != NULL && *str != '\0'; ++str)
                if (!isblank(*str))
-                       return (0);
-               str++;
-       }
-       return (1);
+                       return (B_FALSE);
+       return (B_TRUE);
 }
 
 /* Print command output lines for specific vdev in a specific pool */
@@ -2020,7 +2086,7 @@ zpool_print_cmd(vdev_cmd_data_list_t *vcdl, const char *pool, char *path)
 {
        vdev_cmd_data_t *data;
        int i, j;
-       char *val;
+       const char *val;
 
        for (i = 0; i < vcdl->count; i++) {
                if ((strcmp(vcdl->data[i].path, path) != 0) ||
@@ -2051,17 +2117,17 @@ zpool_print_cmd(vdev_cmd_data_list_t *vcdl, const char *pool, char *path)
 
                        printf("%*s", vcdl->uniq_cols_width[j], val);
                        if (j < vcdl->uniq_cols_cnt - 1)
-                               printf("  ");
+                               fputs("  ", stdout);
                }
 
                /* Print out any values that aren't in a column at the end */
                for (j = data->cols_cnt; j < data->lines_cnt; j++) {
                        /* Did we have any columns?  If so print a spacer. */
                        if (vcdl->uniq_cols_cnt > 0)
-                               printf("  ");
+                               fputs("  ", stdout);
 
                        val = data->lines[j];
-                       printf("%s", val ? val : "");
+                       fputs(val ?: "", stdout);
                }
                break;
        }
@@ -2174,7 +2240,7 @@ print_status_trim(vdev_stat_t *vs, boolean_t verbose)
  * Return the color associated with a health string.  This includes returning
  * NULL for no color change.
  */
-static char *
+static const char *
 health_str_to_color(const char *health)
 {
        if (strcmp(health, gettext("FAULTED")) == 0 ||
@@ -2210,7 +2276,7 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
        const char *state;
        char *type;
        char *path = NULL;
-       char *rcolor = NULL, *wcolor = NULL, *ccolor = NULL;
+       const char *rcolor = NULL, *wcolor = NULL, *ccolor = NULL;
 
        if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
            &child, &children) != 0)
@@ -2252,13 +2318,13 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
                        ccolor = ANSI_RED;
 
                if (cb->cb_literal) {
-                       printf(" ");
+                       fputc(' ', stdout);
                        printf_color(rcolor, "%5llu",
                            (u_longlong_t)vs->vs_read_errors);
-                       printf(" ");
+                       fputc(' ', stdout);
                        printf_color(wcolor, "%5llu",
                            (u_longlong_t)vs->vs_write_errors);
-                       printf(" ");
+                       fputc(' ', stdout);
                        printf_color(ccolor, "%5llu",
                            (u_longlong_t)vs->vs_checksum_errors);
                } else {
@@ -2266,11 +2332,11 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
                        zfs_nicenum(vs->vs_write_errors, wbuf, sizeof (wbuf));
                        zfs_nicenum(vs->vs_checksum_errors, cbuf,
                            sizeof (cbuf));
-                       printf(" ");
+                       fputc(' ', stdout);
                        printf_color(rcolor, "%5s", rbuf);
-                       printf(" ");
+                       fputc(' ', stdout);
                        printf_color(wcolor, "%5s", wbuf);
-                       printf(" ");
+                       fputc(' ', stdout);
                        printf_color(ccolor, "%5s", cbuf);
                }
                if (cb->cb_print_slow_ios) {
@@ -2367,6 +2433,10 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
                        (void) printf(gettext("all children offline"));
                        break;
 
+               case VDEV_AUX_BAD_LABEL:
+                       (void) printf(gettext("invalid label"));
+                       break;
+
                default:
                        (void) printf(gettext("corrupted data"));
                        break;
@@ -2381,13 +2451,26 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
                    1 << vs->vs_configured_ashift, 1 << vs->vs_physical_ashift);
        }
 
+       if (vs->vs_scan_removing != 0) {
+               (void) printf(gettext("  (removing)"));
+       } else if (VDEV_STAT_VALID(vs_noalloc, vsc) && vs->vs_noalloc != 0) {
+               (void) printf(gettext("  (non-allocating)"));
+       }
+
        /* The root vdev has the scrub/resilver stats */
        root = fnvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
            ZPOOL_CONFIG_VDEV_TREE);
        (void) nvlist_lookup_uint64_array(root, ZPOOL_CONFIG_SCAN_STATS,
            (uint64_t **)&ps, &c);
 
-       if (ps != NULL && ps->pss_state == DSS_SCANNING && children == 0) {
+       /*
+        * If you force fault a drive that's resilvering, its scan stats can
+        * get frozen in time, giving the false impression that it's
+        * being resilvered.  That's why we check the state to see if the vdev
+        * is healthy before reporting "resilvering" or "repairing".
+        */
+       if (ps != NULL && ps->pss_state == DSS_SCANNING && children == 0 &&
+           vs->vs_state == VDEV_STATE_HEALTHY) {
                if (vs->vs_scan_processed != 0) {
                        (void) printf(gettext("  (%s)"),
                            (ps->pss_func == POOL_SCAN_RESILVER) ?
@@ -2399,7 +2482,7 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
 
        /* The top-level vdevs have the rebuild stats */
        if (vrs != NULL && vrs->vrs_state == VDEV_REBUILD_ACTIVE &&
-           children == 0) {
+           children == 0 && vs->vs_state == VDEV_STATE_HEALTHY) {
                if (vs->vs_rebuild_processed != 0) {
                        (void) printf(gettext("  (resilvering)"));
                }
@@ -2509,6 +2592,10 @@ print_import_config(status_cbdata_t *cb, const char *name, nvlist_t *nv,
                        (void) printf(gettext("all children offline"));
                        break;
 
+               case VDEV_AUX_BAD_LABEL:
+                       (void) printf(gettext("invalid label"));
+                       break;
+
                default:
                        (void) printf(gettext("corrupted data"));
                        break;
@@ -2591,7 +2678,7 @@ print_class_vdevs(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
                    &is_log);
 
                if (is_log) {
-                       bias = VDEV_ALLOC_CLASS_LOGS;
+                       bias = (char *)VDEV_ALLOC_CLASS_LOGS;
                } else {
                        (void) nvlist_lookup_string(child[c],
                            ZPOOL_CONFIG_ALLOCATION_BIAS, &bias);
@@ -2631,8 +2718,8 @@ show_import(nvlist_t *config, boolean_t report_error)
        char *name;
        uint64_t guid;
        uint64_t hostid = 0;
-       char *msgid;
-       char *hostname = "unknown";
+       const char *msgid;
+       const char *hostname = "unknown";
        nvlist_t *nvroot, *nvinfo;
        zpool_status_t reason;
        zpool_errata_t errata;
@@ -2717,8 +2804,10 @@ show_import(nvlist_t *config, boolean_t report_error)
 
        case ZPOOL_STATUS_FEAT_DISABLED:
                printf_color(ANSI_BOLD, gettext("status: "));
-               printf_color(ANSI_YELLOW, gettext("Some supported and "
-                   "requested features are not enabled on the pool.\n"));
+               printf_color(ANSI_YELLOW, gettext("Some supported "
+                   "features are not enabled on the pool.\n\t"
+                   "(Note that they may be intentionally disabled "
+                   "if the\n\t'compatibility' property is set.)\n"));
                break;
 
        case ZPOOL_STATUS_COMPATIBILITY_ERR:
@@ -2728,6 +2817,13 @@ show_import(nvlist_t *config, boolean_t report_error)
                    "property.\n"));
                break;
 
+       case ZPOOL_STATUS_INCOMPATIBLE_FEAT:
+               printf_color(ANSI_BOLD, gettext("status: "));
+               printf_color(ANSI_YELLOW, gettext("One or more features "
+                   "are enabled on the pool despite not being\n"
+                   "requested by the 'compatibility' property.\n"));
+               break;
+
        case ZPOOL_STATUS_UNSUP_FEAT_READ:
                printf_color(ANSI_BOLD, gettext("status: "));
                printf_color(ANSI_YELLOW, gettext("The pool uses the following "
@@ -2932,9 +3028,8 @@ show_import(nvlist_t *config, boolean_t report_error)
                                    ZPOOL_CONFIG_MMP_HOSTID);
 
                        (void) printf(gettext(" action: The pool must be "
-                           "exported from %s (hostid=%lx)\n\tbefore it "
-                           "can be safely imported.\n"), hostname,
-                           (unsigned long) hostid);
+                           "exported from %s (hostid=%"PRIx64")\n\tbefore it "
+                           "can be safely imported.\n"), hostname, hostid);
                        break;
                case ZPOOL_STATUS_HOSTID_REQUIRED:
                        (void) printf(gettext(" action: Set a unique system "
@@ -3029,7 +3124,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
 {
        int ret = 0;
        zpool_handle_t *zhp;
-       char *name;
+       const char *name;
        uint64_t version;
 
        name = fnvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME);
@@ -3050,7 +3145,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
                            ZPOOL_CONFIG_MMP_STATE);
 
                if (mmp_state == MMP_STATE_ACTIVE) {
-                       char *hostname = "<unknown>";
+                       const char *hostname = "<unknown>";
                        uint64_t hostid = 0;
 
                        if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_HOSTNAME))
@@ -3063,17 +3158,17 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
 
                        (void) fprintf(stderr, gettext("cannot import '%s': "
                            "pool is imported on %s (hostid: "
-                           "0x%lx)\nExport the pool on the other system, "
-                           "then run 'zpool import'.\n"),
-                           name, hostname, (unsigned long) hostid);
+                           "0x%"PRIx64")\nExport the pool on the other "
+                           "system, then run 'zpool import'.\n"),
+                           name, hostname, hostid);
                } else if (mmp_state == MMP_STATE_NO_HOSTID) {
                        (void) fprintf(stderr, gettext("Cannot import '%s': "
                            "pool has the multihost property on and the\n"
                            "system's hostid is not set. Set a unique hostid "
                            "with the zgenhostid(8) command.\n"), name);
                } else {
-                       char *hostname = "<unknown>";
-                       uint64_t timestamp = 0;
+                       const char *hostname = "<unknown>";
+                       time_t timestamp = 0;
                        uint64_t hostid = 0;
 
                        if (nvlist_exists(config, ZPOOL_CONFIG_HOSTNAME))
@@ -3090,10 +3185,10 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
 
                        (void) fprintf(stderr, gettext("cannot import '%s': "
                            "pool was previously in use from another system.\n"
-                           "Last accessed by %s (hostid=%lx) at %s"
+                           "Last accessed by %s (hostid=%"PRIx64") at %s"
                            "The pool can be imported, use 'zpool import -f' "
                            "to import the pool.\n"), name, hostname,
-                           (unsigned long)hostid, ctime((time_t *)&timestamp));
+                           hostid, ctime(&timestamp));
                }
 
                return (1);
@@ -3103,7 +3198,7 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
                return (1);
 
        if (newname != NULL)
-               name = (char *)newname;
+               name = newname;
 
        if ((zhp = zpool_open_canfail(g_zfs, name)) == NULL)
                return (1);
@@ -3112,11 +3207,9 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
         * Loading keys is best effort. We don't want to return immediately
         * if it fails but we do want to give the error to the caller.
         */
-       if (flags & ZFS_IMPORT_LOAD_KEYS) {
-               ret = zfs_crypto_attempt_load_keys(g_zfs, name);
-               if (ret != 0)
+       if (flags & ZFS_IMPORT_LOAD_KEYS &&
+           zfs_crypto_attempt_load_keys(g_zfs, name) != 0)
                        ret = 1;
-       }
 
        if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
            !(flags & ZFS_IMPORT_ONLY) &&
@@ -3166,7 +3259,7 @@ import_pools(nvlist_t *pools, nvlist_t *props, char *mntopts, int flags,
                        if (first)
                                first = B_FALSE;
                        else if (!do_all)
-                               (void) printf("\n");
+                               (void) fputc('\n', stdout);
 
                        if (do_all) {
                                err |= do_import(config, NULL, mntopts,
@@ -3469,16 +3562,8 @@ zpool_do_import(int argc, char **argv)
                        cachefile = optarg;
                        break;
                case 'd':
-                       if (searchdirs == NULL) {
-                               searchdirs = safe_malloc(sizeof (char *));
-                       } else {
-                               char **tmp = safe_malloc((nsearch + 1) *
-                                   sizeof (char *));
-                               bcopy(searchdirs, tmp, nsearch *
-                                   sizeof (char *));
-                               free(searchdirs);
-                               searchdirs = tmp;
-                       }
+                       searchdirs = safe_realloc(searchdirs,
+                           (nsearch + 1) * sizeof (char *));
                        searchdirs[nsearch++] = optarg;
                        break;
                case 'D':
@@ -3518,7 +3603,7 @@ zpool_do_import(int argc, char **argv)
                            ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
                                goto error;
                        if (add_prop_list_default(zpool_prop_to_name(
-                           ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
+                           ZPOOL_PROP_CACHEFILE), "none", &props))
                                goto error;
                        break;
                case 's':
@@ -3527,7 +3612,7 @@ zpool_do_import(int argc, char **argv)
                case 't':
                        flags |= ZFS_IMPORT_TEMP_NAME;
                        if (add_prop_list_default(zpool_prop_to_name(
-                           ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
+                           ZPOOL_PROP_CACHEFILE), "none", &props))
                                goto error;
                        break;
 
@@ -3625,9 +3710,8 @@ zpool_do_import(int argc, char **argv)
        if (argc == 0 && geteuid() != 0) {
                (void) fprintf(stderr, gettext("cannot "
                    "discover pools: permission denied\n"));
-               if (searchdirs != NULL)
-                       free(searchdirs);
 
+               free(searchdirs);
                nvlist_free(props);
                nvlist_free(policy);
                return (1);
@@ -3668,24 +3752,16 @@ zpool_do_import(int argc, char **argv)
         * Check the environment for the preferred search path.
         */
        if ((searchdirs == NULL) && (env = getenv("ZPOOL_IMPORT_PATH"))) {
-               char *dir;
+               char *dir, *tmp = NULL;
 
                envdup = strdup(env);
 
-               dir = strtok(envdup, ":");
-               while (dir != NULL) {
-                       if (searchdirs == NULL) {
-                               searchdirs = safe_malloc(sizeof (char *));
-                       } else {
-                               char **tmp = safe_malloc((nsearch + 1) *
-                                   sizeof (char *));
-                               bcopy(searchdirs, tmp, nsearch *
-                                   sizeof (char *));
-                               free(searchdirs);
-                               searchdirs = tmp;
-                       }
+               for (dir = strtok_r(envdup, ":", &tmp);
+                   dir != NULL;
+                   dir = strtok_r(NULL, ":", &tmp)) {
+                       searchdirs = safe_realloc(searchdirs,
+                           (nsearch + 1) * sizeof (char *));
                        searchdirs[nsearch++] = dir;
-                       dir = strtok(NULL, ":");
                }
        }
 
@@ -3697,7 +3773,12 @@ zpool_do_import(int argc, char **argv)
        idata.scan = do_scan;
        idata.policy = policy;
 
-       pools = zpool_search_import(g_zfs, &idata, &libzfs_config_ops);
+       libpc_handle_t lpch = {
+               .lpc_lib_handle = g_zfs,
+               .lpc_ops = &libzfs_config_ops,
+               .lpc_printerr = B_TRUE
+       };
+       pools = zpool_search_import(&lpch, &idata);
 
        if (pools != NULL && pool_exists &&
            (argc == 1 || strcmp(argv[0], argv[1]) == 0)) {
@@ -3724,19 +3805,18 @@ zpool_do_import(int argc, char **argv)
        }
 
        if (err == 1) {
-               if (searchdirs != NULL)
-                       free(searchdirs);
-               if (envdup != NULL)
-                       free(envdup);
+               free(searchdirs);
+               free(envdup);
                nvlist_free(policy);
                nvlist_free(pools);
                nvlist_free(props);
                return (1);
        }
 
-       err = import_pools(pools, props, mntopts, flags, argv[0],
-           argc == 1 ? NULL : argv[1], do_destroyed, pool_specified,
-           do_all, &idata);
+       err = import_pools(pools, props, mntopts, flags,
+           argc >= 1 ? argv[0] : NULL,
+           argc >= 2 ? argv[1] : NULL,
+           do_destroyed, pool_specified, do_all, &idata);
 
        /*
         * If we're using the cachefile and we failed to import, then
@@ -3754,21 +3834,20 @@ zpool_do_import(int argc, char **argv)
                 */
                idata.scan = B_TRUE;
                nvlist_free(pools);
-               pools = zpool_search_import(g_zfs, &idata, &libzfs_config_ops);
+               pools = zpool_search_import(&lpch, &idata);
 
-               err = import_pools(pools, props, mntopts, flags, argv[0],
-                   argc == 1 ? NULL : argv[1], do_destroyed, pool_specified,
-                   do_all, &idata);
+               err = import_pools(pools, props, mntopts, flags,
+                   argc >= 1 ? argv[0] : NULL,
+                   argc >= 2 ? argv[1] : NULL,
+                   do_destroyed, pool_specified, do_all, &idata);
        }
 
 error:
        nvlist_free(props);
        nvlist_free(pools);
        nvlist_free(policy);
-       if (searchdirs != NULL)
-               free(searchdirs);
-       if (envdup != NULL)
-               free(envdup);
+       free(searchdirs);
+       free(envdup);
 
        return (err ? 1 : 0);
 }
@@ -3807,24 +3886,22 @@ zpool_do_sync(int argc, char **argv)
        argv += optind;
 
        /* if argc == 0 we will execute zpool_sync_one on all pools */
-       ret = for_each_pool(argc, argv, B_FALSE, NULL, B_FALSE, zpool_sync_one,
-           &force);
+       ret = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL,
+           B_FALSE, zpool_sync_one, &force);
 
        return (ret);
 }
 
 typedef struct iostat_cbdata {
        uint64_t cb_flags;
-       int cb_name_flags;
        int cb_namewidth;
        int cb_iteration;
-       char **cb_vdev_names; /* Only show these vdevs */
-       unsigned int cb_vdev_names_count;
        boolean_t cb_verbose;
        boolean_t cb_literal;
        boolean_t cb_scripted;
        zpool_list_t *cb_list;
        vdev_cmd_data_list_t *vcdl;
+       vdev_cbdata_t cb_vdevs;
 } iostat_cbdata_t;
 
 /*  iostat labels */
@@ -3833,22 +3910,23 @@ typedef struct name_and_columns {
        unsigned int columns;   /* Center name to this number of columns */
 } name_and_columns_t;
 
-#define        IOSTAT_MAX_LABELS       13      /* Max number of labels on one line */
+#define        IOSTAT_MAX_LABELS       15      /* Max number of labels on one line */
 
 static const name_and_columns_t iostat_top_labels[][IOSTAT_MAX_LABELS] =
 {
        [IOS_DEFAULT] = {{"capacity", 2}, {"operations", 2}, {"bandwidth", 2},
            {NULL}},
        [IOS_LATENCY] = {{"total_wait", 2}, {"disk_wait", 2}, {"syncq_wait", 2},
-           {"asyncq_wait", 2}, {"scrub", 1}, {"trim", 1}, {NULL}},
+           {"asyncq_wait", 2}, {"scrub", 1}, {"trim", 1}, {"rebuild", 1},
+           {NULL}},
        [IOS_QUEUES] = {{"syncq_read", 2}, {"syncq_write", 2},
            {"asyncq_read", 2}, {"asyncq_write", 2}, {"scrubq_read", 2},
-           {"trimq_write", 2}, {NULL}},
+           {"trimq_write", 2}, {"rebuildq_write", 2}, {NULL}},
        [IOS_L_HISTO] = {{"total_wait", 2}, {"disk_wait", 2}, {"syncq_wait", 2},
            {"asyncq_wait", 2}, {NULL}},
        [IOS_RQ_HISTO] = {{"sync_read", 2}, {"sync_write", 2},
            {"async_read", 2}, {"async_write", 2}, {"scrub", 2},
-           {"trim", 2}, {NULL}},
+           {"trim", 2}, {"rebuild", 2}, {NULL}},
 };
 
 /* Shorthand - if "columns" field not set, default to 1 column */
@@ -3857,14 +3935,17 @@ static const name_and_columns_t iostat_bottom_labels[][IOSTAT_MAX_LABELS] =
        [IOS_DEFAULT] = {{"alloc"}, {"free"}, {"read"}, {"write"}, {"read"},
            {"write"}, {NULL}},
        [IOS_LATENCY] = {{"read"}, {"write"}, {"read"}, {"write"}, {"read"},
-           {"write"}, {"read"}, {"write"}, {"wait"}, {"wait"}, {NULL}},
+           {"write"}, {"read"}, {"write"}, {"wait"}, {"wait"}, {"wait"},
+           {NULL}},
        [IOS_QUEUES] = {{"pend"}, {"activ"}, {"pend"}, {"activ"}, {"pend"},
            {"activ"}, {"pend"}, {"activ"}, {"pend"}, {"activ"},
-           {"pend"}, {"activ"}, {NULL}},
+           {"pend"}, {"activ"}, {"pend"}, {"activ"}, {NULL}},
        [IOS_L_HISTO] = {{"read"}, {"write"}, {"read"}, {"write"}, {"read"},
-           {"write"}, {"read"}, {"write"}, {"scrub"}, {"trim"}, {NULL}},
+           {"write"}, {"read"}, {"write"}, {"scrub"}, {"trim"}, {"rebuild"},
+           {NULL}},
        [IOS_RQ_HISTO] = {{"ind"}, {"agg"}, {"ind"}, {"agg"}, {"ind"}, {"agg"},
-           {"ind"}, {"agg"}, {"ind"}, {"agg"}, {"ind"}, {"agg"}, {NULL}},
+           {"ind"}, {"agg"}, {"ind"}, {"agg"}, {"ind"}, {"agg"},
+           {"ind"}, {"agg"}, {NULL}},
 };
 
 static const char *histo_to_title[] = {
@@ -4074,7 +4155,7 @@ print_iostat_dashes(iostat_cbdata_t *cb, unsigned int force_column_width,
 
        if (cb->cb_flags & IOS_ANYHISTO_M) {
                title = histo_to_title[IOS_HISTO_IDX(cb->cb_flags)];
-       } else if (cb->cb_vdev_names_count) {
+       } else if (cb->cb_vdevs.cb_names_count) {
                title = "vdev";
        } else  {
                title = "pool";
@@ -4134,7 +4215,7 @@ print_iostat_header_impl(iostat_cbdata_t *cb, unsigned int force_column_width,
 
        if (cb->cb_flags & IOS_ANYHISTO_M) {
                title = histo_to_title[IOS_HISTO_IDX(cb->cb_flags)];
-       } else if (cb->cb_vdev_names_count) {
+       } else if (cb->cb_vdevs.cb_names_count) {
                title = "vdev";
        } else  {
                title = "pool";
@@ -4478,11 +4559,8 @@ single_histo_average(uint64_t *histo, unsigned int buckets)
 }
 
 static void
-print_iostat_queues(iostat_cbdata_t *cb, nvlist_t *oldnv,
-    nvlist_t *newnv)
+print_iostat_queues(iostat_cbdata_t *cb, nvlist_t *newnv)
 {
-       int i;
-       uint64_t val;
        const char *names[] = {
                ZPOOL_CONFIG_VDEV_SYNC_R_PEND_QUEUE,
                ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE,
@@ -4496,6 +4574,8 @@ print_iostat_queues(iostat_cbdata_t *cb, nvlist_t *oldnv,
                ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE,
                ZPOOL_CONFIG_VDEV_TRIM_PEND_QUEUE,
                ZPOOL_CONFIG_VDEV_TRIM_ACTIVE_QUEUE,
+               ZPOOL_CONFIG_VDEV_REBUILD_PEND_QUEUE,
+               ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE,
        };
 
        struct stat_array *nva;
@@ -4510,8 +4590,8 @@ print_iostat_queues(iostat_cbdata_t *cb, nvlist_t *oldnv,
        else
                format = ZFS_NICENUM_1024;
 
-       for (i = 0; i < ARRAY_SIZE(names); i++) {
-               val = nva[i].data[0];
+       for (int i = 0; i < ARRAY_SIZE(names); i++) {
+               uint64_t val = nva[i].data[0];
                print_one_stat(val, format, column_width, cb->cb_scripted);
        }
 
@@ -4535,6 +4615,7 @@ print_iostat_latency(iostat_cbdata_t *cb, nvlist_t *oldnv,
                ZPOOL_CONFIG_VDEV_ASYNC_W_LAT_HISTO,
                ZPOOL_CONFIG_VDEV_SCRUB_LAT_HISTO,
                ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO,
+               ZPOOL_CONFIG_VDEV_REBUILD_LAT_HISTO,
        };
        struct stat_array *nva;
 
@@ -4599,7 +4680,7 @@ print_iostat_default(vdev_stat_t *vs, iostat_cbdata_t *cb, double scale)
            format, column_width, cb->cb_scripted);
 }
 
-static const char *class_name[] = {
+static const char *const class_name[] = {
        VDEV_ALLOC_BIAS_DEDUP,
        VDEV_ALLOC_BIAS_SPECIAL,
        VDEV_ALLOC_CLASS_LOGS
@@ -4639,9 +4720,9 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
        }
 
        /* Do we only want to see a specific vdev? */
-       for (i = 0; i < cb->cb_vdev_names_count; i++) {
+       for (i = 0; i < cb->cb_vdevs.cb_names_count; i++) {
                /* Yes we do.  Is this the vdev? */
-               if (strcmp(name, cb->cb_vdev_names[i]) == 0) {
+               if (strcmp(name, cb->cb_vdevs.cb_names[i]) == 0) {
                        /*
                         * This is our vdev.  Since it is the only vdev we
                         * will be displaying, make depth = 0 so that it
@@ -4652,7 +4733,7 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
                }
        }
 
-       if (cb->cb_vdev_names_count && (i == cb->cb_vdev_names_count)) {
+       if (cb->cb_vdevs.cb_names_count && (i == cb->cb_vdevs.cb_names_count)) {
                /* Couldn't match the name */
                goto children;
        }
@@ -4701,7 +4782,7 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
        if (cb->cb_flags & IOS_LATENCY_M)
                print_iostat_latency(cb, oldnv, newnv);
        if (cb->cb_flags & IOS_QUEUES_M)
-               print_iostat_queues(cb, oldnv, newnv);
+               print_iostat_queues(cb, newnv);
        if (cb->cb_flags & IOS_ANYHISTO_M) {
                printf("\n");
                print_iostat_histos(cb, oldnv, newnv, scale, name);
@@ -4759,7 +4840,7 @@ children:
                        continue;
 
                vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
-                   cb->cb_name_flags);
+                   cb->cb_vdevs.cb_name_flags | VDEV_NAME_TYPE_ID);
                ret += print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL,
                    newchild[c], cb, depth + 2);
                free(vname);
@@ -4768,7 +4849,7 @@ children:
        /*
         * print all other top-level devices
         */
-       for (uint_t n = 0; n < 3; n++) {
+       for (uint_t n = 0; n < ARRAY_SIZE(class_name); n++) {
                boolean_t printed = B_FALSE;
 
                for (c = 0; c < children; c++) {
@@ -4779,7 +4860,7 @@ children:
                        (void) nvlist_lookup_uint64(newchild[c],
                            ZPOOL_CONFIG_IS_LOG, &islog);
                        if (islog) {
-                               bias = VDEV_ALLOC_CLASS_LOGS;
+                               bias = (char *)VDEV_ALLOC_CLASS_LOGS;
                        } else {
                                (void) nvlist_lookup_string(newchild[c],
                                    ZPOOL_CONFIG_ALLOCATION_BIAS, &bias);
@@ -4793,7 +4874,8 @@ children:
 
                        if (!printed) {
                                if ((!(cb->cb_flags & IOS_ANYHISTO_M)) &&
-                                   !cb->cb_scripted && !cb->cb_vdev_names) {
+                                   !cb->cb_scripted &&
+                                   !cb->cb_vdevs.cb_names) {
                                        print_iostat_dashes(cb, 0,
                                            class_name[n]);
                                }
@@ -4802,7 +4884,7 @@ children:
                        }
 
                        vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
-                           cb->cb_name_flags);
+                           cb->cb_vdevs.cb_name_flags | VDEV_NAME_TYPE_ID);
                        ret += print_vdev_stats(zhp, vname, oldnv ?
                            oldchild[c] : NULL, newchild[c], cb, depth + 2);
                        free(vname);
@@ -4826,14 +4908,14 @@ children:
 
        if (children > 0) {
                if ((!(cb->cb_flags & IOS_ANYHISTO_M)) && !cb->cb_scripted &&
-                   !cb->cb_vdev_names) {
+                   !cb->cb_vdevs.cb_names) {
                        print_iostat_dashes(cb, 0, "cache");
                }
                printf("\n");
 
                for (c = 0; c < children; c++) {
                        vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
-                           cb->cb_name_flags);
+                           cb->cb_vdevs.cb_name_flags);
                        ret += print_vdev_stats(zhp, vname, oldnv ? oldchild[c]
                            : NULL, newchild[c], cb, depth + 2);
                        free(vname);
@@ -4889,7 +4971,8 @@ print_iostat(zpool_handle_t *zhp, void *data)
        ret = print_vdev_stats(zhp, zpool_get_name(zhp), oldnvroot, newnvroot,
            cb, 0);
        if ((ret != 0) && !(cb->cb_flags & IOS_ANYHISTO_M) &&
-           !cb->cb_scripted && cb->cb_verbose && !cb->cb_vdev_names_count) {
+           !cb->cb_scripted && cb->cb_verbose &&
+           !cb->cb_vdevs.cb_names_count) {
                print_iostat_separator(cb);
                if (cb->vcdl != NULL) {
                        print_cmd_columns(cb->vcdl, 1);
@@ -4931,7 +5014,7 @@ get_namewidth(zpool_handle_t *zhp, int min_width, int flags, boolean_t verbose)
        if ((config = zpool_get_config(zhp, NULL)) != NULL) {
                verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
                    &nvroot) == 0);
-               unsigned int poolname_len = strlen(zpool_get_name(zhp));
+               size_t poolname_len = strlen(zpool_get_name(zhp));
                if (verbose == B_FALSE) {
                        width = MAX(poolname_len, min_width);
                } else {
@@ -4965,8 +5048,8 @@ get_interval_count(int *argcp, char **argv, float *iv,
 
                if (*end == '\0' && errno == 0) {
                        if (interval == 0) {
-                               (void) fprintf(stderr, gettext("interval "
-                                   "cannot be zero\n"));
+                               (void) fprintf(stderr, gettext(
+                                   "interval cannot be zero\n"));
                                usage(B_FALSE);
                        }
                        /*
@@ -4996,8 +5079,8 @@ get_interval_count(int *argcp, char **argv, float *iv,
 
                if (*end == '\0' && errno == 0) {
                        if (interval == 0) {
-                               (void) fprintf(stderr, gettext("interval "
-                                   "cannot be zero\n"));
+                               (void) fprintf(stderr, gettext(
+                                   "interval cannot be zero\n"));
                                usage(B_FALSE);
                        }
 
@@ -5096,26 +5179,30 @@ get_stat_flags(zpool_list_t *list)
 }
 
 /*
- * Return 1 if cb_data->cb_vdev_names[0] is this vdev's name, 0 otherwise.
+ * Return 1 if cb_data->cb_names[0] is this vdev's name, 0 otherwise.
  */
 static int
-is_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_data)
+is_vdev_cb(void *zhp_data, nvlist_t *nv, void *cb_data)
 {
-       iostat_cbdata_t *cb = cb_data;
+       vdev_cbdata_t *cb = cb_data;
        char *name = NULL;
-       int ret = 0;
+       int ret = 1; /* assume match */
+       zpool_handle_t *zhp = zhp_data;
 
        name = zpool_vdev_name(g_zfs, zhp, nv, cb->cb_name_flags);
 
-       if (strcmp(name, cb->cb_vdev_names[0]) == 0)
-               ret = 1; /* match */
+       if (strcmp(name, cb->cb_names[0])) {
+               free(name);
+               name = zpool_vdev_name(g_zfs, zhp, nv, VDEV_NAME_GUID);
+               ret = (strcmp(name, cb->cb_names[0]) == 0);
+       }
        free(name);
 
        return (ret);
 }
 
 /*
- * Returns 1 if cb_data->cb_vdev_names[0] is a vdev name, 0 otherwise.
+ * Returns 1 if cb_data->cb_names[0] is a vdev name, 0 otherwise.
  */
 static int
 is_vdev(zpool_handle_t *zhp, void *cb_data)
@@ -5131,7 +5218,7 @@ is_vdev(zpool_handle_t *zhp, void *cb_data)
  */
 static int
 are_vdevs_in_pool(int argc, char **argv, char *pool_name,
-    iostat_cbdata_t *cb)
+    vdev_cbdata_t *cb)
 {
        char **tmp_name;
        int ret = 0;
@@ -5144,23 +5231,23 @@ are_vdevs_in_pool(int argc, char **argv, char *pool_name,
        if (pool_name)
                pool_count = 1;
 
-       /* Temporarily hijack cb_vdev_names for a second... */
-       tmp_name = cb->cb_vdev_names;
+       /* Temporarily hijack cb_names for a second... */
+       tmp_name = cb->cb_names;
 
        /* Go though our list of prospective vdev names */
        for (i = 0; i < argc; i++) {
-               cb->cb_vdev_names = argv + i;
+               cb->cb_names = argv + i;
 
                /* Is this name a vdev in our pools? */
                ret = for_each_pool(pool_count, &pool_name, B_TRUE, NULL,
-                   B_FALSE, is_vdev, cb);
+                   ZFS_TYPE_POOL, B_FALSE, is_vdev, cb);
                if (!ret) {
                        /* No match */
                        break;
                }
        }
 
-       cb->cb_vdev_names = tmp_name;
+       cb->cb_names = tmp_name;
 
        return (ret);
 }
@@ -5181,8 +5268,8 @@ is_pool_cb(zpool_handle_t *zhp, void *data)
 static int
 is_pool(char *name)
 {
-       return (for_each_pool(0, NULL, B_TRUE, NULL, B_FALSE, is_pool_cb,
-           name));
+       return (for_each_pool(0, NULL, B_TRUE, NULL, ZFS_TYPE_POOL, B_FALSE,
+           is_pool_cb, name));
 }
 
 /* Are all our argv[] strings pool names?  If so return 1, 0 otherwise. */
@@ -5205,7 +5292,7 @@ are_all_pools(int argc, char **argv)
  */
 static void
 error_list_unresolved_vdevs(int argc, char **argv, char *pool_name,
-    iostat_cbdata_t *cb)
+    vdev_cbdata_t *cb)
 {
        int i;
        char *name;
@@ -5229,7 +5316,7 @@ error_list_unresolved_vdevs(int argc, char **argv, char *pool_name,
 /*
  * Same as get_interval_count(), but with additional checks to not misinterpret
  * guids as interval/count values.  Assumes VDEV_NAME_GUID is set in
- * cb.cb_name_flags.
+ * cb.cb_vdevs.cb_name_flags.
  */
 static void
 get_interval_count_filter_guids(int *argc, char **argv, float *interval,
@@ -5239,7 +5326,8 @@ get_interval_count_filter_guids(int *argc, char **argv, float *interval,
        int argc_for_interval = 0;
 
        /* Is the last arg an interval value?  Or a guid? */
-       if (*argc >= 1 && !are_vdevs_in_pool(1, &argv[*argc - 1], NULL, cb)) {
+       if (*argc >= 1 && !are_vdevs_in_pool(1, &argv[*argc - 1], NULL,
+           &cb->cb_vdevs)) {
                /*
                 * The last arg is not a guid, so it's probably an
                 * interval value.
@@ -5247,7 +5335,8 @@ get_interval_count_filter_guids(int *argc, char **argv, float *interval,
                argc_for_interval++;
 
                if (*argc >= 2 &&
-                   !are_vdevs_in_pool(1, &argv[*argc - 2], NULL, cb)) {
+                   !are_vdevs_in_pool(1, &argv[*argc - 2], NULL,
+                   &cb->cb_vdevs)) {
                        /*
                         * The 2nd to last arg is not a guid, so it's probably
                         * an interval value.
@@ -5305,7 +5394,7 @@ terminal_height(void)
 static void
 print_zpool_script_help(char *name, char *path)
 {
-       char *argv[] = {path, "-h", NULL};
+       char *argv[] = {path, (char *)"-h", NULL};
        char **lines = NULL;
        int lines_cnt = 0;
        int rc;
@@ -5357,9 +5446,9 @@ print_zpool_dir_scripts(char *dirpath)
  * Print out help text for all zpool status/iostat -c scripts.
  */
 static void
-print_zpool_script_list(char *subcommand)
+print_zpool_script_list(const char *subcommand)
 {
-       char *dir, *sp;
+       char *dir, *sp, *tmp;
 
        printf(gettext("Available 'zpool %s -c' commands:\n"), subcommand);
 
@@ -5367,11 +5456,10 @@ print_zpool_script_list(char *subcommand)
        if (sp == NULL)
                return;
 
-       dir = strtok(sp, ":");
-       while (dir != NULL) {
+       for (dir = strtok_r(sp, ":", &tmp);
+           dir != NULL;
+           dir = strtok_r(NULL, ":", &tmp))
                print_zpool_dir_scripts(dir);
-               dir = strtok(NULL, ":");
-       }
 
        free(sp);
 }
@@ -5391,8 +5479,8 @@ get_namewidth_iostat(zpool_handle_t *zhp, void *data)
         * get_namewidth() returns the maximum width of any name in that column
         * for any pool/vdev/device line that will be output.
         */
-       width = get_namewidth(zhp, cb->cb_namewidth, cb->cb_name_flags,
-           cb->cb_verbose);
+       width = get_namewidth(zhp, cb->cb_namewidth,
+           cb->cb_vdevs.cb_name_flags | VDEV_NAME_TYPE_ID, cb->cb_verbose);
 
        /*
         * The width we are calculating is the width of the header and also the
@@ -5569,11 +5657,11 @@ zpool_do_iostat(int argc, char **argv)
        cb.cb_scripted = scripted;
 
        if (guid)
-               cb.cb_name_flags |= VDEV_NAME_GUID;
+               cb.cb_vdevs.cb_name_flags |= VDEV_NAME_GUID;
        if (follow_links)
-               cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS;
+               cb.cb_vdevs.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS;
        if (full_name)
-               cb.cb_name_flags |= VDEV_NAME_PATH;
+               cb.cb_vdevs.cb_name_flags |= VDEV_NAME_PATH;
        cb.cb_iteration = 0;
        cb.cb_namewidth = 0;
        cb.cb_verbose = verbose;
@@ -5590,17 +5678,18 @@ zpool_do_iostat(int argc, char **argv)
                /* No args, so just print the defaults. */
        } else if (are_all_pools(argc, argv)) {
                /* All the args are pool names */
-       } else if (are_vdevs_in_pool(argc, argv, NULL, &cb)) {
+       } else if (are_vdevs_in_pool(argc, argv, NULL, &cb.cb_vdevs)) {
                /* All the args are vdevs */
-               cb.cb_vdev_names = argv;
-               cb.cb_vdev_names_count = argc;
+               cb.cb_vdevs.cb_names = argv;
+               cb.cb_vdevs.cb_names_count = argc;
                argc = 0; /* No pools to process */
        } else if (are_all_pools(1, argv)) {
                /* The first arg is a pool name */
-               if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0], &cb)) {
+               if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0],
+                   &cb.cb_vdevs)) {
                        /* ...and the rest are vdev names */
-                       cb.cb_vdev_names = argv + 1;
-                       cb.cb_vdev_names_count = argc - 1;
+                       cb.cb_vdevs.cb_names = argv + 1;
+                       cb.cb_vdevs.cb_names_count = argc - 1;
                        argc = 1; /* One pool to process */
                } else {
                        fprintf(stderr, gettext("Expected either a list of "));
@@ -5608,7 +5697,7 @@ zpool_do_iostat(int argc, char **argv)
                        fprintf(stderr, " \"%s\", ", argv[0]);
                        fprintf(stderr, gettext("but got:\n"));
                        error_list_unresolved_vdevs(argc - 1, argv + 1,
-                           argv[0], &cb);
+                           argv[0], &cb.cb_vdevs);
                        fprintf(stderr, "\n");
                        usage(B_FALSE);
                        return (1);
@@ -5623,7 +5712,7 @@ zpool_do_iostat(int argc, char **argv)
                return (1);
        }
 
-       if (cb.cb_vdev_names_count != 0) {
+       if (cb.cb_vdevs.cb_names_count != 0) {
                /*
                 * If user specified vdevs, it implies verbose.
                 */
@@ -5634,7 +5723,8 @@ zpool_do_iostat(int argc, char **argv)
         * Construct the list of all interesting pools.
         */
        ret = 0;
-       if ((list = pool_list_get(argc, argv, NULL, parsable, &ret)) == NULL)
+       if ((list = pool_list_get(argc, argv, NULL, ZFS_TYPE_POOL, parsable,
+           &ret)) == NULL)
                return (1);
 
        if (pool_list_count(list) == 0 && argc != 0) {
@@ -5742,8 +5832,9 @@ zpool_do_iostat(int argc, char **argv)
                        if (cmd != NULL && cb.cb_verbose &&
                            !(cb.cb_flags & IOS_ANYHISTO_M)) {
                                cb.vcdl = all_pools_for_each_vdev_run(argc,
-                                   argv, cmd, g_zfs, cb.cb_vdev_names,
-                                   cb.cb_vdev_names_count, cb.cb_name_flags);
+                                   argv, cmd, g_zfs, cb.cb_vdevs.cb_names,
+                                   cb.cb_vdevs.cb_names_count,
+                                   cb.cb_vdevs.cb_name_flags);
                        } else {
                                cb.vcdl = NULL;
                        }
@@ -5795,7 +5886,7 @@ zpool_do_iostat(int argc, char **argv)
                        if (((npools > 1 && !verbose &&
                            !(cb.cb_flags & IOS_ANYHISTO_M)) ||
                            (!(cb.cb_flags & IOS_ANYHISTO_M) &&
-                           cb.cb_vdev_names_count)) &&
+                           cb.cb_vdevs.cb_names_count)) &&
                            !cb.cb_scripted) {
                                print_iostat_separator(&cb);
                                if (cb.vcdl != NULL)
@@ -5862,12 +5953,12 @@ print_header(list_cbdata_t *cb)
                }
 
                if (!first)
-                       (void) printf("  ");
+                       (void) fputs("  ", stdout);
                else
                        first = B_FALSE;
 
                right_justify = B_FALSE;
-               if (pl->pl_prop != ZPROP_INVAL) {
+               if (pl->pl_prop != ZPROP_USERPROP) {
                        header = zpool_prop_column_name(pl->pl_prop);
                        right_justify = zpool_prop_align_right(pl->pl_prop);
                } else {
@@ -5880,14 +5971,14 @@ print_header(list_cbdata_t *cb)
                }
 
                if (pl->pl_next == NULL && !right_justify)
-                       (void) printf("%s", header);
+                       (void) fputs(header, stdout);
                else if (right_justify)
                        (void) printf("%*s", (int)width, header);
                else
                        (void) printf("%-*s", (int)width, header);
        }
 
-       (void) printf("\n");
+       (void) fputc('\n', stdout);
 }
 
 /*
@@ -5900,7 +5991,7 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
        zprop_list_t *pl = cb->cb_proplist;
        boolean_t first = B_TRUE;
        char property[ZPOOL_MAXPROPLEN];
-       char *propstr;
+       const char *propstr;
        boolean_t right_justify;
        size_t width;
 
@@ -5917,15 +6008,15 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
 
                if (!first) {
                        if (cb->cb_scripted)
-                               (void) printf("\t");
+                               (void) fputc('\t', stdout);
                        else
-                               (void) printf("  ");
+                               (void) fputs("  ", stdout);
                } else {
                        first = B_FALSE;
                }
 
                right_justify = B_FALSE;
-               if (pl->pl_prop != ZPROP_INVAL) {
+               if (pl->pl_prop != ZPROP_USERPROP) {
                        if (zpool_get_prop(zhp, pl->pl_prop, property,
                            sizeof (property), NULL, cb->cb_literal) != 0)
                                propstr = "-";
@@ -5949,14 +6040,14 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
                 * format specifier.
                 */
                if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
-                       (void) printf("%s", propstr);
+                       (void) fputs(propstr, stdout);
                else if (right_justify)
                        (void) printf("%*s", (int)width, propstr);
                else
                        (void) printf("%-*s", (int)width, propstr);
        }
 
-       (void) printf("\n");
+       (void) fputc('\n', stdout);
 }
 
 static void
@@ -5968,6 +6059,7 @@ print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
        size_t width = zprop_width(prop, &fixed, ZFS_TYPE_POOL);
 
        switch (prop) {
+       case ZPOOL_PROP_SIZE:
        case ZPOOL_PROP_EXPANDSZ:
        case ZPOOL_PROP_CHECKPOINT:
        case ZPOOL_PROP_DEDUPRATIO:
@@ -6000,7 +6092,7 @@ print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
                break;
        case ZPOOL_PROP_HEALTH:
                width = 8;
-               snprintf(propval, sizeof (propval), "%-*s", (int)width, str);
+               (void) strlcpy(propval, str, sizeof (propval));
                break;
        default:
                zfs_nicenum_format(value, propval, sizeof (propval), format);
@@ -6029,8 +6121,8 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
        char *vname;
        boolean_t scripted = cb->cb_scripted;
        uint64_t islog = B_FALSE;
-       char *dashes = "%-*s      -      -      -        -         "
-           "-      -      -      -  -\n";
+       const char *dashes = "%-*s      -      -      -        -         "
+           "-      -      -      -         -\n";
 
        verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
            (uint64_t **)&vs, &c) == 0);
@@ -6063,8 +6155,12 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
                 * 'toplevel' boolean value is passed to the print_one_column()
                 * to indicate that the value is valid.
                 */
-               print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, NULL, scripted,
-                   toplevel, format);
+               if (VDEV_STAT_VALID(vs_pspace, c) && vs->vs_pspace)
+                       print_one_column(ZPOOL_PROP_SIZE, vs->vs_pspace, NULL,
+                           scripted, B_TRUE, format);
+               else
+                       print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, NULL,
+                           scripted, toplevel, format);
                print_one_column(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, NULL,
                    scripted, toplevel, format);
                print_one_column(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc,
@@ -6092,7 +6188,7 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
                }
                print_one_column(ZPOOL_PROP_HEALTH, 0, state, scripted,
                    B_TRUE, format);
-               (void) printf("\n");
+               (void) fputc('\n', stdout);
        }
 
        if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
@@ -6115,13 +6211,13 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
                        continue;
 
                vname = zpool_vdev_name(g_zfs, zhp, child[c],
-                   cb->cb_name_flags);
+                   cb->cb_name_flags | VDEV_NAME_TYPE_ID);
                print_list_stats(zhp, vname, child[c], cb, depth + 2, B_FALSE);
                free(vname);
        }
 
        /* list the classes: 'logs', 'dedup', and 'special' */
-       for (uint_t n = 0; n < 3; n++) {
+       for (uint_t n = 0; n < ARRAY_SIZE(class_name); n++) {
                boolean_t printed = B_FALSE;
 
                for (c = 0; c < children; c++) {
@@ -6130,7 +6226,7 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
 
                        if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
                            &islog) == 0 && islog) {
-                               bias = VDEV_ALLOC_CLASS_LOGS;
+                               bias = (char *)VDEV_ALLOC_CLASS_LOGS;
                        } else {
                                (void) nvlist_lookup_string(child[c],
                                    ZPOOL_CONFIG_ALLOCATION_BIAS, &bias);
@@ -6149,7 +6245,7 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
                                printed = B_TRUE;
                        }
                        vname = zpool_vdev_name(g_zfs, zhp, child[c],
-                           cb->cb_name_flags);
+                           cb->cb_name_flags | VDEV_NAME_TYPE_ID);
                        print_list_stats(zhp, vname, child[c], cb, depth + 2,
                            B_FALSE);
                        free(vname);
@@ -6215,8 +6311,8 @@ get_namewidth_list(zpool_handle_t *zhp, void *data)
        list_cbdata_t *cb = data;
        int width;
 
-       width = get_namewidth(zhp, cb->cb_namewidth, cb->cb_name_flags,
-           cb->cb_verbose);
+       width = get_namewidth(zhp, cb->cb_namewidth,
+           cb->cb_name_flags | VDEV_NAME_TYPE_ID, cb->cb_verbose);
 
        if (width < 9)
                width = 9;
@@ -6257,6 +6353,7 @@ zpool_do_list(int argc, char **argv)
        unsigned long count = 0;
        zpool_list_t *list;
        boolean_t first = B_TRUE;
+       current_prop_type = ZFS_TYPE_POOL;
 
        /* check options */
        while ((c = getopt(argc, argv, ":gHLo:pPT:v")) != -1) {
@@ -6308,7 +6405,7 @@ zpool_do_list(int argc, char **argv)
 
        for (;;) {
                if ((list = pool_list_get(argc, argv, &cb.cb_proplist,
-                   cb.cb_literal, &ret)) == NULL)
+                   ZFS_TYPE_POOL, cb.cb_literal, &ret)) == NULL)
                        return (1);
 
                if (pool_list_count(list) == 0)
@@ -6490,7 +6587,6 @@ zpool_do_attach_or_replace(int argc, char **argv, int replacing)
  *
  * Replace <device> with <new_device>.
  */
-/* ARGSUSED */
 int
 zpool_do_replace(int argc, char **argv)
 {
@@ -6526,7 +6622,6 @@ zpool_do_attach(int argc, char **argv)
  * is the last device in the mirror, or if the DTLs indicate that this device
  * has the only valid copy of some data.
  */
-/* ARGSUSED */
 int
 zpool_do_detach(int argc, char **argv)
 {
@@ -6833,7 +6928,6 @@ zpool_do_online(int argc, char **argv)
  *     -t      Only take the device off-line temporarily.  The offline/faulted
  *             state will not be persistent across reboots.
  */
-/* ARGSUSED */
 int
 zpool_do_offline(int argc, char **argv)
 {
@@ -7064,8 +7158,8 @@ zpool_do_reopen(int argc, char **argv)
        argv += optind;
 
        /* if argc == 0 we will execute zpool_reopen_one on all pools */
-       ret = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, zpool_reopen_one,
-           &scrub_restart);
+       ret = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
+           B_FALSE, zpool_reopen_one, &scrub_restart);
 
        return (ret);
 }
@@ -7194,13 +7288,13 @@ zpool_do_scrub(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       error = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE,
-           scrub_callback, &cb);
+       error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
+           B_FALSE, scrub_callback, &cb);
 
        if (wait && !error) {
                zpool_wait_activity_t act = ZPOOL_WAIT_SCRUB;
-               error = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE,
-                   wait_callback, &act);
+               error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
+                   B_FALSE, wait_callback, &act);
        }
 
        return (error);
@@ -7238,8 +7332,8 @@ zpool_do_resilver(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       return (for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE,
-           scrub_callback, &cb));
+       return (for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
+           B_FALSE, scrub_callback, &cb));
 }
 
 /*
@@ -7296,9 +7390,10 @@ zpool_do_trim(int argc, char **argv)
                                    "combined with the -c or -s options\n"));
                                usage(B_FALSE);
                        }
-                       if (zfs_nicestrtonum(NULL, optarg, &rate) == -1) {
-                               (void) fprintf(stderr,
-                                   gettext("invalid value for rate\n"));
+                       if (zfs_nicestrtonum(g_zfs, optarg, &rate) == -1) {
+                               (void) fprintf(stderr, "%s: %s\n",
+                                   gettext("invalid value for rate"),
+                                   libzfs_error_description(g_zfs));
                                usage(B_FALSE);
                        }
                        break;
@@ -8035,7 +8130,7 @@ status_callback(zpool_handle_t *zhp, void *data)
 {
        status_cbdata_t *cbp = data;
        nvlist_t *config, *nvroot;
-       char *msgid;
+       const char *msgid;
        zpool_status_t reason;
        zpool_errata_t errata;
        const char *health;
@@ -8055,7 +8150,8 @@ status_callback(zpool_handle_t *zhp, void *data)
            (reason == ZPOOL_STATUS_OK ||
            reason == ZPOOL_STATUS_VERSION_OLDER ||
            reason == ZPOOL_STATUS_FEAT_DISABLED ||
-           reason == ZPOOL_STATUS_COMPATIBILITY_ERR)) {
+           reason == ZPOOL_STATUS_COMPATIBILITY_ERR ||
+           reason == ZPOOL_STATUS_INCOMPATIBLE_FEAT)) {
                if (!cbp->cb_allpools) {
                        (void) printf(gettext("pool '%s' is healthy\n"),
                            zpool_get_name(zhp));
@@ -8079,12 +8175,12 @@ status_callback(zpool_handle_t *zhp, void *data)
        printf("  ");
        printf_color(ANSI_BOLD, gettext("pool:"));
        printf(" %s\n", zpool_get_name(zhp));
-       printf(" ");
+       fputc(' ', stdout);
        printf_color(ANSI_BOLD, gettext("state: "));
 
        printf_color(health_str_to_color(health), "%s", health);
 
-       printf("\n");
+       fputc('\n', stdout);
 
        switch (reason) {
        case ZPOOL_STATUS_MISSING_DEV_R:
@@ -8238,7 +8334,7 @@ status_callback(zpool_handle_t *zhp, void *data)
                printf_color(ANSI_YELLOW, gettext("Enable all features using "
                    "'zpool upgrade'. Once this is done,\n\tthe pool may no "
                    "longer be accessible by software that does not support\n\t"
-                   "the features. See zpool-features(5) for details.\n"));
+                   "the features. See zpool-features(7) for details.\n"));
                break;
 
        case ZPOOL_STATUS_COMPATIBILITY_ERR:
@@ -8254,6 +8350,18 @@ status_callback(zpool_handle_t *zhp, void *data)
                    ZPOOL_DATA_COMPAT_D ".\n"));
                break;
 
+       case ZPOOL_STATUS_INCOMPATIBLE_FEAT:
+               printf_color(ANSI_BOLD, gettext("status: "));
+               printf_color(ANSI_YELLOW, gettext("One or more features "
+                   "are enabled on the pool despite not being\n\t"
+                   "requested by the 'compatibility' property.\n"));
+               printf_color(ANSI_BOLD, gettext("action: "));
+               printf_color(ANSI_YELLOW, gettext("Consider setting "
+                   "'compatibility' to an appropriate value, or\n\t"
+                   "adding needed features to the relevant file in\n\t"
+                   ZPOOL_SYSCONF_COMPAT_D " or " ZPOOL_DATA_COMPAT_D ".\n"));
+               break;
+
        case ZPOOL_STATUS_UNSUP_FEAT_READ:
                printf_color(ANSI_BOLD, gettext("status: "));
                printf_color(ANSI_YELLOW, gettext("The pool cannot be accessed "
@@ -8648,8 +8756,8 @@ zpool_do_status(int argc, char **argv)
                        cb.vcdl = all_pools_for_each_vdev_run(argc, argv, cmd,
                            NULL, NULL, 0, 0);
 
-               ret = for_each_pool(argc, argv, B_TRUE, NULL, cb.cb_literal,
-                   status_callback, &cb);
+               ret = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
+                   cb.cb_literal, status_callback, &cb);
 
                if (cb.vcdl != NULL)
                        free_vdev_cmd_data_list(cb.vcdl);
@@ -8713,6 +8821,11 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version)
        verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
            &oldversion) == 0);
 
+       char compat[ZFS_MAXPROPLEN];
+       if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY, compat,
+           ZFS_MAXPROPLEN, NULL, B_FALSE) != 0)
+               compat[0] = '\0';
+
        assert(SPA_VERSION_IS_SUPPORTED(oldversion));
        assert(oldversion < version);
 
@@ -8727,6 +8840,13 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version)
                return (1);
        }
 
+       if (strcmp(compat, ZPOOL_COMPAT_LEGACY) == 0) {
+               (void) fprintf(stderr, gettext("Upgrade not performed because "
+                   "'compatibility' property set to '"
+                   ZPOOL_COMPAT_LEGACY "'.\n"));
+               return (1);
+       }
+
        ret = zpool_upgrade(zhp, version);
        if (ret != 0)
                return (ret);
@@ -8803,7 +8923,7 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
        upgrade_cbdata_t *cbp = arg;
        nvlist_t *config;
        uint64_t version;
-       boolean_t printnl = B_FALSE;
+       boolean_t modified_pool = B_FALSE;
        int ret;
 
        config = zpool_get_config(zhp, NULL);
@@ -8817,7 +8937,7 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
                ret = upgrade_version(zhp, cbp->cb_version);
                if (ret != 0)
                        return (ret);
-               printnl = B_TRUE;
+               modified_pool = B_TRUE;
 
                /*
                 * If they did "zpool upgrade -a", then we could
@@ -8837,12 +8957,13 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
 
                if (count > 0) {
                        cbp->cb_first = B_FALSE;
-                       printnl = B_TRUE;
+                       modified_pool = B_TRUE;
                }
        }
 
-       if (printnl) {
-               (void) printf(gettext("\n"));
+       if (modified_pool) {
+               (void) printf("\n");
+               (void) after_zpool_upgrade(zhp);
        }
 
        return (0);
@@ -8868,7 +8989,10 @@ upgrade_list_older_cb(zpool_handle_t *zhp, void *arg)
                            "be upgraded to use feature flags.  After "
                            "being upgraded, these pools\nwill no "
                            "longer be accessible by software that does not "
-                           "support feature\nflags.\n\n"));
+                           "support feature\nflags.\n\n"
+                           "Note that setting a pool's 'compatibility' "
+                           "feature to '" ZPOOL_COMPAT_LEGACY "' will\n"
+                           "inhibit upgrades.\n\n"));
                        (void) printf(gettext("VER  POOL\n"));
                        (void) printf(gettext("---  ------------\n"));
                        cbp->cb_first = B_FALSE;
@@ -8913,8 +9037,12 @@ upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg)
                                            "pool may become incompatible with "
                                            "software\nthat does not support "
                                            "the feature. See "
-                                           "zpool-features(5) for "
-                                           "details.\n\n"));
+                                           "zpool-features(7) for "
+                                           "details.\n\n"
+                                           "Note that the pool "
+                                           "'compatibility' feature can be "
+                                           "used to inhibit\nfeature "
+                                           "upgrades.\n\n"));
                                        (void) printf(gettext("POOL  "
                                            "FEATURE\n"));
                                        (void) printf(gettext("------"
@@ -8944,11 +9072,10 @@ upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg)
        return (0);
 }
 
-/* ARGSUSED */
 static int
 upgrade_one(zpool_handle_t *zhp, void *data)
 {
-       boolean_t printnl = B_FALSE;
+       boolean_t modified_pool = B_FALSE;
        upgrade_cbdata_t *cbp = data;
        uint64_t cur_version;
        int ret;
@@ -8976,7 +9103,7 @@ upgrade_one(zpool_handle_t *zhp, void *data)
        }
 
        if (cur_version != cbp->cb_version) {
-               printnl = B_TRUE;
+               modified_pool = B_TRUE;
                ret = upgrade_version(zhp, cbp->cb_version);
                if (ret != 0)
                        return (ret);
@@ -8989,7 +9116,7 @@ upgrade_one(zpool_handle_t *zhp, void *data)
                        return (ret);
 
                if (count != 0) {
-                       printnl = B_TRUE;
+                       modified_pool = B_TRUE;
                } else if (cur_version == SPA_VERSION) {
                        (void) printf(gettext("Pool '%s' already has all "
                            "supported and requested features enabled.\n"),
@@ -8997,8 +9124,9 @@ upgrade_one(zpool_handle_t *zhp, void *data)
                }
        }
 
-       if (printnl) {
-               (void) printf(gettext("\n"));
+       if (modified_pool) {
+               (void) printf("\n");
+               (void) after_zpool_upgrade(zhp);
        }
 
        return (0);
@@ -9081,7 +9209,7 @@ zpool_do_upgrade(int argc, char **argv)
                }
        }
 
-       (void) printf(gettext("This system supports ZFS pool feature "
+       (void) printf("%s", gettext("This system supports ZFS pool feature "
            "flags.\n\n"));
        if (showversions) {
                int i;
@@ -9187,8 +9315,8 @@ zpool_do_upgrade(int argc, char **argv)
                        (void) printf(gettext("\n"));
                }
        } else {
-               ret = for_each_pool(argc, argv, B_FALSE, NULL, B_FALSE,
-                   upgrade_one, &cb);
+               ret = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL,
+                   B_FALSE, upgrade_one, &cb);
        }
 
        return (ret);
@@ -9384,8 +9512,8 @@ zpool_do_history(int argc, char **argv)
        argc -= optind;
        argv += optind;
 
-       ret = for_each_pool(argc, argv, B_FALSE, NULL, B_FALSE, get_history_one,
-           &cbdata);
+       ret = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL,
+           B_FALSE, get_history_one, &cbdata);
 
        if (argc == 0 && cbdata.first == B_TRUE) {
                (void) fprintf(stderr, gettext("no pools available\n"));
@@ -9708,7 +9836,7 @@ zpool_do_events_next(ev_opts_t *opts)
 }
 
 static int
-zpool_do_events_clear(ev_opts_t *opts)
+zpool_do_events_clear(void)
 {
        int count, ret;
 
@@ -9775,7 +9903,7 @@ zpool_do_events(int argc, char **argv)
        }
 
        if (opts.clear)
-               ret = zpool_do_events_clear(&opts);
+               ret = zpool_do_events_clear();
        else
                ret = zpool_do_events_next(&opts);
 
@@ -9783,44 +9911,135 @@ zpool_do_events(int argc, char **argv)
 }
 
 static int
-get_callback(zpool_handle_t *zhp, void *data)
+get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data)
 {
        zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
-       char value[MAXNAMELEN];
+       char value[ZFS_MAXPROPLEN];
        zprop_source_t srctype;
-       zprop_list_t *pl;
-
-       for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
 
+       for (zprop_list_t *pl = cbp->cb_proplist; pl != NULL;
+           pl = pl->pl_next) {
+               char *prop_name;
                /*
-                * Skip the special fake placeholder. This will also skip
+                * If the first property is pool name, it is a special
+                * placeholder that we can skip. This will also skip
                 * over the name property when 'all' is specified.
                 */
                if (pl->pl_prop == ZPOOL_PROP_NAME &&
                    pl == cbp->cb_proplist)
                        continue;
 
-               if (pl->pl_prop == ZPROP_INVAL &&
-                   (zpool_prop_feature(pl->pl_user_prop) ||
-                   zpool_prop_unsupported(pl->pl_user_prop))) {
-                       srctype = ZPROP_SRC_LOCAL;
+               if (pl->pl_prop == ZPROP_INVAL) {
+                       prop_name = pl->pl_user_prop;
+               } else {
+                       prop_name = (char *)vdev_prop_to_name(pl->pl_prop);
+               }
+               if (zpool_get_vdev_prop(zhp, vdevname, pl->pl_prop,
+                   prop_name, value, sizeof (value), &srctype,
+                   cbp->cb_literal) == 0) {
+                       zprop_print_one_property(vdevname, cbp, prop_name,
+                           value, srctype, NULL, NULL);
+               }
+       }
 
-                       if (zpool_prop_get_feature(zhp, pl->pl_user_prop,
-                           value, sizeof (value)) == 0) {
-                               zprop_print_one_property(zpool_get_name(zhp),
-                                   cbp, pl->pl_user_prop, value, srctype,
-                                   NULL, NULL);
-                       }
+       return (0);
+}
+
+static int
+get_callback_vdev_width_cb(void *zhp_data, nvlist_t *nv, void *data)
+{
+       zpool_handle_t *zhp = zhp_data;
+       zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
+       char *vdevname = zpool_vdev_name(g_zfs, zhp, nv,
+           cbp->cb_vdevs.cb_name_flags);
+       int ret;
+
+       /* Adjust the column widths for the vdev properties */
+       ret = vdev_expand_proplist(zhp, vdevname, &cbp->cb_proplist);
+
+       return (ret);
+}
+
+static int
+get_callback_vdev_cb(void *zhp_data, nvlist_t *nv, void *data)
+{
+       zpool_handle_t *zhp = zhp_data;
+       zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
+       char *vdevname = zpool_vdev_name(g_zfs, zhp, nv,
+           cbp->cb_vdevs.cb_name_flags);
+       int ret;
+
+       /* Display the properties */
+       ret = get_callback_vdev(zhp, vdevname, data);
+
+       return (ret);
+}
+
+static int
+get_callback(zpool_handle_t *zhp, void *data)
+{
+       zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
+       char value[MAXNAMELEN];
+       zprop_source_t srctype;
+       zprop_list_t *pl;
+       int vid;
+
+       if (cbp->cb_type == ZFS_TYPE_VDEV) {
+               if (strcmp(cbp->cb_vdevs.cb_names[0], "all-vdevs") == 0) {
+                       for_each_vdev(zhp, get_callback_vdev_width_cb, data);
+                       for_each_vdev(zhp, get_callback_vdev_cb, data);
                } else {
-                       if (zpool_get_prop(zhp, pl->pl_prop, value,
-                           sizeof (value), &srctype, cbp->cb_literal) != 0)
+                       /* Adjust column widths for vdev properties */
+                       for (vid = 0; vid < cbp->cb_vdevs.cb_names_count;
+                           vid++) {
+                               vdev_expand_proplist(zhp,
+                                   cbp->cb_vdevs.cb_names[vid],
+                                   &cbp->cb_proplist);
+                       }
+                       /* Display the properties */
+                       for (vid = 0; vid < cbp->cb_vdevs.cb_names_count;
+                           vid++) {
+                               get_callback_vdev(zhp,
+                                   cbp->cb_vdevs.cb_names[vid], data);
+                       }
+               }
+       } else {
+               assert(cbp->cb_type == ZFS_TYPE_POOL);
+               for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
+                       /*
+                        * Skip the special fake placeholder. This will also
+                        * skip over the name property when 'all' is specified.
+                        */
+                       if (pl->pl_prop == ZPOOL_PROP_NAME &&
+                           pl == cbp->cb_proplist)
                                continue;
 
-                       zprop_print_one_property(zpool_get_name(zhp), cbp,
-                           zpool_prop_to_name(pl->pl_prop), value, srctype,
-                           NULL, NULL);
+                       if (pl->pl_prop == ZPROP_INVAL &&
+                           (zpool_prop_feature(pl->pl_user_prop) ||
+                           zpool_prop_unsupported(pl->pl_user_prop))) {
+                               srctype = ZPROP_SRC_LOCAL;
+
+                               if (zpool_prop_get_feature(zhp,
+                                   pl->pl_user_prop, value,
+                                   sizeof (value)) == 0) {
+                                       zprop_print_one_property(
+                                           zpool_get_name(zhp), cbp,
+                                           pl->pl_user_prop, value, srctype,
+                                           NULL, NULL);
+                               }
+                       } else {
+                               if (zpool_get_prop(zhp, pl->pl_prop, value,
+                                   sizeof (value), &srctype,
+                                   cbp->cb_literal) != 0)
+                                       continue;
+
+                               zprop_print_one_property(zpool_get_name(zhp),
+                                   cbp, zpool_prop_to_name(pl->pl_prop),
+                                   value, srctype, NULL, NULL);
+                       }
                }
        }
+
        return (0);
 }
 
@@ -9843,7 +10062,7 @@ zpool_do_get(int argc, char **argv)
        zprop_list_t fake_name = { 0 };
        int ret;
        int c, i;
-       char *value;
+       char *propstr = NULL;
 
        cb.cb_first = B_TRUE;
 
@@ -9856,6 +10075,8 @@ zpool_do_get(int argc, char **argv)
        cb.cb_columns[2] = GET_COL_VALUE;
        cb.cb_columns[3] = GET_COL_SOURCE;
        cb.cb_type = ZFS_TYPE_POOL;
+       cb.cb_vdevs.cb_name_flags |= VDEV_NAME_TYPE_ID;
+       current_prop_type = cb.cb_type;
 
        /* check options */
        while ((c = getopt(argc, argv, ":Hpo:")) != -1) {
@@ -9867,35 +10088,34 @@ zpool_do_get(int argc, char **argv)
                        cb.cb_scripted = B_TRUE;
                        break;
                case 'o':
-                       bzero(&cb.cb_columns, sizeof (cb.cb_columns));
+                       memset(&cb.cb_columns, 0, sizeof (cb.cb_columns));
                        i = 0;
-                       while (*optarg != '\0') {
-                               static char *col_subopts[] =
+
+                       for (char *tok; (tok = strsep(&optarg, ",")); ) {
+                               static const char *const col_opts[] =
                                { "name", "property", "value", "source",
-                               "all", NULL };
+                                   "all" };
+                               static const zfs_get_column_t col_cols[] =
+                               { GET_COL_NAME, GET_COL_PROPERTY, GET_COL_VALUE,
+                                   GET_COL_SOURCE };
 
-                               if (i == ZFS_GET_NCOLS) {
+                               if (i == ZFS_GET_NCOLS - 1) {
                                        (void) fprintf(stderr, gettext("too "
                                        "many fields given to -o "
                                        "option\n"));
                                        usage(B_FALSE);
                                }
 
-                               switch (getsubopt(&optarg, col_subopts,
-                                   &value)) {
-                               case 0:
-                                       cb.cb_columns[i++] = GET_COL_NAME;
-                                       break;
-                               case 1:
-                                       cb.cb_columns[i++] = GET_COL_PROPERTY;
-                                       break;
-                               case 2:
-                                       cb.cb_columns[i++] = GET_COL_VALUE;
-                                       break;
-                               case 3:
-                                       cb.cb_columns[i++] = GET_COL_SOURCE;
-                                       break;
-                               case 4:
+                               for (c = 0; c < ARRAY_SIZE(col_opts); ++c)
+                                       if (strcmp(tok, col_opts[c]) == 0)
+                                               goto found;
+
+                               (void) fprintf(stderr,
+                                   gettext("invalid column name '%s'\n"), tok);
+                               usage(B_FALSE);
+
+found:
+                               if (c >= 4) {
                                        if (i > 0) {
                                                (void) fprintf(stderr,
                                                    gettext("\"all\" conflicts "
@@ -9903,18 +10123,12 @@ zpool_do_get(int argc, char **argv)
                                                    "given to -o option\n"));
                                                usage(B_FALSE);
                                        }
-                                       cb.cb_columns[0] = GET_COL_NAME;
-                                       cb.cb_columns[1] = GET_COL_PROPERTY;
-                                       cb.cb_columns[2] = GET_COL_VALUE;
-                                       cb.cb_columns[3] = GET_COL_SOURCE;
-                                       i = ZFS_GET_NCOLS;
-                                       break;
-                               default:
-                                       (void) fprintf(stderr,
-                                           gettext("invalid column name "
-                                           "'%s'\n"), value);
-                                       usage(B_FALSE);
-                               }
+
+                                       memcpy(cb.cb_columns, col_cols,
+                                           sizeof (col_cols));
+                                       i = ZFS_GET_NCOLS - 1;
+                               } else
+                                       cb.cb_columns[i++] = col_cols[c];
                        }
                        break;
                case '?':
@@ -9933,13 +10147,52 @@ zpool_do_get(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       if (zprop_get_list(g_zfs, argv[0], &cb.cb_proplist,
-           ZFS_TYPE_POOL) != 0)
-               usage(B_FALSE);
+       /* Properties list is needed later by zprop_get_list() */
+       propstr = argv[0];
 
        argc--;
        argv++;
 
+       if (argc == 0) {
+               /* No args, so just print the defaults. */
+       } else if (are_all_pools(argc, argv)) {
+               /* All the args are pool names */
+       } else if (are_all_pools(1, argv)) {
+               /* The first arg is a pool name */
+               if ((argc == 2 && strcmp(argv[1], "all-vdevs") == 0) ||
+                   are_vdevs_in_pool(argc - 1, argv + 1, argv[0],
+                   &cb.cb_vdevs)) {
+                       /* ... and the rest are vdev names */
+                       cb.cb_vdevs.cb_names = argv + 1;
+                       cb.cb_vdevs.cb_names_count = argc - 1;
+                       cb.cb_type = ZFS_TYPE_VDEV;
+                       argc = 1; /* One pool to process */
+               } else {
+                       fprintf(stderr, gettext("Expected a list of vdevs in"
+                           " \"%s\", but got:\n"), argv[0]);
+                       error_list_unresolved_vdevs(argc - 1, argv + 1,
+                           argv[0], &cb.cb_vdevs);
+                       fprintf(stderr, "\n");
+                       usage(B_FALSE);
+                       return (1);
+               }
+       } else {
+               /*
+                * The first arg isn't a pool name,
+                */
+               fprintf(stderr, gettext("missing pool name.\n"));
+               fprintf(stderr, "\n");
+               usage(B_FALSE);
+               return (1);
+       }
+
+       if (zprop_get_list(g_zfs, propstr, &cb.cb_proplist,
+           cb.cb_type) != 0) {
+               /* Use correct list of valid properties (pool or vdev) */
+               current_prop_type = cb.cb_type;
+               usage(B_FALSE);
+       }
+
        if (cb.cb_proplist != NULL) {
                fake_name.pl_prop = ZPOOL_PROP_NAME;
                fake_name.pl_width = strlen(gettext("NAME"));
@@ -9947,8 +10200,8 @@ zpool_do_get(int argc, char **argv)
                cb.cb_proplist = &fake_name;
        }
 
-       ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, cb.cb_literal,
-           get_callback, &cb);
+       ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, cb.cb_type,
+           cb.cb_literal, get_callback, &cb);
 
        if (cb.cb_proplist == &fake_name)
                zprop_free_list(fake_name.pl_next);
@@ -9961,20 +10214,93 @@ zpool_do_get(int argc, char **argv)
 typedef struct set_cbdata {
        char *cb_propname;
        char *cb_value;
+       zfs_type_t cb_type;
+       vdev_cbdata_t cb_vdevs;
        boolean_t cb_any_successful;
 } set_cbdata_t;
 
 static int
-set_callback(zpool_handle_t *zhp, void *data)
+set_pool_callback(zpool_handle_t *zhp, set_cbdata_t *cb)
 {
        int error;
-       set_cbdata_t *cb = (set_cbdata_t *)data;
+
+       /* Check if we have out-of-bounds features */
+       if (strcmp(cb->cb_propname, ZPOOL_CONFIG_COMPATIBILITY) == 0) {
+               boolean_t features[SPA_FEATURES];
+               if (zpool_do_load_compat(cb->cb_value, features) !=
+                   ZPOOL_COMPATIBILITY_OK)
+                       return (-1);
+
+               nvlist_t *enabled = zpool_get_features(zhp);
+               spa_feature_t i;
+               for (i = 0; i < SPA_FEATURES; i++) {
+                       const char *fguid = spa_feature_table[i].fi_guid;
+                       if (nvlist_exists(enabled, fguid) && !features[i])
+                               break;
+               }
+               if (i < SPA_FEATURES)
+                       (void) fprintf(stderr, gettext("Warning: one or "
+                           "more features already enabled on pool '%s'\n"
+                           "are not present in this compatibility set.\n"),
+                           zpool_get_name(zhp));
+       }
+
+       /* if we're setting a feature, check it's in compatibility set */
+       if (zpool_prop_feature(cb->cb_propname) &&
+           strcmp(cb->cb_value, ZFS_FEATURE_ENABLED) == 0) {
+               char *fname = strchr(cb->cb_propname, '@') + 1;
+               spa_feature_t f;
+
+               if (zfeature_lookup_name(fname, &f) == 0) {
+                       char compat[ZFS_MAXPROPLEN];
+                       if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY,
+                           compat, ZFS_MAXPROPLEN, NULL, B_FALSE) != 0)
+                               compat[0] = '\0';
+
+                       boolean_t features[SPA_FEATURES];
+                       if (zpool_do_load_compat(compat, features) !=
+                           ZPOOL_COMPATIBILITY_OK) {
+                               (void) fprintf(stderr, gettext("Error: "
+                                   "cannot enable feature '%s' on pool '%s'\n"
+                                   "because the pool's 'compatibility' "
+                                   "property cannot be parsed.\n"),
+                                   fname, zpool_get_name(zhp));
+                               return (-1);
+                       }
+
+                       if (!features[f]) {
+                               (void) fprintf(stderr, gettext("Error: "
+                                   "cannot enable feature '%s' on pool '%s'\n"
+                                   "as it is not specified in this pool's "
+                                   "current compatibility set.\n"
+                                   "Consider setting 'compatibility' to a "
+                                   "less restrictive set, or to 'off'.\n"),
+                                   fname, zpool_get_name(zhp));
+                               return (-1);
+                       }
+               }
+       }
 
        error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value);
 
-       if (!error)
-               cb->cb_any_successful = B_TRUE;
+       return (error);
+}
+
+static int
+set_callback(zpool_handle_t *zhp, void *data)
+{
+       int error;
+       set_cbdata_t *cb = (set_cbdata_t *)data;
+
+       if (cb->cb_type == ZFS_TYPE_VDEV) {
+               error = zpool_set_vdev_prop(zhp, *cb->cb_vdevs.cb_names,
+                   cb->cb_propname, cb->cb_value);
+       } else {
+               assert(cb->cb_type == ZFS_TYPE_POOL);
+               error = set_pool_callback(zhp, cb);
+       }
 
+       cb->cb_any_successful = !error;
        return (error);
 }
 
@@ -9984,6 +10310,7 @@ zpool_do_set(int argc, char **argv)
        set_cbdata_t cb = { 0 };
        int error;
 
+       current_prop_type = ZFS_TYPE_POOL;
        if (argc > 1 && argv[1][0] == '-') {
                (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                    argv[1][1]);
@@ -10001,12 +10328,14 @@ zpool_do_set(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       if (argc > 3) {
+       if (argc > 4) {
                (void) fprintf(stderr, gettext("too many pool names\n"));
                usage(B_FALSE);
        }
 
        cb.cb_propname = argv[1];
+       cb.cb_type = ZFS_TYPE_POOL;
+       cb.cb_vdevs.cb_name_flags |= VDEV_NAME_TYPE_ID;
        cb.cb_value = strchr(cb.cb_propname, '=');
        if (cb.cb_value == NULL) {
                (void) fprintf(stderr, gettext("missing value in "
@@ -10016,9 +10345,33 @@ zpool_do_set(int argc, char **argv)
 
        *(cb.cb_value) = '\0';
        cb.cb_value++;
+       argc -= 2;
+       argv += 2;
+
+       if (are_vdevs_in_pool(argc, argv, NULL, &cb.cb_vdevs)) {
+               /* Argument is a vdev */
+               cb.cb_vdevs.cb_names = argv;
+               cb.cb_vdevs.cb_names_count = 1;
+               cb.cb_type = ZFS_TYPE_VDEV;
+               argc = 0; /* No pools to process */
+       } else if (are_all_pools(1, argv)) {
+               /* The first arg is a pool name */
+               if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0],
+                   &cb.cb_vdevs)) {
+                       /* 2nd argument is a vdev */
+                       cb.cb_vdevs.cb_names = argv + 1;
+                       cb.cb_vdevs.cb_names_count = 1;
+                       cb.cb_type = ZFS_TYPE_VDEV;
+                       argc = 1; /* One pool to process */
+               } else if (argc > 1) {
+                       (void) fprintf(stderr,
+                           gettext("too many pool names\n"));
+                       usage(B_FALSE);
+               }
+       }
 
-       error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL, B_FALSE,
-           set_callback, &cb);
+       error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
+           B_FALSE, set_callback, &cb);
 
        return (error);
 }
@@ -10144,8 +10497,8 @@ print_wait_status_row(wait_data_t *wd, zpool_handle_t *zhp, int row)
        pool_checkpoint_stat_t *pcs = NULL;
        pool_scan_stat_t *pss = NULL;
        pool_removal_stat_t *prs = NULL;
-       char *headers[] = {"DISCARD", "FREE", "INITIALIZE", "REPLACE",
-           "REMOVE", "RESILVER", "SCRUB", "TRIM"};
+       const char *const headers[] = {"DISCARD", "FREE", "INITIALIZE",
+           "REPLACE", "REMOVE", "RESILVER", "SCRUB", "TRIM"};
        int col_widths[ZPOOL_WAIT_NUM_ACTIVITIES];
 
        /* Calculate the width of each column */
@@ -10167,7 +10520,7 @@ print_wait_status_row(wait_data_t *wd, zpool_handle_t *zhp, int row)
                        if (wd->wd_enabled[i])
                                (void) printf("%*s", col_widths[i], headers[i]);
                }
-               (void) printf("\n");
+               (void) fputc('\n', stdout);
        }
 
        /* Bytes of work remaining in each activity */
@@ -10303,9 +10656,7 @@ int
 zpool_do_wait(int argc, char **argv)
 {
        boolean_t verbose = B_FALSE;
-       int c;
-       char *value;
-       int i;
+       int c, i;
        unsigned long count;
        pthread_t status_thr;
        int error = 0;
@@ -10339,28 +10690,26 @@ zpool_do_wait(int argc, char **argv)
                        get_timestamp_arg(*optarg);
                        break;
                case 't':
-               {
-                       static char *col_subopts[] = { "discard", "free",
-                           "initialize", "replace", "remove", "resilver",
-                           "scrub", "trim", NULL };
-
                        /* Reset activities array */
-                       bzero(&wd.wd_enabled, sizeof (wd.wd_enabled));
-                       while (*optarg != '\0') {
-                               int activity = getsubopt(&optarg, col_subopts,
-                                   &value);
-
-                               if (activity < 0) {
-                                       (void) fprintf(stderr,
-                                           gettext("invalid activity '%s'\n"),
-                                           value);
-                                       usage(B_FALSE);
-                               }
+                       memset(&wd.wd_enabled, 0, sizeof (wd.wd_enabled));
 
-                               wd.wd_enabled[activity] = B_TRUE;
+                       for (char *tok; (tok = strsep(&optarg, ",")); ) {
+                               static const char *const col_opts[] = {
+                                   "discard", "free", "initialize", "replace",
+                                   "remove", "resilver", "scrub", "trim" };
+
+                               for (i = 0; i < ARRAY_SIZE(col_opts); ++i)
+                                       if (strcmp(tok, col_opts[i]) == 0) {
+                                               wd.wd_enabled[i] = B_TRUE;
+                                               goto found;
+                                       }
+
+                               (void) fprintf(stderr,
+                                   gettext("invalid activity '%s'\n"), tok);
+                               usage(B_FALSE);
+found:;
                        }
                        break;
-               }
                case '?':
                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                            optopt);
@@ -10458,11 +10807,9 @@ zpool_do_wait(int argc, char **argv)
 }
 
 static int
-find_command_idx(char *command, int *idx)
+find_command_idx(const char *command, int *idx)
 {
-       int i;
-
-       for (i = 0; i < NCOMMAND; i++) {
+       for (int i = 0; i < NCOMMAND; ++i) {
                if (command_table[i].name == NULL)
                        continue;
 
@@ -10480,10 +10827,8 @@ find_command_idx(char *command, int *idx)
 static int
 zpool_do_version(int argc, char **argv)
 {
-       if (zfs_version_print() == -1)
-               return (1);
-
-       return (0);
+       (void) argc, (void) argv;
+       return (zfs_version_print() != 0);
 }
 
 /*
@@ -10492,28 +10837,25 @@ zpool_do_version(int argc, char **argv)
 static zpool_compat_status_t
 zpool_do_load_compat(const char *compat, boolean_t *list)
 {
-       char badword[ZFS_MAXPROPLEN];
-       char badfile[MAXPATHLEN];
+       char report[1024];
+
        zpool_compat_status_t ret;
 
-       switch (ret = zpool_load_compat(compat, list, badword, badfile)) {
+       ret = zpool_load_compat(compat, list, report, 1024);
+       switch (ret) {
+
        case ZPOOL_COMPATIBILITY_OK:
                break;
-       case ZPOOL_COMPATIBILITY_READERR:
-               (void) fprintf(stderr, gettext("error reading compatibility "
-                   "file '%s'\n"), badfile);
-               break;
+
+       case ZPOOL_COMPATIBILITY_NOFILES:
        case ZPOOL_COMPATIBILITY_BADFILE:
-               (void) fprintf(stderr, gettext("compatibility file '%s' "
-                   "too large or not newline-terminated\n"), badfile);
+       case ZPOOL_COMPATIBILITY_BADTOKEN:
+               (void) fprintf(stderr, "Error: %s\n", report);
                break;
-       case ZPOOL_COMPATIBILITY_BADWORD:
-               (void) fprintf(stderr, gettext("unknown feature '%s' in "
-                   "compatibility file '%s'\n"), badword, badfile);
-               break;
-       case ZPOOL_COMPATIBILITY_NOFILES:
-               (void) fprintf(stderr, gettext("no compatibility files "
-                   "specified\n"));
+
+       case ZPOOL_COMPATIBILITY_WARNTOKEN:
+               (void) fprintf(stderr, "Warning: %s\n", report);
+               ret = ZPOOL_COMPATIBILITY_OK;
                break;
        }
        return (ret);
@@ -10569,7 +10911,7 @@ main(int argc, char **argv)
         * Many commands modify input strings for string parsing reasons.
         * We create a copy to protect the original argv.
         */
-       newargv = malloc((argc + 1) * sizeof (newargv[0]));
+       newargv = safe_malloc((argc + 1) * sizeof (newargv[0]));
        for (i = 0; i < argc; i++)
                newargv[i] = strdup(argv[i]);
        newargv[argc] = NULL;