diff options
author | Colm <[email protected]> | 2021-04-12 17:08:56 +0100 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2021-04-14 13:23:08 -0700 |
commit | 1f3de97374b63ad513176fa9b37b393a02e6ca8c (patch) | |
tree | e00ca8f2b75018f181d9f3ecbf2709a510ceda9d /cmd/zpool/zpool_main.c | |
parent | 7de1797cee8b887871f3dede83c76edbe3e961f4 (diff) |
Improvements to the 'compatibility' property
Several improvements to the operation of the 'compatibility' property:
1) Improved handling of unrecognized features:
Change the way unrecognized features in compatibility files are handled.
* invalid features in files under /usr/share/zfs/compatibility.d
only get a warning (as these may refer to future features not yet in
the library),
* invalid features in files under /etc/zfs/compatibility.d
get an error (as these are presumed to refer to the current system).
2) Improved error reporting from zpool_load_compat.
Note: slight ABI change to zpool_load_compat for better error reporting.
3) compatibility=legacy inhibits all 'zpool upgrade' operations.
4) Detect when features are enabled outside current compatibility set
* zpool set compatibility=foo <-- print a warning
* zpool set feature@xxx=enabled <-- error
* zpool status <-- indicate this state
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Colm Buckley <[email protected]>
Closes #11861
Diffstat (limited to 'cmd/zpool/zpool_main.c')
-rw-r--r-- | cmd/zpool/zpool_main.c | 163 |
1 files changed, 135 insertions, 28 deletions
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 81bbf1375..34742eab5 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -786,7 +786,7 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, if (poolprop) { const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION); - const char *fname = + const char *cname = zpool_prop_to_name(ZPOOL_PROP_COMPATIBILITY); if ((prop = zpool_name_to_prop(propname)) == ZPOOL_PROP_INVAL && @@ -811,16 +811,19 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, } /* - * compatibility property and version should not be specified - * at the same time. + * if version is specified, only "legacy" compatibility + * may be requested */ if ((prop == ZPOOL_PROP_COMPATIBILITY && + strcmp(propval, ZPOOL_COMPAT_LEGACY) != 0 && nvlist_exists(proplist, vname)) || (prop == ZPOOL_PROP_VERSION && - nvlist_exists(proplist, fname))) { - (void) fprintf(stderr, gettext("'compatibility' and " - "'version' properties cannot be specified " - "together\n")); + nvlist_exists(proplist, cname) && + strcmp(fnvlist_lookup_string(proplist, cname), + ZPOOL_COMPAT_LEGACY) != 0)) { + (void) fprintf(stderr, gettext("when 'version' is " + "specified, the 'compatibility' feature may only " + "be set to '" ZPOOL_COMPAT_LEGACY "'\n")); return (2); } @@ -1674,6 +1677,7 @@ zpool_do_create(int argc, char **argv) * - enable_pool_features (ie: no '-d' or '-o version') * - it's supported by the kernel module * - it's in the requested feature set + * - warn if it's enabled but not in compat */ for (spa_feature_t i = 0; i < SPA_FEATURES; i++) { char propname[MAXPATHLEN]; @@ -1687,6 +1691,14 @@ zpool_do_create(int argc, char **argv) if (strcmp(propval, ZFS_FEATURE_DISABLED) == 0) (void) nvlist_remove_all(props, propname); + if (strcmp(propval, + ZFS_FEATURE_ENABLED) == 0 && + !requested_features[i]) + (void) fprintf(stderr, gettext( + "Warning: feature \"%s\" enabled " + "but is not in specified " + "'compatibility' feature set.\n"), + feat->fi_uname); } else if ( enable_pool_features && feat->fi_zfs_mod_supported && @@ -2717,8 +2729,10 @@ show_import(nvlist_t *config, boolean_t report_error) case ZPOOL_STATUS_FEAT_DISABLED: printf_color(ANSI_BOLD, gettext("status: ")); - printf_color(ANSI_YELLOW, gettext("Some supported and " - "requested features are not enabled on the pool.\n")); + printf_color(ANSI_YELLOW, gettext("Some supported " + "features are not enabled on the pool.\n\t" + "(Note that they may be intentionally disabled " + "if the\n\t'compatibility' property is set.)\n")); break; case ZPOOL_STATUS_COMPATIBILITY_ERR: @@ -2728,6 +2742,13 @@ show_import(nvlist_t *config, boolean_t report_error) "property.\n")); break; + case ZPOOL_STATUS_INCOMPATIBLE_FEAT: + printf_color(ANSI_BOLD, gettext("status: ")); + printf_color(ANSI_YELLOW, gettext("One or more features " + "are enabled on the pool despite not being\n" + "requested by the 'compatibility' property.\n")); + break; + case ZPOOL_STATUS_UNSUP_FEAT_READ: printf_color(ANSI_BOLD, gettext("status: ")); printf_color(ANSI_YELLOW, gettext("The pool uses the following " @@ -8055,7 +8076,8 @@ status_callback(zpool_handle_t *zhp, void *data) (reason == ZPOOL_STATUS_OK || reason == ZPOOL_STATUS_VERSION_OLDER || reason == ZPOOL_STATUS_FEAT_DISABLED || - reason == ZPOOL_STATUS_COMPATIBILITY_ERR)) { + reason == ZPOOL_STATUS_COMPATIBILITY_ERR || + reason == ZPOOL_STATUS_INCOMPATIBLE_FEAT)) { if (!cbp->cb_allpools) { (void) printf(gettext("pool '%s' is healthy\n"), zpool_get_name(zhp)); @@ -8254,6 +8276,18 @@ status_callback(zpool_handle_t *zhp, void *data) ZPOOL_DATA_COMPAT_D ".\n")); break; + case ZPOOL_STATUS_INCOMPATIBLE_FEAT: + printf_color(ANSI_BOLD, gettext("status: ")); + printf_color(ANSI_YELLOW, gettext("One or more features " + "are enabled on the pool despite not being\n\t" + "requested by the 'compatibility' property.\n")); + printf_color(ANSI_BOLD, gettext("action: ")); + printf_color(ANSI_YELLOW, gettext("Consider setting " + "'compatibility' to an appropriate value, or\n\t" + "adding needed features to the relevant file in\n\t" + ZPOOL_SYSCONF_COMPAT_D " or " ZPOOL_DATA_COMPAT_D ".\n")); + break; + case ZPOOL_STATUS_UNSUP_FEAT_READ: printf_color(ANSI_BOLD, gettext("status: ")); printf_color(ANSI_YELLOW, gettext("The pool cannot be accessed " @@ -8713,6 +8747,11 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version) verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &oldversion) == 0); + char compat[ZFS_MAXPROPLEN]; + if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY, compat, + ZFS_MAXPROPLEN, NULL, B_FALSE) != 0) + compat[0] = '\0'; + assert(SPA_VERSION_IS_SUPPORTED(oldversion)); assert(oldversion < version); @@ -8727,6 +8766,13 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version) return (1); } + if (strcmp(compat, ZPOOL_COMPAT_LEGACY) == 0) { + (void) fprintf(stderr, gettext("Upgrade not performed because " + "'compatibility' property set to '" + ZPOOL_COMPAT_LEGACY "'.\n")); + return (1); + } + ret = zpool_upgrade(zhp, version); if (ret != 0) return (ret); @@ -8868,7 +8914,10 @@ upgrade_list_older_cb(zpool_handle_t *zhp, void *arg) "be upgraded to use feature flags. After " "being upgraded, these pools\nwill no " "longer be accessible by software that does not " - "support feature\nflags.\n\n")); + "support feature\nflags.\n\n" + "Note that setting a pool's 'compatibility' " + "feature to '" ZPOOL_COMPAT_LEGACY "' will\n" + "inhibit upgrades.\n\n")); (void) printf(gettext("VER POOL\n")); (void) printf(gettext("--- ------------\n")); cbp->cb_first = B_FALSE; @@ -8914,7 +8963,11 @@ upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg) "software\nthat does not support " "the feature. See " "zpool-features(5) for " - "details.\n\n")); + "details.\n\n" + "Note that the pool " + "'compatibility' feature can be " + "used to inhibit\nfeature " + "upgrades.\n\n")); (void) printf(gettext("POOL " "FEATURE\n")); (void) printf(gettext("------" @@ -9970,6 +10023,63 @@ set_callback(zpool_handle_t *zhp, void *data) int error; set_cbdata_t *cb = (set_cbdata_t *)data; + /* Check if we have out-of-bounds features */ + if (strcmp(cb->cb_propname, ZPOOL_CONFIG_COMPATIBILITY) == 0) { + boolean_t features[SPA_FEATURES]; + if (zpool_do_load_compat(cb->cb_value, features) != + ZPOOL_COMPATIBILITY_OK) + return (-1); + + nvlist_t *enabled = zpool_get_features(zhp); + spa_feature_t i; + for (i = 0; i < SPA_FEATURES; i++) { + const char *fguid = spa_feature_table[i].fi_guid; + if (nvlist_exists(enabled, fguid) && !features[i]) + break; + } + if (i < SPA_FEATURES) + (void) fprintf(stderr, gettext("Warning: one or " + "more features already enabled on pool '%s'\n" + "are not present in this compatibility set.\n"), + zpool_get_name(zhp)); + } + + /* if we're setting a feature, check it's in compatibility set */ + if (zpool_prop_feature(cb->cb_propname) && + strcmp(cb->cb_value, ZFS_FEATURE_ENABLED) == 0) { + char *fname = strchr(cb->cb_propname, '@') + 1; + spa_feature_t f; + + if (zfeature_lookup_name(fname, &f) == 0) { + char compat[ZFS_MAXPROPLEN]; + if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY, + compat, ZFS_MAXPROPLEN, NULL, B_FALSE) != 0) + compat[0] = '\0'; + + boolean_t features[SPA_FEATURES]; + if (zpool_do_load_compat(compat, features) != + ZPOOL_COMPATIBILITY_OK) { + (void) fprintf(stderr, gettext("Error: " + "cannot enable feature '%s' on pool '%s'\n" + "because the pool's 'compatibility' " + "property cannot be parsed.\n"), + fname, zpool_get_name(zhp)); + return (-1); + } + + if (!features[f]) { + (void) fprintf(stderr, gettext("Error: " + "cannot enable feature '%s' on pool '%s'\n" + "as it is not specified in this pool's " + "current compatibility set.\n" + "Consider setting 'compatibility' to a " + "less restrictive set, or to 'off'.\n"), + fname, zpool_get_name(zhp)); + return (-1); + } + } + } + error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value); if (!error) @@ -10492,28 +10602,25 @@ zpool_do_version(int argc, char **argv) static zpool_compat_status_t zpool_do_load_compat(const char *compat, boolean_t *list) { - char badword[ZFS_MAXPROPLEN]; - char badfile[MAXPATHLEN]; + char report[1024]; + zpool_compat_status_t ret; - switch (ret = zpool_load_compat(compat, list, badword, badfile)) { + ret = zpool_load_compat(compat, list, report, 1024); + switch (ret) { + case ZPOOL_COMPATIBILITY_OK: break; - case ZPOOL_COMPATIBILITY_READERR: - (void) fprintf(stderr, gettext("error reading compatibility " - "file '%s'\n"), badfile); - break; + + case ZPOOL_COMPATIBILITY_NOFILES: case ZPOOL_COMPATIBILITY_BADFILE: - (void) fprintf(stderr, gettext("compatibility file '%s' " - "too large or not newline-terminated\n"), badfile); - break; - case ZPOOL_COMPATIBILITY_BADWORD: - (void) fprintf(stderr, gettext("unknown feature '%s' in " - "compatibility file '%s'\n"), badword, badfile); + case ZPOOL_COMPATIBILITY_BADTOKEN: + (void) fprintf(stderr, "Error: %s\n", report); break; - case ZPOOL_COMPATIBILITY_NOFILES: - (void) fprintf(stderr, gettext("no compatibility files " - "specified\n")); + + case ZPOOL_COMPATIBILITY_WARNTOKEN: + (void) fprintf(stderr, "Warning: %s\n", report); + ret = ZPOOL_COMPATIBILITY_OK; break; } return (ret); |