Allow GPT+EFI vdevs for root pools
[zfs.git] / lib / libzfs / libzfs_pool.c
index fa9f36c..e2a67c6 100644 (file)
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 #include <ctype.h>
@@ -235,6 +235,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
 
                case ZPOOL_PROP_ALTROOT:
                case ZPOOL_PROP_CACHEFILE:
+               case ZPOOL_PROP_COMMENT:
                        if (zhp->zpool_props != NULL ||
                            zpool_get_all_props(zhp) == 0) {
                                (void) strlcpy(buf,
@@ -272,6 +273,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
                case ZPOOL_PROP_SIZE:
                case ZPOOL_PROP_ALLOCATED:
                case ZPOOL_PROP_FREE:
+               case ZPOOL_PROP_EXPANDSZ:
                case ZPOOL_PROP_ASHIFT:
                        (void) zfs_nicenum(intval, buf, len);
                        break;
@@ -339,6 +341,7 @@ 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.
@@ -359,9 +362,10 @@ pool_uses_efi(nvlist_t *config)
        }
        return (B_FALSE);
 }
+#endif
 
-static boolean_t
-pool_is_bootable(zpool_handle_t *zhp)
+boolean_t
+zpool_is_bootable(zpool_handle_t *zhp)
 {
        char bootfs[ZPOOL_MAXNAMELEN];
 
@@ -385,7 +389,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
        zpool_prop_t prop;
        char *strval;
        uint64_t intval;
-       char *slash;
+       char *slash, *check;
        struct stat64 statbuf;
        zpool_handle_t *zhp;
        nvlist_t *nvroot;
@@ -566,6 +570,26 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
                        *slash = '/';
                        break;
 
+               case ZPOOL_PROP_COMMENT:
+                       for (check = strval; *check != '\0'; check++) {
+                               if (!isprint(*check)) {
+                                       zfs_error_aux(hdl,
+                                           dgettext(TEXT_DOMAIN,
+                                           "comment may only have printable "
+                                           "characters"));
+                                       (void) zfs_error(hdl, EZFS_BADPROP,
+                                           errbuf);
+                                       goto error;
+                               }
+                       }
+                       if (strlen(strval) > ZPROP_MAX_COMMENT) {
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "comment must not exceed %d characters"),
+                                   ZPROP_MAX_COMMENT);
+                               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                               goto error;
+                       }
+                       break;
                case ZPOOL_PROP_READONLY:
                        if (!flags.import) {
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -1106,7 +1130,8 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
                return (zfs_error(hdl, EZFS_BADVERSION, msg));
        }
 
-       if (pool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot,
+#if defined(__sun__) || defined(__sun)
+       if (zpool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot,
            ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) {
                uint64_t s;
 
@@ -1124,6 +1149,7 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
                        }
                }
        }
+#endif
 
        if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
            SPA_VERSION_L2CACHE &&
@@ -1722,10 +1748,11 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare,
                /*
                 * Search for the requested value. Special cases:
                 *
-                * - ZPOOL_CONFIG_PATH for whole disk entries.  These end in with a
-                *   partition suffix "1", "-part1", or "p1".  The suffix is  hidden
-                *   from the user, but included in the string, so this matches around
-                *   it.
+                * - ZPOOL_CONFIG_PATH for whole disk entries.  These end in
+                *   "-part1", or "p1".  The suffix is hidden from the user,
+                *   but included in the string, so this matches around it.
+                * - ZPOOL_CONFIG_PATH for short names zfs_strcmp_shortname()
+                *   is used to check all possible expanded paths.
                 * - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE).
                 *
                 * Otherwise, all other searches are simple string compares.
@@ -1735,15 +1762,9 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare,
 
                        (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
                            &wholedisk);
-                       if (wholedisk) {
-                               char buf[MAXPATHLEN];
-
-                               zfs_append_partition(srchval, buf, sizeof (buf));
-                               if (strcmp(val, buf) == 0)
-                                       return (nv);
+                       if (zfs_strcmp_pathname(srchval, val, wholedisk) == 0)
+                               return (nv);
 
-                               break;
-                       }
                } else if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0 && val) {
                        char *type, *idx, *end, *p;
                        uint64_t id, vdev_id;
@@ -1894,7 +1915,6 @@ nvlist_t *
 zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
     boolean_t *l2cache, boolean_t *log)
 {
-       char buf[MAXPATHLEN];
        char *end;
        nvlist_t *nvroot, *search, *ret;
        uint64_t guid;
@@ -1906,12 +1926,6 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
                verify(nvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid) == 0);
        } else if (zpool_vdev_is_interior(path)) {
                verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0);
-       } else if (path[0] != '/') {
-               if (zfs_resolve_shortname(path, buf, sizeof (buf)) < 0) {
-                       nvlist_free(search);
-                       return (NULL);
-               }
-               verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, buf) == 0);
        } else {
                verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, path) == 0);
        }
@@ -2049,6 +2063,7 @@ 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.
@@ -2056,6 +2071,7 @@ zpool_get_config_physpath(nvlist_t *config, char *physpath, size_t phypath_size)
        if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1 ||
            pool_uses_efi(vdev_root))
                return (EZFS_POOL_INVALARG);
+#endif
 
        (void) vdev_get_physpaths(child[0], physpath, phypath_size, &rsz,
            B_FALSE);
@@ -2097,6 +2113,9 @@ zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
         * It's possible that we might encounter an error if the device
         * does not have any unallocated space left. If so, we simply
         * ignore that error and continue on.
+        *
+        * Also, we don't call efi_rescan() - that would just return EBUSY.
+        * The module will do it for us in vdev_disk_open().
         */
        error = efi_use_whole_disk(fd);
        (void) close(fd);
@@ -2350,7 +2369,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
        uint_t children;
        nvlist_t *config_root;
        libzfs_handle_t *hdl = zhp->zpool_hdl;
-       boolean_t rootpool = pool_is_bootable(zhp);
+       boolean_t rootpool = zpool_is_bootable(zhp);
 
        if (replacing)
                (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
@@ -2359,6 +2378,7 @@ 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.
@@ -2368,6 +2388,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
                    "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,
@@ -2982,6 +3003,26 @@ zpool_reguid(zpool_handle_t *zhp)
 }
 
 /*
+ * Reopen the pool.
+ */
+int
+zpool_reopen(zpool_handle_t *zhp)
+{
+       zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+       char msg[1024];
+       libzfs_handle_t *hdl = zhp->zpool_hdl;
+
+       (void) snprintf(msg, sizeof (msg),
+           dgettext(TEXT_DOMAIN, "cannot reopen '%s'"),
+           zhp->zpool_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));
+}
+
+/*
  * Convert from a devid string to a path.
  */
 static char *
@@ -3110,6 +3151,7 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
        char *path, *devid, *type;
        uint64_t value;
        char buf[PATH_BUF_LEN];
+       char tmpbuf[PATH_BUF_LEN];
        vdev_stat_t *vs;
        uint_t vsc;
 
@@ -3182,13 +3224,12 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
                 * If it's a raidz device, we need to stick in the parity level.
                 */
                if (strcmp(path, VDEV_TYPE_RAIDZ) == 0) {
-                       char tmpbuf[PATH_BUF_LEN];
 
                        verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
                            &value) == 0);
-                       (void) snprintf(tmpbuf, sizeof (tmpbuf), "%s%llu", path,
+                       (void) snprintf(buf, sizeof (buf), "%s%llu", path,
                            (u_longlong_t)value);
-                       path = tmpbuf;
+                       path = buf;
                }
 
                /*
@@ -3200,9 +3241,9 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
 
                        verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
                            &id) == 0);
-                       (void) snprintf(buf, sizeof (buf), "%s-%llu", path,
-                           (u_longlong_t)id);
-                       path = buf;
+                       (void) snprintf(tmpbuf, sizeof (tmpbuf), "%s-%llu",
+                           path, (u_longlong_t)id);
+                       path = tmpbuf;
                }
        }
 
@@ -3656,7 +3697,7 @@ read_efi_label(nvlist_t *config, diskaddr_t *sb)
        if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
                return (err);
 
-       (void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT,
+       (void) snprintf(diskname, sizeof (diskname), "%s%s", DISK_ROOT,
            strrchr(path, '/'));
        if ((fd = open(diskname, O_RDWR|O_DIRECT)) >= 0) {
                struct dk_gpt *vtoc;
@@ -3774,12 +3815,14 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
        if (zhp) {
                nvlist_t *nvroot;
 
-               if (pool_is_bootable(zhp)) {
+#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);
@@ -3794,8 +3837,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
                start_block = NEW_START_BLOCK;
        }
 
-       (void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name,
-           BACKUP_SLICE);
+       (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
 
        if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
                /*
@@ -3847,7 +3889,7 @@ 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) {
+       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-
@@ -3865,9 +3907,11 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
        (void) close(fd);
        efi_free(vtoc);
 
-       /* Wait for the first expected slice to appear. */
-       (void) snprintf(path, sizeof (path), "%s/%s%s%s", DISK_ROOT, name,
-           isdigit(name[strlen(name)-1]) ? "p" : "", FIRST_SLICE);
+       /* Wait for the first expected partition to appear. */
+
+       (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
+       (void) zfs_append_partition(path, MAXPATHLEN);
+
        rval = zpool_label_disk_wait(path, 3000);
        if (rval) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to "