Print this page
2619 asynchronous destruction of ZFS file systems
2747 SPA versioning with zfs feature flags
Reviewed by: Matt Ahrens <mahrens@delphix.com>
Reviewed by: George Wilson <gwilson@delphix.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: Dan Kruchinin <dan.kruchinin@gmail.com>
Approved by: Dan McDonald <danmcd@nexenta.com>
        
@@ -41,10 +41,11 @@
 
 #include "zfs_namecheck.h"
 #include "zfs_prop.h"
 #include "libzfs_impl.h"
 #include "zfs_comutil.h"
+#include "zfeature_common.h"
 
 static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
 
 #define DISK_ROOT       "/dev/dsk"
 #define RDISK_ROOT      "/dev/rdsk"
@@ -271,10 +272,11 @@
 
                 switch (prop) {
                 case ZPOOL_PROP_SIZE:
                 case ZPOOL_PROP_ALLOCATED:
                 case ZPOOL_PROP_FREE:
+                case ZPOOL_PROP_FREEING:
                 case ZPOOL_PROP_EXPANDSZ:
                         (void) zfs_nicenum(intval, buf, len);
                         break;
 
                 case ZPOOL_PROP_CAPACITY:
@@ -296,10 +298,16 @@
                             == 0);
 
                         (void) strlcpy(buf, zpool_state_to_name(intval,
                             vs->vs_aux), len);
                         break;
+                case ZPOOL_PROP_VERSION:
+                        if (intval >= SPA_VERSION_FEATURES) {
+                                (void) snprintf(buf, len, "-");
+                                break;
+                        }
+                        /* FALLTHROUGH */
                 default:
                         (void) snprintf(buf, len, "%llu", intval);
                 }
                 break;
 
@@ -398,14 +406,52 @@
 
         elem = NULL;
         while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
                 const char *propname = nvpair_name(elem);
 
+                prop = zpool_name_to_prop(propname);
+                if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
+                        int err;
+                        zfeature_info_t *feature;
+                        char *fname = strchr(propname, '@') + 1;
+
+                        err = zfeature_lookup_name(fname, &feature);
+                        if (err != 0) {
+                                ASSERT3U(err, ==, ENOENT);
+                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                    "invalid feature '%s'"), fname);
+                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                                goto error;
+                        }
+
+                        if (nvpair_type(elem) != DATA_TYPE_STRING) {
+                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                    "'%s' must be a string"), propname);
+                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                                goto error;
+                        }
+
+                        (void) nvpair_value_string(elem, &strval);
+                        if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0) {
+                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                    "property '%s' can only be set to "
+                                    "'enabled'"), propname);
+                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                                goto error;
+                        }
+
+                        if (nvlist_add_uint64(retprops, propname, 0) != 0) {
+                                (void) no_memory(hdl);
+                                goto error;
+                        }
+                        continue;
+                }
+
                 /*
                  * Make sure this property is valid and applies to this type.
                  */
-                if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+                if (prop == ZPROP_INVAL) {
                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                             "invalid property '%s'"), propname);
                         (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                         goto error;
                 }
@@ -424,11 +470,12 @@
                 /*
                  * Perform additional checking for specific properties.
                  */
                 switch (prop) {
                 case ZPOOL_PROP_VERSION:
-                        if (intval < version || intval > SPA_VERSION) {
+                        if (intval < version ||
+                            !SPA_VERSION_IS_SUPPORTED(intval)) {
                                 zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                     "property '%s' number %d is invalid."),
                                     propname, intval);
                                 (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
                                 goto error;
@@ -646,14 +693,81 @@
 zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
 {
         libzfs_handle_t *hdl = zhp->zpool_hdl;
         zprop_list_t *entry;
         char buf[ZFS_MAXPROPLEN];
+        nvlist_t *features = NULL;
+        zprop_list_t **last;
+        boolean_t firstexpand = (NULL == *plp);
 
         if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0)
                 return (-1);
 
+        last = plp;
+        while (*last != NULL)
+                last = &(*last)->pl_next;
+
+        if ((*plp)->pl_all)
+                features = zpool_get_features(zhp);
+
+        if ((*plp)->pl_all && firstexpand) {
+                for (int i = 0; i < SPA_FEATURES; i++) {
+                        zprop_list_t *entry = zfs_alloc(hdl,
+                            sizeof (zprop_list_t));
+                        entry->pl_prop = ZPROP_INVAL;
+                        entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
+                            spa_feature_table[i].fi_uname);
+                        entry->pl_width = strlen(entry->pl_user_prop);
+                        entry->pl_all = B_TRUE;
+
+                        *last = entry;
+                        last = &entry->pl_next;
+                }
+        }
+
+        /* add any unsupported features */
+        for (nvpair_t *nvp = nvlist_next_nvpair(features, NULL);
+            nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
+                char *propname;
+                boolean_t found;
+                zprop_list_t *entry;
+
+                if (zfeature_is_supported(nvpair_name(nvp)))
+                        continue;
+
+                propname = zfs_asprintf(hdl, "unsupported@%s",
+                    nvpair_name(nvp));
+
+                /*
+                 * Before adding the property to the list make sure that no
+                 * other pool already added the same property.
+                 */
+                found = B_FALSE;
+                entry = *plp;
+                while (entry != NULL) {
+                        if (entry->pl_user_prop != NULL &&
+                            strcmp(propname, entry->pl_user_prop) == 0) {
+                                found = B_TRUE;
+                                break;
+                        }
+                        entry = entry->pl_next;
+                }
+                if (found) {
+                        free(propname);
+                        continue;
+                }
+
+                entry = zfs_alloc(hdl, sizeof (zprop_list_t));
+                entry->pl_prop = ZPROP_INVAL;
+                entry->pl_user_prop = propname;
+                entry->pl_width = strlen(entry->pl_user_prop);
+                entry->pl_all = B_TRUE;
+
+                *last = entry;
+                last = &entry->pl_next;
+        }
+
         for (entry = *plp; entry != NULL; entry = entry->pl_next) {
 
                 if (entry->pl_fixed)
                         continue;
 
@@ -666,11 +780,71 @@
         }
 
         return (0);
 }
 
+/*
+ * Get the state for the given feature on the given ZFS pool.
+ */
+int
+zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf,
+    size_t len)
+{
+        uint64_t refcount;
+        boolean_t found = B_FALSE;
+        nvlist_t *features = zpool_get_features(zhp);
+        boolean_t supported;
+        const char *feature = strchr(propname, '@') + 1;
 
+        supported = zpool_prop_feature(propname);
+        ASSERT(supported || zfs_prop_unsupported(propname));
+
+        /*
+         * Convert from feature name to feature guid. This conversion is
+         * unecessary for unsupported@... properties because they already
+         * use guids.
+         */
+        if (supported) {
+                int ret;
+                zfeature_info_t *fi;
+
+                ret = zfeature_lookup_name(feature, &fi);
+                if (ret != 0) {
+                        (void) strlcpy(buf, "-", len);
+                        return (ENOTSUP);
+                }
+                feature = fi->fi_guid;
+        }
+
+        if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
+                found = B_TRUE;
+
+        if (supported) {
+                if (!found) {
+                        (void) strlcpy(buf, ZFS_FEATURE_DISABLED, len);
+                } else  {
+                        if (refcount == 0)
+                                (void) strlcpy(buf, ZFS_FEATURE_ENABLED, len);
+                        else
+                                (void) strlcpy(buf, ZFS_FEATURE_ACTIVE, len);
+                }
+        } else {
+                if (found) {
+                        if (refcount == 0) {
+                                (void) strcpy(buf, ZFS_UNSUPPORTED_INACTIVE);
+                        } else {
+                                (void) strcpy(buf, ZFS_UNSUPPORTED_READONLY);
+                        }
+                } else {
+                        (void) strlcpy(buf, "-", len);
+                        return (ENOTSUP);
+                }
+        }
+
+        return (0);
+}
+
 /*
  * Don't start the slice at the default block of 34; many storage
  * devices will use a stripe width of 128k, so start there instead.
  */
 #define NEW_START_BLOCK 256
@@ -1252,12 +1426,14 @@
         char timestr[128];
 
         if (!hdl->libzfs_printerr || config == NULL)
                 return;
 
-        if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0)
+        if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
+            nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0) {
                 return;
+        }
 
         if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
                 return;
         (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
 
@@ -1309,10 +1485,11 @@
         else
                 (void) printf(dgettext(TEXT_DOMAIN, "\t"));
 
         /* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */
         if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
+            nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0 ||
             nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
                 goto no_info;
 
         (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
         (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_DATA_ERRORS,
@@ -1431,10 +1608,34 @@
                 print_vdev_tree(hdl, vname, child[c], indent + 2);
                 free(vname);
         }
 }
 
+void
+zpool_print_unsup_feat(nvlist_t *config)
+{
+        nvlist_t *nvinfo, *unsup_feat;
+
+        verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) ==
+            0);
+        verify(nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT,
+            &unsup_feat) == 0);
+
+        for (nvpair_t *nvp = nvlist_next_nvpair(unsup_feat, NULL); nvp != NULL;
+            nvp = nvlist_next_nvpair(unsup_feat, nvp)) {
+                char *desc;
+
+                verify(nvpair_type(nvp) == DATA_TYPE_STRING);
+                verify(nvpair_value_string(nvp, &desc) == 0);
+
+                if (strlen(desc) > 0)
+                        (void) printf("\t%s (%s)\n", nvpair_name(nvp), desc);
+                else
+                        (void) printf("\t%s\n", nvpair_name(nvp));
+        }
+}
+
 /*
  * Import the given pool using the known configuration and a list of
  * properties to be set. The configuration should have come from
  * zpool_find_import(). The 'newname' parameters control whether the pool
  * is imported with a different name.
@@ -1537,10 +1738,26 @@
                             dgettext(TEXT_DOMAIN, "cannot import '%s' as '%s'"),
                             origname, thename);
 
                 switch (error) {
                 case ENOTSUP:
+                        if (nv != NULL && nvlist_lookup_nvlist(nv,
+                            ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
+                            nvlist_exists(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT)) {
+                                (void) printf(dgettext(TEXT_DOMAIN, "This "
+                                    "pool uses the following feature(s) not "
+                                    "supported by this system:\n"));
+                                zpool_print_unsup_feat(nv);
+                                if (nvlist_exists(nvinfo,
+                                    ZPOOL_CONFIG_CAN_RDONLY)) {
+                                        (void) printf(dgettext(TEXT_DOMAIN,
+                                            "All unsupported features are only "
+                                            "required for writing to the pool."
+                                            "\nThe pool can be imported using "
+                                            "'-o readonly=on'.\n"));
+                                }
+                        }
                         /*
                          * Unsupported version.
                          */
                         (void) zfs_error(hdl, EZFS_BADVERSION, desc);
                         break;