Illumos #2619 and #2747
[zfs.git] / cmd / zpool / zpool_main.c
index cb0535a..d0c0a92 100644 (file)
@@ -54,6 +54,7 @@
 
 #include "zpool_util.h"
 #include "zfs_comutil.h"
+#include "zfeature_common.h"
 
 #include "statcommon.h"
 
@@ -208,7 +209,7 @@ get_usage(zpool_help_t idx) {
        case HELP_CLEAR:
                return (gettext("\tclear [-nF] <pool> [device]\n"));
        case HELP_CREATE:
-               return (gettext("\tcreate [-fn] [-o property=value] ... \n"
+               return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
                    "\t    [-O file-system-property=value] ... \n"
                    "\t    [-m mountpoint] [-R root] <pool> <vdev> ...\n"));
        case HELP_DESTROY:
@@ -341,6 +342,12 @@ usage(boolean_t requested)
                /* Iterate over all properties */
                (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE,
                    ZFS_TYPE_POOL);
+
+               (void) fprintf(fp, "\t%-15s   ", "feature@...");
+               (void) fprintf(fp, "YES   disabled | enabled | active\n");
+
+               (void) fprintf(fp, gettext("\nThe feature@ properties must be "
+                   "appended with a feature name.\nSee zpool-features(5).\n"));
        }
 
        /*
@@ -407,12 +414,16 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
        proplist = *props;
 
        if (poolprop) {
-               if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
+               if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL &&
+                   !zpool_prop_feature(propname)) {
                        (void) fprintf(stderr, gettext("property '%s' is "
                            "not a valid pool property\n"), propname);
                        return (2);
                }
-               normnm = zpool_prop_to_name(prop);
+               if (zpool_prop_feature(propname))
+                       normnm = propname;
+               else
+                       normnm = zpool_prop_to_name(prop);
        } else {
                if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
                        normnm = zfs_prop_to_name(fprop);
@@ -601,7 +612,7 @@ zpool_do_remove(int argc, char **argv)
 }
 
 /*
- * zpool create [-fn] [-o property=value] ...
+ * zpool create [-fnd] [-o property=value] ...
  *             [-O file-system-property=value] ...
  *             [-R root] [-m mountpoint] <pool> <dev> ...
  *
@@ -610,8 +621,10 @@ zpool_do_remove(int argc, char **argv)
  *             were to be created.
  *      -R     Create a pool under an alternate root
  *      -m     Set default mountpoint for the root dataset.  By default it's
- *             '/<pool>'
+ *             '/<pool>'
  *     -o      Set property=value.
+ *     -d      Don't automatically enable all supported pool features
+ *             (individual features can be enabled with -o).
  *     -O      Set fsproperty=value in the pool's root file system
  *
  * Creates the named pool according to the given vdev specification.  The
@@ -624,6 +637,7 @@ zpool_do_create(int argc, char **argv)
 {
        boolean_t force = B_FALSE;
        boolean_t dryrun = B_FALSE;
+       boolean_t enable_all_pool_feat = B_TRUE;
        int c;
        nvlist_t *nvroot = NULL;
        char *poolname;
@@ -635,7 +649,7 @@ zpool_do_create(int argc, char **argv)
        char *propval;
 
        /* check options */
-       while ((c = getopt(argc, argv, ":fnR:m:o:O:")) != -1) {
+       while ((c = getopt(argc, argv, ":fndR:m:o:O:")) != -1) {
                switch (c) {
                case 'f':
                        force = B_TRUE;
@@ -643,6 +657,9 @@ zpool_do_create(int argc, char **argv)
                case 'n':
                        dryrun = B_TRUE;
                        break;
+               case 'd':
+                       enable_all_pool_feat = B_FALSE;
+                       break;
                case 'R':
                        altroot = optarg;
                        if (add_prop_list(zpool_prop_to_name(
@@ -670,6 +687,21 @@ zpool_do_create(int argc, char **argv)
 
                        if (add_prop_list(optarg, propval, &props, B_TRUE))
                                goto errout;
+
+                       /*
+                        * If the user is creating a pool that doesn't support
+                        * feature flags, don't enable any features.
+                        */
+                       if (zpool_name_to_prop(optarg) == ZPOOL_PROP_VERSION) {
+                               char *end;
+                               u_longlong_t ver;
+
+                               ver = strtoull(propval, &end, 10);
+                               if (*end == '\0' &&
+                                   ver < SPA_VERSION_FEATURES) {
+                                       enable_all_pool_feat = B_FALSE;
+                               }
+                       }
                        break;
                case 'O':
                        if ((propval = strchr(optarg, '=')) == NULL) {
@@ -735,7 +767,6 @@ zpool_do_create(int argc, char **argv)
                goto errout;
        }
 
-
        if (altroot != NULL && altroot[0] != '/') {
                (void) fprintf(stderr, gettext("invalid alternate root '%s': "
                    "must be an absolute path\n"), altroot);
@@ -817,6 +848,27 @@ zpool_do_create(int argc, char **argv)
                /*
                 * Hand off to libzfs.
                 */
+               if (enable_all_pool_feat) {
+                       int i;
+                       for (i = 0; i < SPA_FEATURES; i++) {
+                               char propname[MAXPATHLEN];
+                               zfeature_info_t *feat = &spa_feature_table[i];
+
+                               (void) snprintf(propname, sizeof (propname),
+                                   "feature@%s", feat->fi_uname);
+
+                               /*
+                                * Skip feature if user specified it manually
+                                * on the command line.
+                                */
+                               if (nvlist_exists(props, propname))
+                                       continue;
+
+                               if (add_prop_list(propname, ZFS_FEATURE_ENABLED,
+                                   &props, B_TRUE) != 0)
+                                       goto errout;
+                       }
+               }
                if (zpool_create(g_zfs, poolname,
                    nvroot, props, fsprops) == 0) {
                        zfs_handle_t *pool = zfs_open(g_zfs, poolname,
@@ -1148,6 +1200,10 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
                        (void) printf(gettext("newer version"));
                        break;
 
+               case VDEV_AUX_UNSUP_FEAT:
+                       (void) printf(gettext("unsupported feature(s)"));
+                       break;
+
                case VDEV_AUX_SPARED:
                        verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
                            &cb.cb_guid) == 0);
@@ -1265,6 +1321,10 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth)
                        (void) printf(gettext("newer version"));
                        break;
 
+               case VDEV_AUX_UNSUP_FEAT:
+                       (void) printf(gettext("unsupported feature(s)"));
+                       break;
+
                case VDEV_AUX_ERR_EXCEEDED:
                        (void) printf(gettext("too many errors"));
                        break;
@@ -1431,6 +1491,20 @@ show_import(nvlist_t *config)
                    "incompatible version.\n"));
                break;
 
+       case ZPOOL_STATUS_UNSUP_FEAT_READ:
+               (void) printf(gettext("status: The pool uses the following "
+                   "feature(s) not supported on this sytem:\n"));
+               zpool_print_unsup_feat(config);
+               break;
+
+       case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+               (void) printf(gettext("status: The pool can only be accessed "
+                   "in read-only mode on this system. It\n\tcannot be "
+                   "accessed in read-write mode because it uses the "
+                   "following\n\tfeature(s) not supported on this system:\n"));
+               zpool_print_unsup_feat(config);
+               break;
+
        case ZPOOL_STATUS_HOSTID_MISMATCH:
                (void) printf(gettext(" status: The pool was last accessed by "
                    "another system.\n"));
@@ -1488,6 +1562,20 @@ show_import(nvlist_t *config)
                            "newer\n\tsoftware, or recreate the pool from "
                            "backup.\n"));
                        break;
+               case ZPOOL_STATUS_UNSUP_FEAT_READ:
+                       (void) printf(gettext("action: The pool cannot be "
+                           "imported. Access the pool on a system that "
+                           "supports\n\tthe required feature(s), or recreate "
+                           "the pool from backup.\n"));
+                       break;
+               case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+                       (void) printf(gettext("action: The pool cannot be "
+                           "imported in read-write mode. Import the pool "
+                           "with\n"
+                           "\t\"-o readonly=on\", access the pool on a system "
+                           "that supports the\n\trequired feature(s), or "
+                           "recreate the pool from backup.\n"));
+                       break;
                case ZPOOL_STATUS_MISSING_DEV_R:
                case ZPOOL_STATUS_MISSING_DEV_NR:
                case ZPOOL_STATUS_BAD_GUID_SUM:
@@ -1563,9 +1651,9 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
            ZPOOL_CONFIG_POOL_STATE, &state) == 0);
        verify(nvlist_lookup_uint64(config,
            ZPOOL_CONFIG_VERSION, &version) == 0);
-       if (version > SPA_VERSION) {
+       if (!SPA_VERSION_IS_SUPPORTED(version)) {
                (void) fprintf(stderr, gettext("cannot import '%s': pool "
-                   "is formatted using a newer ZFS version\n"), name);
+                   "is formatted using an unsupported ZFS version\n"), name);
                return (1);
        } else if (state != POOL_STATE_EXPORTED &&
            !(flags & ZFS_IMPORT_ANY_HOST)) {
@@ -2556,15 +2644,13 @@ static void
 print_header(list_cbdata_t *cb)
 {
        zprop_list_t *pl = cb->cb_proplist;
+       char headerbuf[ZPOOL_MAXPROPLEN];
        const char *header;
        boolean_t first = B_TRUE;
        boolean_t right_justify;
        size_t width = 0;
 
        for (; pl != NULL; pl = pl->pl_next) {
-               if (pl->pl_prop == ZPROP_INVAL)
-                       continue;
-
                width = pl->pl_width;
                if (first && cb->cb_verbose) {
                        /*
@@ -2579,8 +2665,18 @@ print_header(list_cbdata_t *cb)
                else
                        first = B_FALSE;
 
-               header = zpool_prop_column_name(pl->pl_prop);
-               right_justify = zpool_prop_align_right(pl->pl_prop);
+               right_justify = B_FALSE;
+               if (pl->pl_prop != ZPROP_INVAL) {
+                       header = zpool_prop_column_name(pl->pl_prop);
+                       right_justify = zpool_prop_align_right(pl->pl_prop);
+               } else {
+                       int i;
+
+                       for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
+                               headerbuf[i] = toupper(pl->pl_user_prop[i]);
+                       headerbuf[i] = '\0';
+                       header = headerbuf;
+               }
 
                if (pl->pl_next == NULL && !right_justify)
                        (void) printf("%s", header);
@@ -2639,6 +2735,11 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
                                propstr = property;
 
                        right_justify = zpool_prop_align_right(pl->pl_prop);
+               } else if ((zpool_prop_feature(pl->pl_user_prop) ||
+                   zpool_prop_unsupported(pl->pl_user_prop)) &&
+                   zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
+                   sizeof (property)) == 0) {
+                       propstr = property;
                } else {
                        propstr = "-";
                }
@@ -3958,6 +4059,31 @@ status_callback(zpool_handle_t *zhp, void *data)
                    "backup.\n"));
                break;
 
+       case ZPOOL_STATUS_UNSUP_FEAT_READ:
+               (void) printf(gettext("status: The pool cannot be accessed on "
+                   "this system because it uses the\n\tfollowing feature(s) "
+                   "not supported on this system:\n"));
+               zpool_print_unsup_feat(config);
+               (void) printf("\n");
+               (void) printf(gettext("action: Access the pool from a system "
+                   "that supports the required feature(s),\n\tor restore the "
+                   "pool from backup.\n"));
+               break;
+
+       case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
+               (void) printf(gettext("status: The pool can only be accessed "
+                   "in read-only mode on this system. It\n\tcannot be "
+                   "accessed in read-write mode because it uses the "
+                   "following\n\tfeature(s) not supported on this system:\n"));
+               zpool_print_unsup_feat(config);
+               (void) printf("\n");
+               (void) printf(gettext("action: The pool cannot be accessed in "
+                   "read-write mode. Import the pool with\n"
+                   "\t\"-o readonly=on\", access the pool from a system that "
+                   "supports the\n\trequired feature(s), or restore the "
+                   "pool from backup.\n"));
+               break;
+
        case ZPOOL_STATUS_FAULTED_DEV_R:
                (void) printf(gettext("status: One or more devices are "
                    "faulted in response to persistent errors.\n\tSufficient "
@@ -4182,7 +4308,8 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
        verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
            &version) == 0);
 
-       if (!cbp->cb_newer && version < SPA_VERSION) {
+       if (!cbp->cb_newer && SPA_VERSION_IS_SUPPORTED(version) &&
+           version != SPA_VERSION) {
                if (!cbp->cb_all) {
                        if (cbp->cb_first) {
                                (void) printf(gettext("The following pools are "
@@ -4205,13 +4332,14 @@ upgrade_cb(zpool_handle_t *zhp, void *arg)
                                    "'%s'\n\n"), zpool_get_name(zhp));
                        }
                }
-       } else if (cbp->cb_newer && version > SPA_VERSION) {
+       } else if (cbp->cb_newer && !SPA_VERSION_IS_SUPPORTED(version)) {
                assert(!cbp->cb_all);
 
                if (cbp->cb_first) {
                        (void) printf(gettext("The following pools are "
-                           "formatted using a newer software version and\n"
-                           "cannot be accessed on the current system.\n\n"));
+                           "formatted using an unsupported software version "
+                           "and\ncannot be accessed on the current "
+                           "system.\n\n"));
                        (void) printf(gettext("VER  POOL\n"));
                        (void) printf(gettext("---  ------------\n"));
                        cbp->cb_first = B_FALSE;
@@ -4295,8 +4423,8 @@ zpool_do_upgrade(int argc, char **argv)
                        break;
                case 'V':
                        cb.cb_version = strtoll(optarg, &end, 10);
-                       if (*end != '\0' || cb.cb_version > SPA_VERSION ||
-                           cb.cb_version < SPA_VERSION_1) {
+                       if (*end != '\0' ||
+                           !SPA_VERSION_IS_SUPPORTED(cb.cb_version)) {
                                (void) fprintf(stderr,
                                    gettext("invalid version '%s'\n"), optarg);
                                usage(B_FALSE);
@@ -4341,8 +4469,8 @@ zpool_do_upgrade(int argc, char **argv)
                }
        }
 
-       (void) printf(gettext("This system is currently running "
-           "ZFS pool version %llu.\n\n"), SPA_VERSION);
+       (void) printf(gettext("This system supports ZFS pool feature "
+           "flags.\n\n"));
        cb.cb_first = B_TRUE;
        if (showversions) {
                (void) printf(gettext("The following versions are "
@@ -4923,13 +5051,26 @@ get_callback(zpool_handle_t *zhp, void *data)
                    pl == cbp->cb_proplist)
                        continue;
 
-               if (zpool_get_prop(zhp, pl->pl_prop,
-                   value, sizeof (value), &srctype) != 0)
-                       continue;
+               if (pl->pl_prop == ZPROP_INVAL &&
+                   (zpool_prop_feature(pl->pl_user_prop) ||
+                   zpool_prop_unsupported(pl->pl_user_prop))) {
+                       srctype = ZPROP_SRC_LOCAL;
+
+                       if (zpool_prop_get_feature(zhp, pl->pl_user_prop,
+                           value, sizeof (value)) == 0) {
+                               zprop_print_one_property(zpool_get_name(zhp),
+                                   cbp, pl->pl_user_prop, value, srctype,
+                                   NULL, NULL);
+                       }
+               } else {
+                       if (zpool_get_prop(zhp, pl->pl_prop, value,
+                           sizeof (value), &srctype) != 0)
+                               continue;
 
-               zprop_print_one_property(zpool_get_name(zhp), cbp,
-                   zpool_prop_to_name(pl->pl_prop), value, srctype, NULL,
-                   NULL);
+                       zprop_print_one_property(zpool_get_name(zhp), cbp,
+                           zpool_prop_to_name(pl->pl_prop), value, srctype,
+                           NULL, NULL);
+               }
        }
        return (0);
 }
@@ -4941,8 +5082,11 @@ zpool_do_get(int argc, char **argv)
        zprop_list_t fake_name = { 0 };
        int ret;
 
-       if (argc < 3)
+       if (argc < 2) {
+               (void) fprintf(stderr, gettext("missing property "
+                   "argument\n"));
                usage(B_FALSE);
+       }
 
        cb.cb_first = B_TRUE;
        cb.cb_sources = ZPROP_SRC_ALL;
@@ -4952,7 +5096,7 @@ zpool_do_get(int argc, char **argv)
        cb.cb_columns[3] = GET_COL_SOURCE;
        cb.cb_type = ZFS_TYPE_POOL;
 
-       if (zprop_get_list(g_zfs, argv[1],  &cb.cb_proplist,
+       if (zprop_get_list(g_zfs, argv[1], &cb.cb_proplist,
            ZFS_TYPE_POOL) != 0)
                usage(B_FALSE);