Implement database to workaround misreported physical sector sizes
[zfs.git] / cmd / zpool / zpool_vdev.c
index 0c22490..723e10b 100644 (file)
@@ -50,7 +50,7 @@
  *
  *     1. Construct the vdev specification.  Performs syntax validation and
  *         makes sure each device is valid.
- *     2. Check for devices in use.  Using libdiskmgt, makes sure that no
+ *     2. Check for devices in use.  Using libblkid to make sure that no
  *         devices are also in use.  Some can be overridden using the 'force'
  *         flag, others cannot.
  *     3. Check for replication errors if the 'force' flag is not specified.
  */
 
 #include <assert.h>
+#include <ctype.h>
 #include <devid.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <libdiskmgt.h>
 #include <libintl.h>
 #include <libnvpair.h>
 #include <limits.h>
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/stat.h>
 #include <sys/vtoc.h>
 #include <sys/mntent.h>
+#include <uuid/uuid.h>
+#ifdef HAVE_LIBBLKID
+#include <blkid/blkid.h>
+#else
+#define blkid_cache void *
+#endif /* HAVE_LIBBLKID */
 
 #include "zpool_util.h"
-
-#define        DISK_ROOT       "/dev/dsk"
-#define        RDISK_ROOT      "/dev/rdsk"
-#define        BACKUP_SLICE    "s2"
+#include <sys/zfs_context.h>
 
 /*
  * For any given vdev specification, we can have multiple errors.  The
 boolean_t error_seen;
 boolean_t is_force;
 
-/*PRINTFLIKE1*/
-static void
-vdev_error(const char *fmt, ...)
+typedef struct vdev_disk_db_entry
 {
-       va_list ap;
-
-       if (!error_seen) {
-               (void) fprintf(stderr, gettext("invalid vdev specification\n"));
-               if (!is_force)
-                       (void) fprintf(stderr, gettext("use '-f' to override "
-                           "the following errors:\n"));
-               else
-                       (void) fprintf(stderr, gettext("the following errors "
-                           "must be manually repaired:\n"));
-               error_seen = B_TRUE;
-       }
-
-       va_start(ap, fmt);
-       (void) vfprintf(stderr, fmt, ap);
-       va_end(ap);
-}
-
-static void
-libdiskmgt_error(int error)
-{
-       /*
-        * ENXIO/ENODEV is a valid error message if the device doesn't live in
-        * /dev/dsk.  Don't bother printing an error message in this case.
-        */
-       if (error == ENXIO || error == ENODEV)
-               return;
-
-       (void) fprintf(stderr, gettext("warning: device in use checking "
-           "failed: %s\n"), strerror(error));
-}
+       char id[24];
+       int sector_size;
+} vdev_disk_db_entry_t;
 
 /*
- * Validate a device, passing the bulk of the work off to libdiskmgt.
+ * Database of block devices that lie about physical sector sizes.  The
+ * identification string must be precisely 24 characters to avoid false
+ * negatives
  */
-static int
-check_slice(const char *path, int force, boolean_t wholedisk, boolean_t isspare)
-{
-       char *msg;
-       int error = 0;
-       dm_who_type_t who;
-
-       if (force)
-               who = DM_WHO_ZPOOL_FORCE;
-       else if (isspare)
-               who = DM_WHO_ZPOOL_SPARE;
-       else
-               who = DM_WHO_ZPOOL;
-
-       if (dm_inuse((char *)path, &msg, who, &error) || error) {
-               if (error != 0) {
-                       libdiskmgt_error(error);
-                       return (0);
-               } else {
-                       vdev_error("%s", msg);
-                       free(msg);
-                       return (-1);
-               }
-       }
-
-       /*
-        * If we're given a whole disk, ignore overlapping slices since we're
-        * about to label it anyway.
-        */
-       error = 0;
-       if (!wholedisk && !force &&
-           (dm_isoverlapping((char *)path, &msg, &error) || error)) {
-               if (error == 0) {
-                       /* dm_isoverlapping returned -1 */
-                       vdev_error(gettext("%s overlaps with %s\n"), path, msg);
-                       free(msg);
-                       return (-1);
-               } else if (error != ENODEV) {
-                       /* libdiskmgt's devcache only handles physical drives */
-                       libdiskmgt_error(error);
-                       return (0);
-               }
-       }
-
-       return (0);
-}
-
+static vdev_disk_db_entry_t vdev_disk_database[] = {
+       {"ATA     Corsair Force 3 ", 8192},
+       {"ATA     INTEL SSDSA2CT04", 8192},
+       {"ATA     INTEL SSDSA2CW16", 8192},
+       {"ATA     INTEL SSDSC2CT18", 8192},
+       {"ATA     INTEL SSDSC2CW12", 8192},
+       {"ATA     KINGSTON SH100S3", 8192},
+       {"ATA     M4-CT064M4SSD2  ", 8192},
+       {"ATA     M4-CT128M4SSD2  ", 8192},
+       {"ATA     M4-CT256M4SSD2  ", 8192},
+       {"ATA     M4-CT512M4SSD2  ", 8192},
+       {"ATA     OCZ-AGILITY2    ", 8192},
+       {"ATA     OCZ-VERTEX2 3.5 ", 8192},
+       {"ATA     OCZ-VERTEX3     ", 8192},
+       {"ATA     OCZ-VERTEX3 LT  ", 8192},
+       {"ATA     OCZ-VERTEX3 MI  ", 8192},
+       {"ATA     SAMSUNG SSD 830 ", 8192},
+       {"ATA     Samsung SSD 840 ", 8192},
+       {"ATA     INTEL SSDSA2M040", 4096},
+       {"ATA     INTEL SSDSA2M080", 4096},
+       {"ATA     INTEL SSDSA2M160", 4096},
+       /* Imported from Open Solaris*/
+       {"ATA     MARVELL SD88SA02", 4096},
+       /* Advanced format Hard drives */
+       {"ATA     Hitachi HDS5C303", 4096},
+       {"ATA     SAMSUNG HD204UI ", 4096},
+       {"ATA     ST2000DL004 HD20", 4096},
+       {"ATA     WDC WD10EARS-00M", 4096},
+       {"ATA     WDC WD10EARS-00S", 4096},
+       {"ATA     WDC WD10EARS-00Z", 4096},
+       {"ATA     WDC WD15EARS-00M", 4096},
+       {"ATA     WDC WD15EARS-00S", 4096},
+       {"ATA     WDC WD15EARS-00Z", 4096},
+       {"ATA     WDC WD20EARS-00M", 4096},
+       {"ATA     WDC WD20EARS-00S", 4096},
+       {"ATA     WDC WD20EARS-00Z", 4096},
+       /* Virtual disks: Assume zvols with default volblocksize */
+#if 0
+       {"ATA     QEMU HARDDISK   ", 8192},
+       {"IET     VIRTUAL-DISK    ", 8192},
+       {"OI      COMSTAR         ", 8192},
+#endif
+};
+
+static const int vdev_disk_database_size =
+       sizeof (vdev_disk_database) / sizeof (vdev_disk_database[0]);
+
+#define        INQ_REPLY_LEN   96
+#define        INQ_CMD_LEN     6
 
-/*
- * Validate a whole disk.  Iterate over all slices on the disk and make sure
- * that none is in use by calling check_slice().
- */
-static int
-check_disk(const char *name, dm_descriptor_t disk, int force, int isspare)
+static boolean_t
+check_sector_size_database(char *path, int *sector_size)
 {
-       dm_descriptor_t *drive, *media, *slice;
-       int err = 0;
+       unsigned char inq_buff[INQ_REPLY_LEN];
+       unsigned char sense_buffer[32];
+       unsigned char inq_cmd_blk[INQ_CMD_LEN] =
+           {INQUIRY, 0, 0, 0, INQ_REPLY_LEN, 0};
+       sg_io_hdr_t io_hdr;
+       int error;
+       int fd;
        int i;
-       int ret;
-
-       /*
-        * Get the drive associated with this disk.  This should never fail,
-        * because we already have an alias handle open for the device.
-        */
-       if ((drive = dm_get_associated_descriptors(disk, DM_DRIVE,
-           &err)) == NULL || *drive == NULL) {
-               if (err)
-                       libdiskmgt_error(err);
-               return (0);
-       }
 
-       if ((media = dm_get_associated_descriptors(*drive, DM_MEDIA,
-           &err)) == NULL) {
-               dm_free_descriptors(drive);
-               if (err)
-                       libdiskmgt_error(err);
-               return (0);
-       }
-
-       dm_free_descriptors(drive);
-
-       /*
-        * It is possible that the user has specified a removable media drive,
-        * and the media is not present.
-        */
-       if (*media == NULL) {
-               dm_free_descriptors(media);
-               vdev_error(gettext("'%s' has no media in drive\n"), name);
-               return (-1);
-       }
+       /* Prepare INQUIRY command */
+       memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+       io_hdr.interface_id = 'S';
+       io_hdr.cmd_len = sizeof(inq_cmd_blk);
+       io_hdr.mx_sb_len = sizeof(sense_buffer);
+       io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+       io_hdr.dxfer_len = INQ_REPLY_LEN;
+       io_hdr.dxferp = inq_buff;
+       io_hdr.cmdp = inq_cmd_blk;
+       io_hdr.sbp = sense_buffer;
+       io_hdr.timeout = 10;        /* 10 milliseconds is ample time */
+
+       if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0)
+               return (B_FALSE);
 
-       if ((slice = dm_get_associated_descriptors(*media, DM_SLICE,
-           &err)) == NULL) {
-               dm_free_descriptors(media);
-               if (err)
-                       libdiskmgt_error(err);
-               return (0);
-       }
+       error = ioctl(fd, SG_IO, (unsigned long) &io_hdr);
 
-       dm_free_descriptors(media);
+       (void) close(fd);
 
-       ret = 0;
+       if (error < 0)
+               return (B_FALSE);
 
-       /*
-        * Iterate over all slices and report any errors.  We don't care about
-        * overlapping slices because we are using the whole disk.
-        */
-       for (i = 0; slice[i] != NULL; i++) {
-               char *name = dm_get_name(slice[i], &err);
+       if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
+               return (B_FALSE);
 
-               if (check_slice(name, force, B_TRUE, isspare) != 0)
-                       ret = -1;
+       for (i = 0; i < vdev_disk_database_size; i++) {
+               if (memcmp(inq_buff + 8, vdev_disk_database[i].id, 24))
+                       continue;
 
-               dm_free_name(name);
+               *sector_size = vdev_disk_database[i].sector_size;
+               return (B_TRUE);
        }
 
-       dm_free_descriptors(slice);
-       return (ret);
+       return (B_FALSE);
 }
 
-/*
- * Validate a device.
- */
-static int
-check_device(const char *path, boolean_t force, boolean_t isspare)
+/*PRINTFLIKE1*/
+static void
+vdev_error(const char *fmt, ...)
 {
-       dm_descriptor_t desc;
-       int err;
-       char *dev;
+       va_list ap;
 
-       /*
-        * For whole disks, libdiskmgt does not include the leading dev path.
-        */
-       dev = strrchr(path, '/');
-       assert(dev != NULL);
-       dev++;
-       if ((desc = dm_get_descriptor_by_name(DM_ALIAS, dev, &err)) != NULL) {
-               err = check_disk(path, desc, force, isspare);
-               dm_free_descriptor(desc);
-               return (err);
+       if (!error_seen) {
+               (void) fprintf(stderr, gettext("invalid vdev specification\n"));
+               if (!is_force)
+                       (void) fprintf(stderr, gettext("use '-f' to override "
+                           "the following errors:\n"));
+               else
+                       (void) fprintf(stderr, gettext("the following errors "
+                           "must be manually repaired:\n"));
+               error_seen = B_TRUE;
        }
 
-       return (check_slice(path, force, B_FALSE, isspare));
+       va_start(ap, fmt);
+       (void) vfprintf(stderr, fmt, ap);
+       va_end(ap);
 }
 
 /*
@@ -283,19 +235,9 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
        char  *name;
        int fd;
        int ret = 0;
-       int err;
        pool_state_t state;
        boolean_t inuse;
 
-       if (dm_inuse_swap(file, &err)) {
-               if (err)
-                       libdiskmgt_error(err);
-               else
-                       vdev_error(gettext("%s is currently used by swap. "
-                           "Please see swap(1M).\n"), file);
-               return (-1);
-       }
-
        if ((fd = open(file, O_RDONLY)) < 0)
                return (0);
 
@@ -348,6 +290,166 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
        return (ret);
 }
 
+static void
+check_error(int err)
+{
+       (void) fprintf(stderr, gettext("warning: device in use checking "
+           "failed: %s\n"), strerror(err));
+}
+
+static int
+check_slice(const char *path, blkid_cache cache, int force, boolean_t isspare)
+{
+       int err;
+#ifdef HAVE_LIBBLKID
+       char *value;
+
+       /* No valid type detected device is safe to use */
+       value = blkid_get_tag_value(cache, "TYPE", path);
+       if (value == NULL)
+               return (0);
+
+       /*
+        * If libblkid detects a ZFS device, we check the device
+        * using check_file() to see if it's safe.  The one safe
+        * case is a spare device shared between multiple pools.
+        */
+       if (strcmp(value, "zfs") == 0) {
+               err = check_file(path, force, isspare);
+       } else {
+               if (force) {
+                       err = 0;
+               } else {
+                       err = -1;
+                       vdev_error(gettext("%s contains a filesystem of "
+                                  "type '%s'\n"), path, value);
+               }
+       }
+
+       free(value);
+#else
+       err = check_file(path, force, isspare);
+#endif /* HAVE_LIBBLKID */
+
+       return (err);
+}
+
+/*
+ * Validate a whole disk.  Iterate over all slices on the disk and make sure
+ * that none is in use by calling check_slice().
+ */
+static int
+check_disk(const char *path, blkid_cache cache, int force,
+          boolean_t isspare, boolean_t iswholedisk)
+{
+       struct dk_gpt *vtoc;
+       char slice_path[MAXPATHLEN];
+       int err = 0;
+       int fd, i;
+
+       /* This is not a wholedisk we only check the given partition */
+       if (!iswholedisk)
+               return check_slice(path, cache, force, isspare);
+
+       /*
+        * When the device is a whole disk try to read the efi partition
+        * label.  If this is successful we safely check the all of the
+        * partitions.  However, when it fails it may simply be because
+        * the disk is partitioned via the MBR.  Since we currently can
+        * not easily decode the MBR return a failure and prompt to the
+        * user to use force option since we cannot check the partitions.
+        */
+       if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0) {
+               check_error(errno);
+               return -1;
+       }
+
+       if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
+               (void) close(fd);
+
+               if (force) {
+                       return 0;
+               } else {
+                       vdev_error(gettext("%s does not contain an EFI "
+                           "label but it may contain partition\n"
+                           "information in the MBR.\n"), path);
+                       return -1;
+               }
+       }
+
+       /*
+        * The primary efi partition label is damaged however the secondary
+        * label at the end of the device is intact.  Rather than use this
+        * label we should play it safe and treat this as a non efi device.
+        */
+       if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) {
+               efi_free(vtoc);
+               (void) close(fd);
+
+               if (force) {
+                       /* Partitions will no be created using the backup */
+                       return 0;
+               } else {
+                       vdev_error(gettext("%s contains a corrupt primary "
+                           "EFI label.\n"), path);
+                       return -1;
+               }
+       }
+
+       for (i = 0; i < vtoc->efi_nparts; i++) {
+
+               if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED ||
+                   uuid_is_null((uchar_t *)&vtoc->efi_parts[i].p_guid))
+                       continue;
+
+               if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
+                       (void) snprintf(slice_path, sizeof (slice_path),
+                           "%s%s%d", path, "-part", i+1);
+               else
+                       (void) snprintf(slice_path, sizeof (slice_path),
+                           "%s%s%d", path, isdigit(path[strlen(path)-1]) ?
+                           "p" : "", i+1);
+
+               err = check_slice(slice_path, cache, force, isspare);
+               if (err)
+                       break;
+       }
+
+       efi_free(vtoc);
+       (void) close(fd);
+
+       return (err);
+}
+
+static int
+check_device(const char *path, boolean_t force,
+            boolean_t isspare, boolean_t iswholedisk)
+{
+       static blkid_cache cache = NULL;
+
+#ifdef HAVE_LIBBLKID
+       /*
+        * There is no easy way to add a correct blkid_put_cache() call,
+        * memory will be reclaimed when the command exits.
+        */
+       if (cache == NULL) {
+               int err;
+
+               if ((err = blkid_get_cache(&cache, NULL)) != 0) {
+                       check_error(err);
+                       return -1;
+               }
+
+               if ((err = blkid_probe_all(cache)) != 0) {
+                       blkid_put_cache(cache);
+                       check_error(err);
+                       return -1;
+               }
+       }
+#endif /* HAVE_LIBBLKID */
+
+       return check_disk(path, cache, force, isspare, iswholedisk);
+}
 
 /*
  * By "whole disk" we mean an entire physical disk (something we can
@@ -358,15 +460,12 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
  * it isn't.
  */
 static boolean_t
-is_whole_disk(const char *arg)
+is_whole_disk(const char *path)
 {
        struct dk_gpt *label;
        int     fd;
-       char    path[MAXPATHLEN];
 
-       (void) snprintf(path, sizeof (path), "%s%s%s",
-           RDISK_ROOT, strrchr(arg, '/'), BACKUP_SLICE);
-       if ((fd = open(path, O_RDWR | O_NDELAY)) < 0)
+       if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0)
                return (B_FALSE);
        if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
                (void) close(fd);
@@ -378,22 +477,102 @@ is_whole_disk(const char *arg)
 }
 
 /*
+ * This may be a shorthand device path or it could be total gibberish.
+ * Check to see if it is a known device available in zfs_vdev_paths.
+ * As part of this check, see if we've been given an entire disk
+ * (minus the slice number).
+ */
+static int
+is_shorthand_path(const char *arg, char *path,
+                  struct stat64 *statbuf, boolean_t *wholedisk)
+{
+       int error;
+
+       error = zfs_resolve_shortname(arg, path, MAXPATHLEN);
+       if (error == 0) {
+               *wholedisk = is_whole_disk(path);
+               if (*wholedisk || (stat64(path, statbuf) == 0))
+                       return (0);
+       }
+
+       strlcpy(path, arg, sizeof(path));
+       memset(statbuf, 0, sizeof(*statbuf));
+       *wholedisk = B_FALSE;
+
+       return (error);
+}
+
+/*
+ * Determine if the given path is a hot spare within the given configuration.
+ * If no configuration is given we rely solely on the label.
+ */
+static boolean_t
+is_spare(nvlist_t *config, const char *path)
+{
+       int fd;
+       pool_state_t state;
+       char *name = NULL;
+       nvlist_t *label;
+       uint64_t guid, spareguid;
+       nvlist_t *nvroot;
+       nvlist_t **spares;
+       uint_t i, nspares;
+       boolean_t inuse;
+
+       if ((fd = open(path, O_RDONLY)) < 0)
+               return (B_FALSE);
+
+       if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
+           !inuse ||
+           state != POOL_STATE_SPARE ||
+           zpool_read_label(fd, &label) != 0) {
+               free(name);
+               (void) close(fd);
+               return (B_FALSE);
+       }
+       free(name);
+       (void) close(fd);
+
+       if (config == NULL)
+               return (B_TRUE);
+
+       verify(nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) == 0);
+       nvlist_free(label);
+
+       verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+           &nvroot) == 0);
+       if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
+           &spares, &nspares) == 0) {
+               for (i = 0; i < nspares; i++) {
+                       verify(nvlist_lookup_uint64(spares[i],
+                           ZPOOL_CONFIG_GUID, &spareguid) == 0);
+                       if (spareguid == guid)
+                               return (B_TRUE);
+               }
+       }
+
+       return (B_FALSE);
+}
+
+/*
  * Create a leaf vdev.  Determine if this is a file or a device.  If it's a
  * device, fill in the device id to make a complete nvlist.  Valid forms for a
  * leaf vdev are:
  *
- *     /dev/dsk/xxx    Complete disk path
- *     /xxx            Full path to file
- *     xxx             Shorthand for /dev/dsk/xxx
+ *     /dev/xxx        Complete disk path
+ *     /xxx            Full path to file
+ *     xxx             Shorthand for <zfs_vdev_paths>/xxx
  */
 static nvlist_t *
-make_leaf_vdev(const char *arg, uint64_t is_log)
+make_leaf_vdev(nvlist_t *props, const char *arg, uint64_t is_log)
 {
        char path[MAXPATHLEN];
        struct stat64 statbuf;
        nvlist_t *vdev = NULL;
        char *type = NULL;
        boolean_t wholedisk = B_FALSE;
+       uint64_t ashift = 0;
+       int err;
 
        /*
         * Determine what type of vdev this is, and put the full path into
@@ -403,28 +582,31 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
        if (arg[0] == '/') {
                /*
                 * Complete device or file path.  Exact type is determined by
-                * examining the file descriptor afterwards.
+                * examining the file descriptor afterwards.  Symbolic links
+                * are resolved to their real paths for the is_whole_disk()
+                * and S_ISBLK/S_ISREG type checks.  However, we are careful
+                * to store the given path as ZPOOL_CONFIG_PATH to ensure we
+                * can leverage udev's persistent device labels.
                 */
-               wholedisk = is_whole_disk(arg);
-               if (!wholedisk && (stat64(arg, &statbuf) != 0)) {
+               if (realpath(arg, path) == NULL) {
                        (void) fprintf(stderr,
-                           gettext("cannot open '%s': %s\n"),
-                           arg, strerror(errno));
+                           gettext("cannot resolve path '%s'\n"), arg);
                        return (NULL);
                }
 
-               (void) strlcpy(path, arg, sizeof (path));
-       } else {
-               /*
-                * This may be a short path for a device, or it could be total
-                * gibberish.  Check to see if it's a known device in
-                * /dev/dsk/.  As part of this check, see if we've been given a
-                * an entire disk (minus the slice number).
-                */
-               (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT,
-                   arg);
                wholedisk = is_whole_disk(path);
                if (!wholedisk && (stat64(path, &statbuf) != 0)) {
+                       (void) fprintf(stderr,
+                           gettext("cannot open '%s': %s\n"),
+                           path, strerror(errno));
+                       return (NULL);
+               }
+
+               /* After is_whole_disk() check restore original passed path */
+               strlcpy(path, arg, MAXPATHLEN);
+       } else {
+               err = is_shorthand_path(arg, path, &statbuf, &wholedisk);
+               if (err != 0) {
                        /*
                         * If we got ENOENT, then the user gave us
                         * gibberish, so try to direct them with a
@@ -432,7 +614,7 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
                         * regurgitate strerror() since it's the best we
                         * can do.
                         */
-                       if (errno == ENOENT) {
+                       if (err == ENOENT) {
                                (void) fprintf(stderr,
                                    gettext("cannot open '%s': no such "
                                    "device in %s\n"), arg, DISK_ROOT);
@@ -476,40 +658,30 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
                    (uint64_t)wholedisk) == 0);
 
        /*
-        * For a whole disk, defer getting its devid until after labeling it.
+        * Override defaults if custom properties are provided.
         */
-       if (S_ISBLK(statbuf.st_mode) && !wholedisk) {
-               /*
-                * Get the devid for the device.
-                */
-               int fd;
-               ddi_devid_t devid;
-               char *minor = NULL, *devid_str = NULL;
-
-               if ((fd = open(path, O_RDONLY)) < 0) {
-                       (void) fprintf(stderr, gettext("cannot open '%s': "
-                           "%s\n"), path, strerror(errno));
-                       nvlist_free(vdev);
-                       return (NULL);
-               }
+       if (props != NULL) {
+               char *value = NULL;
 
-               if (devid_get(fd, &devid) == 0) {
-                       if (devid_get_minor_name(fd, &minor) == 0 &&
-                           (devid_str = devid_str_encode(devid, minor)) !=
-                           NULL) {
-                               verify(nvlist_add_string(vdev,
-                                   ZPOOL_CONFIG_DEVID, devid_str) == 0);
-                       }
-                       if (devid_str != NULL)
-                               devid_str_free(devid_str);
-                       if (minor != NULL)
-                               devid_str_free(minor);
-                       devid_free(devid);
-               }
+               if (nvlist_lookup_string(props,
+                   zpool_prop_to_name(ZPOOL_PROP_ASHIFT), &value) == 0)
+                       zfs_nicestrtonum(NULL, value, &ashift);
+       }
 
-               (void) close(fd);
+       /*
+        * If the device is known to incorrectly report its physical sector
+        * size explicitly provide the known correct value.
+        */
+       if (ashift == 0) {
+               int sector_size;
+
+               if (check_sector_size_database(path, &sector_size) == B_TRUE)
+                       ashift = highbit(sector_size) - 1;
        }
 
+       if (ashift > 0)
+               nvlist_add_uint64(vdev, ZPOOL_CONFIG_ASHIFT, ashift);
+
        return (vdev);
 }
 
@@ -871,6 +1043,39 @@ check_replication(nvlist_t *config, nvlist_t *newroot)
        return (ret);
 }
 
+static int
+zero_label(char *path)
+{
+       const int size = 4096;
+       char buf[size];
+       int err, fd;
+
+       if ((fd = open(path, O_WRONLY|O_EXCL)) < 0) {
+               (void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
+                   path, strerror(errno));
+               return (-1);
+       }
+
+       memset(buf, 0, size);
+       err = write(fd, buf, size);
+       (void) fdatasync(fd);
+       (void) close(fd);
+
+       if (err == -1) {
+               (void) fprintf(stderr, gettext("cannot zero first %d bytes "
+                   "of '%s': %s\n"), size, path, strerror(errno));
+               return (-1);
+       }
+
+       if (err != size) {
+               (void) fprintf(stderr, gettext("could only zero %d/%d bytes "
+                   "of '%s'\n"), err, size, path);
+               return (-1);
+       }
+
+       return 0;
+}
+
 /*
  * Go through and find any whole disks in the vdev specification, labelling them
  * as appropriate.  When constructing the vdev spec, we were unable to open this
@@ -886,13 +1091,14 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
 {
        nvlist_t **child;
        uint_t c, children;
-       char *type, *path, *diskname;
-       char buf[MAXPATHLEN];
+       char *type, *path;
+       char devpath[MAXPATHLEN];
+       char udevpath[MAXPATHLEN];
        uint64_t wholedisk;
+       struct stat64 statbuf;
+       int is_exclusive = 0;
        int fd;
        int ret;
-       ddi_devid_t devid;
-       char *minor = NULL, *devid_str = NULL;
 
        verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
 
@@ -903,55 +1109,87 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
                        return (0);
 
                /*
-                * We have a disk device.  Get the path to the device
-                * and see if it's a whole disk by appending the backup
-                * slice and stat()ing the device.
+                * We have a disk device.  If this is a whole disk write
+                * out the efi partition table, otherwise write zero's to
+                * the first 4k of the partition.  This is to ensure that
+                * libblkid will not misidentify the partition due to a
+                * magic value left by the previous filesystem.
                 */
-               verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
-               if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
-                   &wholedisk) != 0 || !wholedisk)
+               verify(!nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path));
+               verify(!nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
+                   &wholedisk));
+
+               if (!wholedisk) {
+                       (void) zero_label(path);
                        return (0);
+               }
 
-               diskname = strrchr(path, '/');
-               assert(diskname != NULL);
-               diskname++;
-               if (zpool_label_disk(g_zfs, zhp, diskname) == -1)
-                       return (-1);
+               if (realpath(path, devpath) == NULL) {
+                       ret = errno;
+                       (void) fprintf(stderr,
+                           gettext("cannot resolve path '%s'\n"), path);
+                       return (ret);
+               }
 
                /*
-                * Fill in the devid, now that we've labeled the disk.
+                * Remove any previously existing symlink from a udev path to
+                * the device before labeling the disk.  This makes
+                * zpool_label_disk_wait() truly wait for the new link to show
+                * up instead of returning if it finds an old link still in
+                * place.  Otherwise there is a window between when udev
+                * deletes and recreates the link during which access attempts
+                * will fail with ENOENT.
                 */
-               (void) snprintf(buf, sizeof (buf), "%ss0", path);
-               if ((fd = open(buf, O_RDONLY)) < 0) {
-                       (void) fprintf(stderr,
-                           gettext("cannot open '%s': %s\n"),
-                           buf, strerror(errno));
-                       return (-1);
+               strncpy(udevpath, path, MAXPATHLEN);
+               (void) zfs_append_partition(udevpath, MAXPATHLEN);
+
+               fd = open(devpath, O_RDWR|O_EXCL);
+               if (fd == -1) {
+                       if (errno == EBUSY)
+                               is_exclusive = 1;
+               } else {
+                       (void) close(fd);
                }
 
-               if (devid_get(fd, &devid) == 0) {
-                       if (devid_get_minor_name(fd, &minor) == 0 &&
-                           (devid_str = devid_str_encode(devid, minor)) !=
-                           NULL) {
-                               verify(nvlist_add_string(nv,
-                                   ZPOOL_CONFIG_DEVID, devid_str) == 0);
+               /*
+                * If the partition exists, contains a valid spare label,
+                * and is opened exclusively there is no need to partition
+                * it.  Hot spares have already been partitioned and are
+                * held open exclusively by the kernel as a safety measure.
+                *
+                * If the provided path is for a /dev/disk/ device its
+                * symbolic link will be removed, partition table created,
+                * and then block until udev creates the new link.
+                */
+               if (!is_exclusive || !is_spare(NULL, udevpath)) {
+                       ret = strncmp(udevpath,UDISK_ROOT,strlen(UDISK_ROOT));
+                       if (ret == 0) {
+                               ret = lstat64(udevpath, &statbuf);
+                               if (ret == 0 && S_ISLNK(statbuf.st_mode))
+                                       (void) unlink(udevpath);
                        }
-                       if (devid_str != NULL)
-                               devid_str_free(devid_str);
-                       if (minor != NULL)
-                               devid_str_free(minor);
-                       devid_free(devid);
+
+                       if (zpool_label_disk(g_zfs, zhp,
+                           strrchr(devpath, '/') + 1) == -1)
+                               return (-1);
+
+                       ret = zpool_label_disk_wait(udevpath, 1000);
+                       if (ret) {
+                               (void) fprintf(stderr, gettext("cannot "
+                                   "resolve path '%s': %d\n"), udevpath, ret);
+                               return (-1);
+                       }
+
+                       (void) zero_label(udevpath);
                }
 
                /*
-                * Update the path to refer to the 's0' slice.  The presence of
+                * Update the path to refer to the partition.  The presence of
                 * the 'whole_disk' field indicates to the CLI that we should
-                * chop off the slice number when displaying the device in
+                * chop off the partition number when displaying the device in
                 * future output.
                 */
-               verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, buf) == 0);
-
-               (void) close(fd);
+               verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, udevpath) == 0);
 
                return (0);
        }
@@ -976,54 +1214,6 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
 }
 
 /*
- * Determine if the given path is a hot spare within the given configuration.
- */
-static boolean_t
-is_spare(nvlist_t *config, const char *path)
-{
-       int fd;
-       pool_state_t state;
-       char *name = NULL;
-       nvlist_t *label;
-       uint64_t guid, spareguid;
-       nvlist_t *nvroot;
-       nvlist_t **spares;
-       uint_t i, nspares;
-       boolean_t inuse;
-
-       if ((fd = open(path, O_RDONLY)) < 0)
-               return (B_FALSE);
-
-       if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
-           !inuse ||
-           state != POOL_STATE_SPARE ||
-           zpool_read_label(fd, &label) != 0) {
-               free(name);
-               (void) close(fd);
-               return (B_FALSE);
-       }
-       free(name);
-       (void) close(fd);
-
-       verify(nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) == 0);
-       nvlist_free(label);
-
-       verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
-           &nvroot) == 0);
-       if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
-           &spares, &nspares) == 0) {
-               for (i = 0; i < nspares; i++) {
-                       verify(nvlist_lookup_uint64(spares[i],
-                           ZPOOL_CONFIG_GUID, &spareguid) == 0);
-                       if (spareguid == guid)
-                               return (B_TRUE);
-               }
-       }
-
-       return (B_FALSE);
-}
-
-/*
  * Go through and find any devices that are in use.  We rely on libdiskmgt for
  * the majority of this task.
  */
@@ -1034,36 +1224,39 @@ check_in_use(nvlist_t *config, nvlist_t *nv, boolean_t force,
        nvlist_t **child;
        uint_t c, children;
        char *type, *path;
-       int ret;
+       int ret = 0;
        char buf[MAXPATHLEN];
-       uint64_t wholedisk;
+       uint64_t wholedisk = B_FALSE;
 
        verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
 
        if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
            &child, &children) != 0) {
 
-               verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
+               verify(!nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path));
+               if (strcmp(type, VDEV_TYPE_DISK) == 0)
+                       verify(!nvlist_lookup_uint64(nv,
+                              ZPOOL_CONFIG_WHOLE_DISK, &wholedisk));
 
                /*
                 * As a generic check, we look to see if this is a replace of a
                 * hot spare within the same pool.  If so, we allow it
-                * regardless of what libdiskmgt or zpool_in_use() says.
+                * regardless of what libblkid or zpool_in_use() says.
                 */
                if (replacing) {
-                       if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
-                           &wholedisk) == 0 && wholedisk)
-                               (void) snprintf(buf, sizeof (buf), "%ss0",
-                                   path);
-                       else
-                               (void) strlcpy(buf, path, sizeof (buf));
+                       (void) strlcpy(buf, path, sizeof (buf));
+                       if (wholedisk) {
+                               ret = zfs_append_partition(buf,  sizeof (buf));
+                               if (ret == -1)
+                                       return (-1);
+                       }
 
                        if (is_spare(config, buf))
                                return (0);
                }
 
                if (strcmp(type, VDEV_TYPE_DISK) == 0)
-                       ret = check_device(path, force, isspare);
+                       ret = check_device(path, force, isspare, wholedisk);
 
                if (strcmp(type, VDEV_TYPE_FILE) == 0)
                        ret = check_file(path, force, isspare);
@@ -1157,7 +1350,7 @@ is_grouping(const char *type, int *mindev, int *maxdev)
  * because the program is just going to exit anyway.
  */
 nvlist_t *
-construct_spec(int argc, char **argv)
+construct_spec(nvlist_t *props, int argc, char **argv)
 {
        nvlist_t *nvroot, *nv, **top, **spares, **l2cache;
        int t, toplevels, mindev, maxdev, nspares, nlogs, nl2cache;
@@ -1246,7 +1439,7 @@ construct_spec(int argc, char **argv)
                                    children * sizeof (nvlist_t *));
                                if (child == NULL)
                                        zpool_no_memory();
-                               if ((nv = make_leaf_vdev(argv[c], B_FALSE))
+                               if ((nv = make_leaf_vdev(props, argv[c], B_FALSE))
                                    == NULL)
                                        return (NULL);
                                child[children - 1] = nv;
@@ -1302,7 +1495,7 @@ construct_spec(int argc, char **argv)
                         * We have a device.  Pass off to make_leaf_vdev() to
                         * construct the appropriate nvlist describing the vdev.
                         */
-                       if ((nv = make_leaf_vdev(argv[0], is_log)) == NULL)
+                       if ((nv = make_leaf_vdev(props, argv[0], is_log)) == NULL)
                                return (NULL);
                        if (is_log)
                                nlogs++;
@@ -1368,7 +1561,7 @@ split_mirror_vdev(zpool_handle_t *zhp, char *newname, nvlist_t *props,
        uint_t c, children;
 
        if (argc > 0) {
-               if ((newroot = construct_spec(argc, argv)) == NULL) {
+               if ((newroot = construct_spec(props, argc, argv)) == NULL) {
                        (void) fprintf(stderr, gettext("Unable to build a "
                            "pool from the specified devices\n"));
                        return (NULL);
@@ -1418,7 +1611,7 @@ split_mirror_vdev(zpool_handle_t *zhp, char *newname, nvlist_t *props,
  * added, even if they appear in use.
  */
 nvlist_t *
-make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
+make_root_vdev(zpool_handle_t *zhp, nvlist_t *props, int force, int check_rep,
     boolean_t replacing, boolean_t dryrun, int argc, char **argv)
 {
        nvlist_t *newroot;
@@ -1430,7 +1623,7 @@ make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
         * that we have a valid specification, and that all devices can be
         * opened.
         */
-       if ((newroot = construct_spec(argc, argv)) == NULL)
+       if ((newroot = construct_spec(props, argc, argv)) == NULL)
                return (NULL);
 
        if (zhp && ((poolconfig = zpool_get_config(zhp, NULL)) == NULL))