]> git.proxmox.com Git - mirror_zfs.git/blobdiff - lib/libzfs/libzfs_pool.c
Add 'zpool split' coverage to the ZFS Test Suite
[mirror_zfs.git] / lib / libzfs / libzfs_pool.c
index 0641c18449dd24515f6e13f2000d7c2f68e62a79..3fe0b9e0ee44c6968e1c0be1bc286eca1d6c2327 100644 (file)
@@ -23,6 +23,9 @@
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011, 2014 by Delphix. All rights reserved.
+ * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
+ * Copyright (c) 2017 Datto Inc.
+ * Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
  */
 
 #include <ctype.h>
 #include <zone.h>
 #include <sys/stat.h>
 #include <sys/efi_partition.h>
+#include <sys/systeminfo.h>
 #include <sys/vtoc.h>
 #include <sys/zfs_ioctl.h>
 #include <dlfcn.h>
-#if HAVE_LIBDEVMAPPER
-#include <libdevmapper.h>
-#endif
 
 #include "zfs_namecheck.h"
 #include "zfs_prop.h"
@@ -52,6 +53,7 @@
 #include "zfeature_common.h"
 
 static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
+static boolean_t zpool_vdev_is_interior(const char *name);
 
 typedef struct prop_flags {
        int create:1;   /* Validate property on creation */
@@ -111,7 +113,7 @@ zpool_props_refresh(zpool_handle_t *zhp)
        return (0);
 }
 
-static char *
+static const char *
 zpool_get_prop_string(zpool_handle_t *zhp, zpool_prop_t prop,
     zprop_source_t *src)
 {
@@ -179,12 +181,10 @@ zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *src)
 /*
  * Map VDEV STATE to printed strings.
  */
-char *
+const char *
 zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
 {
        switch (state) {
-       default:
-               break;
        case VDEV_STATE_CLOSED:
        case VDEV_STATE_OFFLINE:
                return (gettext("OFFLINE"));
@@ -203,6 +203,9 @@ zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
                return (gettext("DEGRADED"));
        case VDEV_STATE_HEALTHY:
                return (gettext("ONLINE"));
+
+       default:
+               break;
        }
 
        return (gettext("UNKNOWN"));
@@ -311,7 +314,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
                case ZPOOL_PROP_ASHIFT:
                        if (literal)
                                (void) snprintf(buf, len, "%llu",
-                                       (u_longlong_t)intval);
+                                   (u_longlong_t)intval);
                        else
                                (void) zfs_nicenum(intval, buf, len);
                        break;
@@ -323,7 +326,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
                                (void) snprintf(buf, len, "%llu",
                                    (u_longlong_t)intval);
                        } else {
-                               (void) zfs_nicenum(intval, buf, len);
+                               (void) zfs_nicebytes(intval, buf, len);
                        }
                        break;
 
@@ -407,6 +410,8 @@ static boolean_t
 bootfs_name_valid(const char *pool, char *bootfs)
 {
        int len = strlen(pool);
+       if (bootfs[0] == '\0')
+               return (B_TRUE);
 
        if (!zfs_name_valid(bootfs, ZFS_TYPE_FILESYSTEM|ZFS_TYPE_SNAPSHOT))
                return (B_FALSE);
@@ -418,29 +423,6 @@ bootfs_name_valid(const char *pool, char *bootfs)
        return (B_FALSE);
 }
 
-#if defined(__sun__) || defined(__sun)
-/*
- * Inspect the configuration to determine if any of the devices contain
- * an EFI label.
- */
-static boolean_t
-pool_uses_efi(nvlist_t *config)
-{
-       nvlist_t **child;
-       uint_t c, children;
-
-       if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
-           &child, &children) != 0)
-               return (read_efi_label(config, NULL) >= 0);
-
-       for (c = 0; c < children; c++) {
-               if (pool_uses_efi(child[c]))
-                       return (B_TRUE);
-       }
-       return (B_FALSE);
-}
-#endif
-
 boolean_t
 zpool_is_bootable(zpool_handle_t *zhp)
 {
@@ -469,7 +451,6 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
        char *slash, *check;
        struct stat64 statbuf;
        zpool_handle_t *zhp;
-       nvlist_t *nvroot;
 
        if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) {
                (void) no_memory(hdl);
@@ -481,7 +462,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                const char *propname = nvpair_name(elem);
 
                prop = zpool_name_to_prop(propname);
-               if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
+               if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) {
                        int err;
                        char *fname = strchr(propname, '@') + 1;
 
@@ -502,10 +483,20 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                        }
 
                        (void) nvpair_value_string(elem, &strval);
-                       if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0) {
+                       if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0 &&
+                           strcmp(strval, ZFS_FEATURE_DISABLED) != 0) {
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "property '%s' can only be set to "
-                                   "'enabled'"), propname);
+                                   "'enabled' or 'disabled'"), propname);
+                               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                               goto error;
+                       }
+
+                       if (!flags.create &&
+                           strcmp(strval, ZFS_FEATURE_DISABLED) == 0) {
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "property '%s' can only be set to "
+                                   "'disabled' at creation time"), propname);
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                                goto error;
                        }
@@ -520,7 +511,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                /*
                 * Make sure this property is valid and applies to this type.
                 */
-               if (prop == ZPROP_INVAL) {
+               if (prop == ZPOOL_PROP_INVAL) {
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "invalid property '%s'"), propname);
                        (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
@@ -542,8 +533,6 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                 * Perform additional checking for specific properties.
                 */
                switch (prop) {
-               default:
-                       break;
                case ZPOOL_PROP_VERSION:
                        if (intval < version ||
                            !SPA_VERSION_IS_SUPPORTED(intval)) {
@@ -556,18 +545,13 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                        break;
 
                case ZPOOL_PROP_ASHIFT:
-                       if (!flags.create) {
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "property '%s' can only be set at "
-                                   "creation time"), propname);
-                               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-                               goto error;
-                       }
-
-                       if (intval != 0 && (intval < 9 || intval > 13)) {
+                       if (intval != 0 &&
+                           (intval < ASHIFT_MIN || intval > ASHIFT_MAX)) {
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "property '%s' number %d is invalid."),
-                                   propname, intval);
+                                   "invalid '%s=%d' property: only values "
+                                   "between %" PRId32 " and %" PRId32 " "
+                                   "are allowed.\n"),
+                                   propname, intval, ASHIFT_MIN, ASHIFT_MAX);
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                                goto error;
                        }
@@ -594,8 +578,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                         * bootfs property value has to be a dataset name and
                         * the dataset has to be in the same pool as it sets to.
                         */
-                       if (strval[0] != '\0' && !bootfs_name_valid(poolname,
-                           strval)) {
+                       if (!bootfs_name_valid(poolname, strval)) {
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
                                    "is an invalid name"), strval);
                                (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
@@ -608,23 +591,6 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                                (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
                                goto error;
                        }
-                       verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
-                           ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
-
-#if defined(__sun__) || defined(__sun)
-                       /*
-                        * bootfs property cannot be set on a disk which has
-                        * been EFI labeled.
-                        */
-                       if (pool_uses_efi(nvroot)) {
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "property '%s' not supported on "
-                                   "EFI labeled devices"), propname);
-                               (void) zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf);
-                               zpool_close(zhp);
-                               goto error;
-                       }
-#endif
                        zpool_close(zhp);
                        break;
 
@@ -723,6 +689,18 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                                goto error;
                        }
                        break;
+               case ZPOOL_PROP_MULTIHOST:
+                       if (get_system_hostid() == 0) {
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "requires a non-zero system hostid"));
+                               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                               goto error;
+                       }
+                       break;
+               default:
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "property '%s'(%d) not defined"), propname, prop);
+                       break;
                }
        }
 
@@ -904,7 +882,7 @@ zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf,
 
        /*
         * Convert from feature name to feature guid. This conversion is
-        * unecessary for unsupported@... properties because they already
+        * unnecessary for unsupported@... properties because they already
         * use guids.
         */
        if (supported) {
@@ -1033,14 +1011,21 @@ zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
                                    "trailing slash in name"));
                                break;
 
-                       case NAME_ERR_MULTIPLE_AT:
+                       case NAME_ERR_MULTIPLE_DELIMITERS:
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "multiple '@' delimiters in name"));
+                                   "multiple '@' and/or '#' delimiters in "
+                                   "name"));
                                break;
+
                        case NAME_ERR_NO_AT:
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "permission set is missing '@'"));
                                break;
+
+                       default:
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "(%d) not defined"), why);
+                               break;
                        }
                }
                return (B_FALSE);
@@ -1187,6 +1172,9 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
        zfs_cmd_t zc = {"\0"};
        nvlist_t *zc_fsprops = NULL;
        nvlist_t *zc_props = NULL;
+       nvlist_t *hidden_args = NULL;
+       uint8_t *wkeydata = NULL;
+       uint_t wkeylen = 0;
        char msg[1024];
        int ret = -1;
 
@@ -1217,17 +1205,34 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
                    strcmp(zonestr, "on") == 0);
 
                if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM,
-                   fsprops, zoned, NULL, NULL, msg)) == NULL) {
+                   fsprops, zoned, NULL, NULL, B_TRUE, msg)) == NULL) {
                        goto create_failed;
                }
                if (!zc_props &&
                    (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) {
                        goto create_failed;
                }
+               if (zfs_crypto_create(hdl, NULL, zc_fsprops, props,
+                   &wkeydata, &wkeylen) != 0) {
+                       zfs_error(hdl, EZFS_CRYPTOFAILED, msg);
+                       goto create_failed;
+               }
                if (nvlist_add_nvlist(zc_props,
                    ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) {
                        goto create_failed;
                }
+               if (wkeydata != NULL) {
+                       if (nvlist_alloc(&hidden_args, NV_UNIQUE_NAME, 0) != 0)
+                               goto create_failed;
+
+                       if (nvlist_add_uint8_array(hidden_args, "wkeydata",
+                           wkeydata, wkeylen) != 0)
+                               goto create_failed;
+
+                       if (nvlist_add_nvlist(zc_props, ZPOOL_HIDDEN_ARGS,
+                           hidden_args) != 0)
+                               goto create_failed;
+               }
        }
 
        if (zc_props && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0)
@@ -1240,6 +1245,9 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
                zcmd_free_nvlists(&zc);
                nvlist_free(zc_props);
                nvlist_free(zc_fsprops);
+               nvlist_free(hidden_args);
+               if (wkeydata != NULL)
+                       free(wkeydata);
 
                switch (errno) {
                case EBUSY:
@@ -1281,7 +1289,8 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
                        {
                                char buf[64];
 
-                               zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
+                               zfs_nicebytes(SPA_MINDEVSIZE, buf,
+                                   sizeof (buf));
 
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "one or more devices is less than the "
@@ -1308,6 +1317,9 @@ create_failed:
        zcmd_free_nvlists(&zc);
        nvlist_free(zc_props);
        nvlist_free(zc_fsprops);
+       nvlist_free(hidden_args);
+       if (wkeydata != NULL)
+               free(wkeydata);
        return (ret);
 }
 
@@ -1381,26 +1393,6 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
                return (zfs_error(hdl, EZFS_BADVERSION, msg));
        }
 
-#if defined(__sun__) || defined(__sun)
-       if (zpool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot,
-           ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) {
-               uint64_t s;
-
-               for (s = 0; s < nspares; s++) {
-                       char *path;
-
-                       if (nvlist_lookup_string(spares[s], ZPOOL_CONFIG_PATH,
-                           &path) == 0 && pool_uses_efi(spares[s])) {
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "device '%s' contains an EFI label and "
-                                   "cannot be used on root pools."),
-                                   zpool_vdev_name(hdl, NULL, spares[s], 0));
-                               return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg));
-                       }
-               }
-       }
-#endif
-
        if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
            SPA_VERSION_L2CACHE &&
            nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
@@ -1438,7 +1430,8 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
                        {
                                char buf[64];
 
-                               zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
+                               zfs_nicebytes(SPA_MINDEVSIZE, buf,
+                                   sizeof (buf));
 
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "device is less than the minimum "
@@ -1832,6 +1825,7 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
 
        if (error) {
                char desc[1024];
+               char aux[256];
 
                /*
                 * Dry-run failed, but we print out what success
@@ -1877,6 +1871,47 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
                        (void) zfs_error(hdl, EZFS_BADVERSION, desc);
                        break;
 
+               case EREMOTEIO:
+                       if (nv != NULL && nvlist_lookup_nvlist(nv,
+                           ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0) {
+                               char *hostname = "<unknown>";
+                               uint64_t hostid = 0;
+                               mmp_state_t mmp_state;
+
+                               mmp_state = fnvlist_lookup_uint64(nvinfo,
+                                   ZPOOL_CONFIG_MMP_STATE);
+
+                               if (nvlist_exists(nvinfo,
+                                   ZPOOL_CONFIG_MMP_HOSTNAME))
+                                       hostname = fnvlist_lookup_string(nvinfo,
+                                           ZPOOL_CONFIG_MMP_HOSTNAME);
+
+                               if (nvlist_exists(nvinfo,
+                                   ZPOOL_CONFIG_MMP_HOSTID))
+                                       hostid = fnvlist_lookup_uint64(nvinfo,
+                                           ZPOOL_CONFIG_MMP_HOSTID);
+
+                               if (mmp_state == MMP_STATE_ACTIVE) {
+                                       (void) snprintf(aux, sizeof (aux),
+                                           dgettext(TEXT_DOMAIN, "pool is imp"
+                                           "orted on host '%s' (hostid=%lx).\n"
+                                           "Export the pool on the other "
+                                           "system, then run 'zpool import'."),
+                                           hostname, (unsigned long) hostid);
+                               } else if (mmp_state == MMP_STATE_NO_HOSTID) {
+                                       (void) snprintf(aux, sizeof (aux),
+                                           dgettext(TEXT_DOMAIN, "pool has "
+                                           "the multihost property on and "
+                                           "the\nsystem's hostid is not set. "
+                                           "Set a unique system hostid with "
+                                           "the zgenhostid(8) command.\n"));
+                               }
+
+                               (void) zfs_error_aux(hdl, aux);
+                       }
+                       (void) zfs_error(hdl, EZFS_ACTIVE_POOL, desc);
+                       break;
+
                case EINVAL:
                        (void) zfs_error(hdl, EZFS_INVALCONFIG, desc);
                        break;
@@ -1951,22 +1986,39 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
  * Scan the pool.
  */
 int
-zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
+zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd)
 {
        zfs_cmd_t zc = {"\0"};
        char msg[1024];
+       int err;
        libzfs_handle_t *hdl = zhp->zpool_hdl;
 
        (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
        zc.zc_cookie = func;
+       zc.zc_flags = cmd;
 
-       if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0 ||
-           (errno == ENOENT && func != POOL_SCAN_NONE))
+       if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0)
+               return (0);
+
+       err = errno;
+
+       /* ECANCELED on a scrub means we resumed a paused scrub */
+       if (err == ECANCELED && func == POOL_SCAN_SCRUB &&
+           cmd == POOL_SCRUB_NORMAL)
+               return (0);
+
+       if (err == ENOENT && func != POOL_SCAN_NONE && cmd == POOL_SCRUB_NORMAL)
                return (0);
 
        if (func == POOL_SCAN_SCRUB) {
-               (void) snprintf(msg, sizeof (msg),
-                   dgettext(TEXT_DOMAIN, "cannot scrub %s"), zc.zc_name);
+               if (cmd == POOL_SCRUB_PAUSE) {
+                       (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+                           "cannot pause scrubbing %s"), zc.zc_name);
+               } else {
+                       assert(cmd == POOL_SCRUB_NORMAL);
+                       (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+                           "cannot scrub %s"), zc.zc_name);
+               }
        } else if (func == POOL_SCAN_NONE) {
                (void) snprintf(msg, sizeof (msg),
                    dgettext(TEXT_DOMAIN, "cannot cancel scrubbing %s"),
@@ -1975,7 +2027,7 @@ zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
                assert(!"unexpected result");
        }
 
-       if (errno == EBUSY) {
+       if (err == EBUSY) {
                nvlist_t *nvroot;
                pool_scan_stat_t *ps = NULL;
                uint_t psc;
@@ -1984,14 +2036,18 @@ zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
                    ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
                (void) nvlist_lookup_uint64_array(nvroot,
                    ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc);
-               if (ps && ps->pss_func == POOL_SCAN_SCRUB)
-                       return (zfs_error(hdl, EZFS_SCRUBBING, msg));
-               else
+               if (ps && ps->pss_func == POOL_SCAN_SCRUB) {
+                       if (cmd == POOL_SCRUB_PAUSE)
+                               return (zfs_error(hdl, EZFS_SCRUB_PAUSED, msg));
+                       else
+                               return (zfs_error(hdl, EZFS_SCRUBBING, msg));
+               } else {
                        return (zfs_error(hdl, EZFS_RESILVERING, msg));
-       } else if (errno == ENOENT) {
+               }
+       } else if (err == ENOENT) {
                return (zfs_error(hdl, EZFS_NO_SCRUB, msg));
        } else {
-               return (zpool_standard_error(hdl, errno, msg));
+               return (zpool_standard_error(hdl, err, msg));
        }
 }
 
@@ -2086,10 +2142,7 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare,
                                break;
                        }
 
-                       verify(strncmp(type, VDEV_TYPE_RAIDZ,
-                           strlen(VDEV_TYPE_RAIDZ)) == 0 ||
-                           strncmp(type, VDEV_TYPE_MIRROR,
-                           strlen(VDEV_TYPE_MIRROR)) == 0);
+                       verify(zpool_vdev_is_interior(type));
                        verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
                            &id) == 0);
 
@@ -2196,10 +2249,13 @@ zpool_find_vdev_by_physpath(zpool_handle_t *zhp, const char *ppath,
 /*
  * Determine if we have an "interior" top-level vdev (i.e mirror/raidz).
  */
-boolean_t
+static boolean_t
 zpool_vdev_is_interior(const char *name)
 {
        if (strncmp(name, VDEV_TYPE_RAIDZ, strlen(VDEV_TYPE_RAIDZ)) == 0 ||
+           strncmp(name, VDEV_TYPE_SPARE, strlen(VDEV_TYPE_SPARE)) == 0 ||
+           strncmp(name,
+           VDEV_TYPE_REPLACING, strlen(VDEV_TYPE_REPLACING)) == 0 ||
            strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0)
                return (B_TRUE);
        return (B_FALSE);
@@ -2238,7 +2294,7 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
 }
 
 static int
-vdev_online(nvlist_t *nv)
+vdev_is_online(nvlist_t *nv)
 {
        uint64_t ival;
 
@@ -2306,12 +2362,13 @@ vdev_get_physpaths(nvlist_t *nv, char *physpath, size_t phypath_size,
                                return (EZFS_INVALCONFIG);
                }
 
-               if (vdev_online(nv)) {
+               if (vdev_is_online(nv)) {
                        if ((ret = vdev_get_one_physpath(nv, physpath,
                            phypath_size, rsz)) != 0)
                                return (ret);
                }
        } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 ||
+           strcmp(type, VDEV_TYPE_RAIDZ) == 0 ||
            strcmp(type, VDEV_TYPE_REPLACING) == 0 ||
            (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) {
                nvlist_t **child;
@@ -2357,15 +2414,11 @@ zpool_get_config_physpath(nvlist_t *config, char *physpath, size_t phypath_size)
            &child, &count) != 0)
                return (EZFS_INVALCONFIG);
 
-#if defined(__sun__) || defined(__sun)
        /*
-        * root pool can not have EFI labeled disks and can only have
-        * a single top-level vdev.
+        * root pool can only have a single top-level vdev.
         */
-       if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1 ||
-           pool_uses_efi(vdev_root))
+       if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1)
                return (EZFS_POOL_INVALARG);
-#endif
 
        (void) vdev_get_physpaths(child[0], physpath, phypath_size, &rsz,
            B_FALSE);
@@ -2412,15 +2465,58 @@ zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
         * The module will do it for us in vdev_disk_open().
         */
        error = efi_use_whole_disk(fd);
+
+       /* Flush the buffers to disk and invalidate the page cache. */
+       (void) fsync(fd);
+       (void) ioctl(fd, BLKFLSBUF);
+
        (void) close(fd);
        if (error && error != VT_ENOSPC) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
                    "relabel '%s': unable to read disk capacity"), path);
                return (zfs_error(hdl, EZFS_NOCAP, msg));
        }
+
        return (0);
 }
 
+/*
+ * Convert a vdev path to a GUID.  Returns GUID or 0 on error.
+ *
+ * If is_spare, is_l2cache, or is_log is non-NULL, then store within it
+ * if the VDEV is a spare, l2cache, or log device.  If they're NULL then
+ * ignore them.
+ */
+static uint64_t
+zpool_vdev_path_to_guid_impl(zpool_handle_t *zhp, const char *path,
+    boolean_t *is_spare, boolean_t *is_l2cache, boolean_t *is_log)
+{
+       uint64_t guid;
+       boolean_t spare = B_FALSE, l2cache = B_FALSE, log = B_FALSE;
+       nvlist_t *tgt;
+
+       if ((tgt = zpool_find_vdev(zhp, path, &spare, &l2cache,
+           &log)) == NULL)
+               return (0);
+
+       verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &guid) == 0);
+       if (is_spare != NULL)
+               *is_spare = spare;
+       if (is_l2cache != NULL)
+               *is_l2cache = l2cache;
+       if (is_log != NULL)
+               *is_log = log;
+
+       return (guid);
+}
+
+/* Convert a vdev path to a GUID.  Returns GUID or 0 on error. */
+uint64_t
+zpool_vdev_path_to_guid(zpool_handle_t *zhp, const char *path)
+{
+       return (zpool_vdev_path_to_guid_impl(zhp, path, NULL, NULL, NULL));
+}
+
 /*
  * Bring the specified vdev online.   The 'flags' parameter is a set of the
  * ZFS_ONLINE_* flags.
@@ -2431,6 +2527,7 @@ zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
 {
        zfs_cmd_t zc = {"\0"};
        char msg[1024];
+       char *pathname;
        nvlist_t *tgt;
        boolean_t avail_spare, l2cache, islog;
        libzfs_handle_t *hdl = zhp->zpool_hdl;
@@ -2454,8 +2551,9 @@ zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
        if (avail_spare)
                return (zfs_error(hdl, EZFS_ISSPARE, msg));
 
-       if (flags & ZFS_ONLINE_EXPAND ||
-           zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) {
+       if ((flags & ZFS_ONLINE_EXPAND ||
+           zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) &&
+           nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &pathname) == 0) {
                uint64_t wholedisk = 0;
 
                (void) nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK,
@@ -2672,18 +2770,6 @@ zpool_vdev_attach(zpool_handle_t *zhp,
                (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
                    "cannot attach %s to %s"), new_disk, old_disk);
 
-#if defined(__sun__) || defined(__sun)
-       /*
-        * If this is a root pool, make sure that we're not attaching an
-        * EFI labeled device.
-        */
-       if (rootpool && pool_uses_efi(nvroot)) {
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "EFI labeled devices are not supported on root pools."));
-               return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg));
-       }
-#endif
-
        (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
        if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache,
            &islog)) == 0)
@@ -2933,7 +3019,7 @@ zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot,
        nvlist_t **varray = NULL, *zc_props = NULL;
        uint_t c, children, newchildren, lastlog = 0, vcount, found = 0;
        libzfs_handle_t *hdl = zhp->zpool_hdl;
-       uint64_t vers;
+       uint64_t vers, readonly = B_FALSE;
        boolean_t freelist = B_FALSE, memory_err = B_TRUE;
        int retval = 0;
 
@@ -2958,6 +3044,14 @@ zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot,
                if ((zc_props = zpool_valid_proplist(hdl, zhp->zpool_name,
                    props, vers, flags, msg)) == NULL)
                        return (-1);
+               (void) nvlist_lookup_uint64(zc_props,
+                   zpool_prop_to_name(ZPOOL_PROP_READONLY), &readonly);
+               if (readonly) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "property %s can only be set at import time"),
+                           zpool_prop_to_name(ZPOOL_PROP_READONLY));
+                       return (-1);
+               }
        }
 
        if (nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN, &child,
@@ -3298,20 +3392,41 @@ zpool_reguid(zpool_handle_t *zhp)
  * Reopen the pool.
  */
 int
-zpool_reopen(zpool_handle_t *zhp)
+zpool_reopen_one(zpool_handle_t *zhp, void *data)
 {
-       zfs_cmd_t zc = {"\0"};
-       char msg[1024];
-       libzfs_handle_t *hdl = zhp->zpool_hdl;
+       libzfs_handle_t *hdl = zpool_get_handle(zhp);
+       const char *pool_name = zpool_get_name(zhp);
+       boolean_t *scrub_restart = data;
+       int error;
 
-       (void) snprintf(msg, sizeof (msg),
-           dgettext(TEXT_DOMAIN, "cannot reopen '%s'"),
-           zhp->zpool_name);
+       error = lzc_reopen(pool_name, *scrub_restart);
+       if (error) {
+               return (zpool_standard_error_fmt(hdl, error,
+                   dgettext(TEXT_DOMAIN, "cannot reopen '%s'"), pool_name));
+       }
 
-       (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-       if (zfs_ioctl(hdl, ZFS_IOC_POOL_REOPEN, &zc) == 0)
-               return (0);
-       return (zpool_standard_error(hdl, errno, msg));
+       return (0);
+}
+
+/* call into libzfs_core to execute the sync IOCTL per pool */
+int
+zpool_sync_one(zpool_handle_t *zhp, void *data)
+{
+       int ret;
+       libzfs_handle_t *hdl = zpool_get_handle(zhp);
+       const char *pool_name = zpool_get_name(zhp);
+       boolean_t *force = data;
+       nvlist_t *innvl = fnvlist_alloc();
+
+       fnvlist_add_boolean_value(innvl, "force", *force);
+       if ((ret = lzc_sync(pool_name, innvl, NULL)) != 0) {
+               nvlist_free(innvl);
+               return (zpool_standard_error_fmt(hdl, ret,
+                   dgettext(TEXT_DOMAIN, "sync '%s' failed"), pool_name));
+       }
+       nvlist_free(innvl);
+
+       return (0);
 }
 
 #if defined(__sun__) || defined(__sun)
@@ -3418,12 +3533,12 @@ zfs_strip_partition(char *path)
                d = part + 1;
        } else if ((tmp[0] == 'h' || tmp[0] == 's' || tmp[0] == 'v') &&
            tmp[1] == 'd') {
-               for (d = &tmp[2]; isalpha(*d); part = ++d);
+               for (d = &tmp[2]; isalpha(*d); part = ++d) { }
        } else if (strncmp("xvd", tmp, 3) == 0) {
-               for (d = &tmp[3]; isalpha(*d); part = ++d);
+               for (d = &tmp[3]; isalpha(*d); part = ++d) { }
        }
        if (part && d && *d != '\0') {
-               for (; isdigit(*d); d++);
+               for (; isdigit(*d); d++) { }
                if (*d == '\0')
                        *part = '\0';
        }
@@ -3431,6 +3546,43 @@ zfs_strip_partition(char *path)
        return (tmp);
 }
 
+/*
+ * Same as zfs_strip_partition, but allows "/dev/" to be in the pathname
+ *
+ * path:       /dev/sda1
+ * returns:    /dev/sda
+ *
+ * Returned string must be freed.
+ */
+char *
+zfs_strip_partition_path(char *path)
+{
+       char *newpath = strdup(path);
+       char *sd_offset;
+       char *new_sd;
+
+       if (!newpath)
+               return (NULL);
+
+       /* Point to "sda1" part of "/dev/sda1" */
+       sd_offset = strrchr(newpath, '/') + 1;
+
+       /* Get our new name "sda" */
+       new_sd = zfs_strip_partition(sd_offset);
+       if (!new_sd) {
+               free(newpath);
+               return (NULL);
+       }
+
+       /* Paste the "sda" where "sda1" was */
+       strlcpy(sd_offset, new_sd, strlen(sd_offset) + 1);
+
+       /* Free temporary "sda" */
+       free(new_sd);
+
+       return (newpath);
+}
+
 #define        PATH_BUF_LEN    64
 
 /*
@@ -3457,6 +3609,14 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
        char buf[PATH_BUF_LEN];
        char tmpbuf[PATH_BUF_LEN];
 
+       /*
+        * vdev_name will be "root"/"root-0" for the root vdev, but it is the
+        * zpool name that will be displayed to the user.
+        */
+       verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
+       if (zhp != NULL && strcmp(type, "root") == 0)
+               return (zfs_strdup(hdl, zpool_get_name(zhp)));
+
        env = getenv("ZPOOL_VDEV_NAME_PATH");
        if (env && (strtoul(env, NULL, 0) > 0 ||
            !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
@@ -3538,7 +3698,6 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
                /*
                 * For a block device only use the name.
                 */
-               verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
                if ((strcmp(type, VDEV_TYPE_DISK) == 0) &&
                    !(name_flags & VDEV_NAME_PATH)) {
                        path = strrchr(path, '/');
@@ -3553,7 +3712,7 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
                        return (zfs_strip_partition(path));
                }
        } else {
-               verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
+               path = type;
 
                /*
                 * If it's a raidz device, we need to stick in the parity level.
@@ -3597,6 +3756,7 @@ int
 zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
 {
        zfs_cmd_t zc = {"\0"};
+       libzfs_handle_t *hdl = zhp->zpool_hdl;
        uint64_t count;
        zbookmark_phys_t *zb = NULL;
        int i;
@@ -3610,9 +3770,8 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
            &count) == 0);
        if (count == 0)
                return (0);
-       if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
-           count * sizeof (zbookmark_phys_t))) == (uintptr_t)NULL)
-               return (-1);
+       zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
+           count * sizeof (zbookmark_phys_t));
        zc.zc_nvlist_dst_size = count;
        (void) strcpy(zc.zc_name, zhp->zpool_name);
        for (;;) {
@@ -3625,11 +3784,11 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
                                count = zc.zc_nvlist_dst_size;
                                dst = zfs_alloc(zhp->zpool_hdl, count *
                                    sizeof (zbookmark_phys_t));
-                               if (dst == NULL)
-                                       return (-1);
                                zc.zc_nvlist_dst = (uintptr_t)dst;
                        } else {
-                               return (-1);
+                               return (zpool_standard_error_fmt(hdl, errno,
+                                   dgettext(TEXT_DOMAIN, "errors: List of "
+                                   "errors unavailable")));
                        }
                } else {
                        break;
@@ -3798,6 +3957,7 @@ zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover,
        uint64_t reclen;
        nvlist_t *nv;
        int i;
+       void *tmp;
 
        while (bytes_read > sizeof (reclen)) {
 
@@ -3817,8 +3977,14 @@ zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover,
                /* add record to nvlist array */
                (*numrecords)++;
                if (ISP2(*numrecords + 1)) {
-                       *records = realloc(*records,
+                       tmp = realloc(*records,
                            *numrecords * 2 * sizeof (nvlist_t *));
+                       if (tmp == NULL) {
+                               nvlist_free(nv);
+                               (*numrecords)--;
+                               return (ENOMEM);
+                       }
+                       *records = tmp;
                }
                (*records)[*numrecords - 1] = nv;
        }
@@ -4081,7 +4247,7 @@ read_efi_label(nvlist_t *config, diskaddr_t *sb)
 
        (void) snprintf(diskname, sizeof (diskname), "%s%s", DISK_ROOT,
            strrchr(path, '/'));
-       if ((fd = open(diskname, O_RDWR|O_DIRECT)) >= 0) {
+       if ((fd = open(diskname, O_RDONLY|O_DIRECT)) >= 0) {
                struct dk_gpt *vtoc;
 
                if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
@@ -4133,7 +4299,7 @@ zpool_label_disk_check(char *path)
        struct dk_gpt *vtoc;
        int fd, err;
 
-       if ((fd = open(path, O_RDWR|O_DIRECT)) < 0)
+       if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0)
                return (errno);
 
        if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
@@ -4175,7 +4341,7 @@ zpool_label_name(char *label_name, int label_size)
        if (id == 0)
                id = (((uint64_t)rand()) << 32) | (uint64_t)rand();
 
-       snprintf(label_name, label_size, "zfs-%016llx", (u_longlong_t) id);
+       snprintf(label_name, label_size, "zfs-%016llx", (u_longlong_t)id);
 }
 
 /*
@@ -4200,15 +4366,6 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
        if (zhp) {
                nvlist_t *nvroot;
 
-#if defined(__sun__) || defined(__sun)
-               if (zpool_is_bootable(zhp)) {
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "EFI labeled devices are not supported on root "
-                           "pools."));
-                       return (zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf));
-               }
-#endif
-
                verify(nvlist_lookup_nvlist(zhp->zpool_config,
                    ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
 
@@ -4274,13 +4431,21 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
        vtoc->efi_parts[8].p_size = resv;
        vtoc->efi_parts[8].p_tag = V_RESERVED;
 
-       if ((rval = efi_write(fd, vtoc)) != 0 || (rval = efi_rescan(fd)) != 0) {
-               /*
-                * Some block drivers (like pcata) may not support EFI
-                * GPT labels.  Print out a helpful error message dir-
-                * ecting the user to manually label the disk and give
-                * a specific slice.
-                */
+       rval = efi_write(fd, vtoc);
+
+       /* Flush the buffers to disk and invalidate the page cache. */
+       (void) fsync(fd);
+       (void) ioctl(fd, BLKFLSBUF);
+
+       if (rval == 0)
+               rval = efi_rescan(fd);
+
+       /*
+        * Some block drivers (like pcata) may not support EFI GPT labels.
+        * Print out a helpful error message directing the user to manually
+        * label the disk and give a specific slice.
+        */
+       if (rval != 0) {
                (void) close(fd);
                efi_free(vtoc);
 
@@ -4317,112 +4482,69 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
        return (0);
 }
 
-#if HAVE_LIBDEVMAPPER
-static void libdevmapper_dummy_log(int level, const char *file, int line,
-    int dm_errno_or_class, const char *f, ...) {}
-
-/* Disable libdevmapper error logging */
-static void disable_libdevmapper_errors(void) {
-       dm_log_with_errno_init(libdevmapper_dummy_log);
-}
-/* Enable libdevmapper error logging */
-static void enable_libdevmapper_errors(void) {
-       dm_log_with_errno_init(NULL);
-}
-#endif
-
 /*
  * Allocate and return the underlying device name for a device mapper device.
  * If a device mapper device maps to multiple devices, return the first device.
  *
- * For example, dm_name = "/dev/dm-0" could return "/dev/sda"
- *
- * dm_name should include the "/dev[/mapper]" prefix.
+ * For example, dm_name = "/dev/dm-0" could return "/dev/sda". Symlinks to a
+ * DM device (like /dev/disk/by-vdev/A0) are also allowed.
  *
  * Returns device name, or NULL on error or no match.  If dm_name is not a DM
  * device then return NULL.
  *
  * NOTE: The returned name string must be *freed*.
  */
-static char * dm_get_underlying_path(char *dm_name)
+char *
+dm_get_underlying_path(char *dm_name)
 {
-       char *name = NULL;
-#if HAVE_LIBDEVMAPPER
-       char *tmp;
-       struct dm_task *dmt = NULL;
-       struct dm_tree *dt = NULL;
-       struct dm_tree_node *root, *child;
-       void *handle = NULL;
-       struct dm_info info;
-       const struct dm_info *child_info;
+       DIR *dp = NULL;
+       struct dirent *ep;
+       char *realp;
+       char *tmp = NULL;
+       char *path = NULL;
+       char *dev_str;
+       int size;
 
-       /*
-        * Disable libdevmapper errors.  It's entirely possible user is not
-        * running devmapper, or that dm_name is not a devmapper device.
-        * That's totally ok, we will just harmlessly and silently return NULL.
-        */
-       disable_libdevmapper_errors();
+       if (dm_name == NULL)
+               return (NULL);
+
+       /* dm name may be a symlink (like /dev/disk/by-vdev/A0) */
+       realp = realpath(dm_name, NULL);
+       if (realp == NULL)
+               return (NULL);
 
        /*
-        * libdevmapper tutorial
-        *
-        * libdevmapper is basically a fancy wrapper for its ioctls.  You
-        * create a "task", fill in the needed info to the task (fill in the
-        * ioctl fields), then run the task (call the ioctl).
-        *
-        * First we need the major/minor number for our DM device.
+        * If they preface 'dev' with a path (like "/dev") then strip it off.
+        * We just want the 'dm-N' part.
         */
-       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
-               goto end;
-
-       /* Lookup the name in libdevmapper */
-       if (!dm_task_set_name(dmt, dm_name)) {
-               enable_libdevmapper_errors();
-               goto end;
-       }
-
-       if (!dm_task_run(dmt))
-               goto end;
-
-       /* Get DM device's major/minor */
-       if (!dm_task_get_info(dmt, &info))
-               goto end;
-
-       /* We have major/minor number.  Lookup the dm device's children */
-       if (!(dt = dm_tree_create()))
-               goto end;
-
-       /* We add the device into the tree and its children get populated */
-       if (!dm_tree_add_dev(dt, info.major, info.minor))
-               goto end;
-
-       if (!(root = dm_tree_find_node(dt, 0, 0)))
-               goto end;
-
-       if (!(child = dm_tree_next_child(&handle, root, 1)))
-               goto end;
+       tmp = strrchr(realp, '/');
+       if (tmp != NULL)
+               dev_str = tmp + 1;    /* +1 since we want the chr after '/' */
+       else
+               dev_str = tmp;
 
-       /* Get child's major/minor numbers */
-       if (!(child_info = dm_tree_node_get_info(child)))
+       size = asprintf(&tmp, "/sys/block/%s/slaves/", dev_str);
+       if (size == -1 || !tmp)
                goto end;
 
-       if ((asprintf(&tmp, "/dev/block/%d:%d", child_info->major,
-           child_info->minor) == -1) || tmp == NULL)
+       dp = opendir(tmp);
+       if (dp == NULL)
                goto end;
 
-       /* Further translate /dev/block/ name into the normal name */
-       name = realpath(tmp, NULL);
-       free(tmp);
+       /* Return first sd* entry in /sys/block/dm-N/slaves/ */
+       while ((ep = readdir(dp))) {
+               if (ep->d_type != DT_DIR) {     /* skip "." and ".." dirs */
+                       size = asprintf(&path, "/dev/%s", ep->d_name);
+                       break;
+               }
+       }
 
 end:
-       if (dmt)
-               dm_task_destroy(dmt);
-       if (dt)
-               dm_tree_free(dt);
-       enable_libdevmapper_errors();
-#endif /* HAVE_LIBDEVMAPPER */
-
-       return (name);
+       if (dp != NULL)
+               closedir(dp);
+       free(tmp);
+       free(realp);
+       return (path);
 }
 
 /*
@@ -4435,13 +4557,41 @@ zfs_dev_is_dm(char *dev_name)
 
        char *tmp;
        tmp = dm_get_underlying_path(dev_name);
-       if (!tmp)
+       if (tmp == NULL)
                return (0);
 
        free(tmp);
        return (1);
 }
 
+/*
+ * By "whole disk" we mean an entire physical disk (something we can
+ * label, toggle the write cache on, etc.) as opposed to the full
+ * capacity of a pseudo-device such as lofi or did.  We act as if we
+ * are labeling the disk, which should be a pretty good test of whether
+ * it's a viable device or not.  Returns B_TRUE if it is and B_FALSE if
+ * it isn't.
+ */
+int
+zfs_dev_is_whole_disk(char *dev_name)
+{
+       struct dk_gpt *label;
+       int fd;
+
+       if ((fd = open(dev_name, O_RDONLY | O_DIRECT)) < 0)
+               return (0);
+
+       if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
+               (void) close(fd);
+               return (0);
+       }
+
+       efi_free(label);
+       (void) close(fd);
+
+       return (1);
+}
+
 /*
  * Lookup the underlying device for a device name
  *
@@ -4488,17 +4638,17 @@ zfs_get_underlying_path(char *dev_name)
        char *name = NULL;
        char *tmp;
 
-       if (!dev_name)
+       if (dev_name == NULL)
                return (NULL);
 
        tmp = dm_get_underlying_path(dev_name);
 
        /* dev_name not a DM device, so just un-symlinkize it */
-       if (!tmp)
+       if (tmp == NULL)
                tmp = realpath(dev_name, NULL);
 
-       if (tmp) {
-               name = zfs_strip_partition(tmp);
+       if (tmp != NULL) {
+               name = zfs_strip_partition_path(tmp);
                free(tmp);
        }
 
@@ -4531,12 +4681,12 @@ zfs_get_enclosure_sysfs_path(char *dev_name)
        size_t size;
        int tmpsize;
 
-       if (!dev_name)
+       if (dev_name == NULL)
                return (NULL);
 
        /* If they preface 'dev' with a path (like "/dev") then strip it off */
        tmp1 = strrchr(dev_name, '/');
-       if (tmp1)
+       if (tmp1 != NULL)
                dev_name = tmp1 + 1;    /* +1 since we want the chr after '/' */
 
        tmpsize = asprintf(&tmp1, "/sys/block/%s/device", dev_name);
@@ -4557,7 +4707,7 @@ zfs_get_enclosure_sysfs_path(char *dev_name)
         */
        while ((ep = readdir(dp))) {
                /* Ignore everything that's not our enclosure_device link */
-               if (!strstr(ep->d_name, "enclosure_device"))
+               if (strstr(ep->d_name, "enclosure_device") == NULL)
                        continue;
 
                if (asprintf(&tmp2, "%s/%s", tmp1, ep->d_name) == -1 ||
@@ -4604,7 +4754,7 @@ end:
        free(tmp2);
        free(tmp1);
 
-       if (dp)
+       if (dp != NULL)
                closedir(dp);
 
        return (path);