Allow 'zpool replace' to use short device names
[zfs.git] / cmd / zpool / zpool_vdev.c
index febdda9..da121f0 100644 (file)
@@ -188,19 +188,10 @@ check_error(int err)
 static int
 check_slice(const char *path, blkid_cache cache, int force, boolean_t isspare)
 {
-       struct stat64 statbuf;
        int err;
 #ifdef HAVE_LIBBLKID
        char *value;
-#endif /* HAVE_LIBBLKID */
 
-       if (stat64(path, &statbuf) != 0) {
-               vdev_error(gettext("cannot stat %s: %s\n"),
-                          path, strerror(errno));
-               return (-1);
-       }
-
-#ifdef HAVE_LIBBLKID
        /* No valid type detected device is safe to use */
        value = blkid_get_tag_value(cache, "TYPE", path);
        if (value == NULL)
@@ -375,32 +366,20 @@ is_whole_disk(const char *path)
 
 /*
  * This may be a shorthand device path or it could be total gibberish.
- * Check to see if it's a known device in /dev/, /dev/disk/by-id,
- * /dev/disk/by-label, /dev/disk/by-path, /dev/disk/by-uuid, or
- * /dev/disk/zpool/.  As part of this check, see if we've been given
- * an entire disk (minus the slice number).
+ * 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)
 {
-       char dirs[5][8] = {"by-id", "by-label", "by-path", "by-uuid", "zpool"};
-       int i, err;
-
-       /* /dev/<name> */
-       (void) snprintf(path, MAXPATHLEN, "%s/%s", DISK_ROOT, arg);
-       *wholedisk = is_whole_disk(path);
-       err = stat64(path, statbuf);
-       if (*wholedisk || err == 0)
-               return (0);
+       int error;
 
-       /* /dev/disk/<dirs>/<name> */
-       for (i = 0; i < 5; i++) {
-               (void) snprintf(path, MAXPATHLEN, "%s/%s/%s",
-                   UDISK_ROOT, dirs[i], arg);
+       error = zfs_resolve_shortname(arg, path, MAXPATHLEN);
+       if (error == 0) {
                *wholedisk = is_whole_disk(path);
-               err = stat64(path, statbuf);
-               if (*wholedisk || err == 0)
+               if (*wholedisk || (stat64(path, statbuf) == 0))
                        return (0);
        }
 
@@ -408,7 +387,7 @@ is_shorthand_path(const char *arg, char *path,
        memset(statbuf, 0, sizeof(*statbuf));
        *wholedisk = B_FALSE;
 
-       return (ENOENT);
+       return (error);
 }
 
 /*
@@ -416,12 +395,12 @@ is_shorthand_path(const char *arg, char *path,
  * device, fill in the device id to make a complete nvlist.  Valid forms for a
  * leaf vdev are:
  *
- *     /dev/xxx        Complete disk path
- *     /xxx            Full path to file
- *     xxx             Shorthand for /dev/disk/yyy/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;
@@ -513,42 +492,18 @@ make_leaf_vdev(const char *arg, uint64_t is_log)
                verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
                    (uint64_t)wholedisk) == 0);
 
-#if defined(__sun__) || defined(__sun)
-       /*
-        * For a whole disk, defer getting its devid until after labeling it.
-        */
-       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|O_EXCL)) < 0) {
-                       (void) fprintf(stderr, gettext("cannot open '%s': "
-                           "%s\n"), path, strerror(errno));
-                       nvlist_free(vdev);
-                       return (NULL);
-               }
+       if (props != NULL) {
+               uint64_t ashift = 0;
+               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 (ashift > 0)
+                       verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_ASHIFT,
+                           ashift) == 0);
        }
-#endif
 
        return (vdev);
 }
@@ -960,8 +915,10 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
        nvlist_t **child;
        uint_t c, children;
        char *type, *path, *diskname;
-       char buf[MAXPATHLEN];
+       char devpath[MAXPATHLEN];
+       char udevpath[MAXPATHLEN];
        uint64_t wholedisk;
+       struct stat64 statbuf;
        int ret;
 
        verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
@@ -988,51 +945,57 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
                        return (ret);
                }
 
-               if (realpath(path, buf) == NULL) {
+               if (realpath(path, devpath) == NULL) {
                        ret = errno;
                        (void) fprintf(stderr,
                            gettext("cannot resolve path '%s'\n"), path);
                        return (ret);
                }
 
-               diskname = strrchr(buf, '/');
+               /*
+                * 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.
+                */
+               strncpy(udevpath, path, MAXPATHLEN);
+               (void) zfs_append_partition(udevpath, MAXPATHLEN);
+
+               if ((strncmp(udevpath, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) &&
+                   (lstat64(udevpath, &statbuf) == 0) &&
+                   S_ISLNK(statbuf.st_mode))
+                       (void) unlink(udevpath);
+
+               diskname = strrchr(devpath, '/');
                assert(diskname != NULL);
                diskname++;
                if (zpool_label_disk(g_zfs, zhp, diskname) == -1)
                        return (-1);
 
                /*
-                * Now the we've labeled the disk and the partitions have
-                * been created.  We still need to wait for udev to create
-                * the symlinks to those partitions.  If we are accessing
-                * the devices via a udev disk path, /dev/disk, then wait
-                * for *-part# to be created.  Otherwise just use the normal
-                * syntax for devices in /dev.
+                * Now we've labeled the disk and the partitions have been
+                * created.  We still need to wait for udev to create the
+                * symlinks to those partitions.
                 */
-               if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
-                       (void) snprintf(buf, sizeof (buf),
-                           "%s%s%s", path, "-part", FIRST_SLICE);
-               else
-                       (void) snprintf(buf, sizeof (buf),
-                           "%s%s%s", path, isdigit(path[strlen(path)-1]) ?
-                           "p" : "", FIRST_SLICE);
-
-               if ((ret = zpool_label_disk_wait(buf, 1000)) != 0) {
+               if ((ret = zpool_label_disk_wait(udevpath, 1000)) != 0) {
                        (void) fprintf(stderr,
-                           gettext( "cannot resolve path '%s'\n"), buf);
+                           gettext( "cannot resolve path '%s'\n"), udevpath);
                        return (-1);
                }
 
                /*
-                * Update the path to refer to FIRST_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);
+               verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, udevpath) == 0);
 
                /* Just in case this partition already existed. */
-               (void) zero_label(buf);
+               (void) zero_label(udevpath);
 
                return (0);
        }
@@ -1240,7 +1203,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;
@@ -1329,7 +1292,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;
@@ -1385,7 +1348,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++;
@@ -1451,7 +1414,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);
@@ -1501,7 +1464,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;
@@ -1513,7 +1476,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))