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,50 ****
--- 41,51 ----
  
  #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,280 ****
--- 272,282 ----
  
                  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,305 ****
--- 298,313 ----
                              == 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,411 ****
  
          elem = NULL;
          while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
                  const char *propname = nvpair_name(elem);
  
                  /*
                   * Make sure this property is valid and applies to this type.
                   */
!                 if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                              "invalid property '%s'"), propname);
                          (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                          goto error;
                  }
--- 406,457 ----
  
          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 == ZPROP_INVAL) {
                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                              "invalid property '%s'"), propname);
                          (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                          goto error;
                  }
*** 424,434 ****
                  /*
                   * Perform additional checking for specific properties.
                   */
                  switch (prop) {
                  case ZPOOL_PROP_VERSION:
!                         if (intval < version || intval > SPA_VERSION) {
                                  zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                      "property '%s' number %d is invalid."),
                                      propname, intval);
                                  (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
                                  goto error;
--- 470,481 ----
                  /*
                   * Perform additional checking for specific properties.
                   */
                  switch (prop) {
                  case ZPOOL_PROP_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,659 ****
--- 693,773 ----
  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,676 ****
--- 780,850 ----
          }
  
          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,1263 ****
          char timestr[128];
  
          if (!hdl->libzfs_printerr || config == NULL)
                  return;
  
!         if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_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);
  
--- 1426,1439 ----
          char timestr[128];
  
          if (!hdl->libzfs_printerr || config == NULL)
                  return;
  
!         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,1318 ****
--- 1485,1495 ----
          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,1440 ****
--- 1608,1641 ----
                  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,1546 ****
--- 1738,1763 ----
                              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;