Skip to content

Commit 297d1dd

Browse files
colmbuckleymcmilk
authored andcommitted
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 openzfs#11861
1 parent 97708eb commit 297d1dd

File tree

7 files changed

+2935
-2711
lines changed

7 files changed

+2935
-2711
lines changed

cmd/zpool/zpool_main.c

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
786786

787787
if (poolprop) {
788788
const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION);
789-
const char *fname =
789+
const char *cname =
790790
zpool_prop_to_name(ZPOOL_PROP_COMPATIBILITY);
791791

792792
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,
811811
}
812812

813813
/*
814-
* compatibility property and version should not be specified
815-
* at the same time.
814+
* if version is specified, only "legacy" compatibility
815+
* may be requested
816816
*/
817817
if ((prop == ZPOOL_PROP_COMPATIBILITY &&
818+
strcmp(propval, ZPOOL_COMPAT_LEGACY) != 0 &&
818819
nvlist_exists(proplist, vname)) ||
819820
(prop == ZPOOL_PROP_VERSION &&
820-
nvlist_exists(proplist, fname))) {
821-
(void) fprintf(stderr, gettext("'compatibility' and "
822-
"'version' properties cannot be specified "
823-
"together\n"));
821+
nvlist_exists(proplist, cname) &&
822+
strcmp(fnvlist_lookup_string(proplist, cname),
823+
ZPOOL_COMPAT_LEGACY) != 0)) {
824+
(void) fprintf(stderr, gettext("when 'version' is "
825+
"specified, the 'compatibility' feature may only "
826+
"be set to '" ZPOOL_COMPAT_LEGACY "'\n"));
824827
return (2);
825828
}
826829

@@ -1674,6 +1677,7 @@ zpool_do_create(int argc, char **argv)
16741677
* - enable_pool_features (ie: no '-d' or '-o version')
16751678
* - it's supported by the kernel module
16761679
* - it's in the requested feature set
1680+
* - warn if it's enabled but not in compat
16771681
*/
16781682
for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
16791683
char propname[MAXPATHLEN];
@@ -1687,6 +1691,14 @@ zpool_do_create(int argc, char **argv)
16871691
if (strcmp(propval, ZFS_FEATURE_DISABLED) == 0)
16881692
(void) nvlist_remove_all(props,
16891693
propname);
1694+
if (strcmp(propval,
1695+
ZFS_FEATURE_ENABLED) == 0 &&
1696+
!requested_features[i])
1697+
(void) fprintf(stderr, gettext(
1698+
"Warning: feature \"%s\" enabled "
1699+
"but is not in specified "
1700+
"'compatibility' feature set.\n"),
1701+
feat->fi_uname);
16901702
} else if (
16911703
enable_pool_features &&
16921704
feat->fi_zfs_mod_supported &&
@@ -2717,8 +2729,10 @@ show_import(nvlist_t *config, boolean_t report_error)
27172729

27182730
case ZPOOL_STATUS_FEAT_DISABLED:
27192731
printf_color(ANSI_BOLD, gettext("status: "));
2720-
printf_color(ANSI_YELLOW, gettext("Some supported and "
2721-
"requested features are not enabled on the pool.\n"));
2732+
printf_color(ANSI_YELLOW, gettext("Some supported "
2733+
"features are not enabled on the pool.\n\t"
2734+
"(Note that they may be intentionally disabled "
2735+
"if the\n\t'compatibility' property is set.)\n"));
27222736
break;
27232737

27242738
case ZPOOL_STATUS_COMPATIBILITY_ERR:
@@ -2728,6 +2742,13 @@ show_import(nvlist_t *config, boolean_t report_error)
27282742
"property.\n"));
27292743
break;
27302744

2745+
case ZPOOL_STATUS_INCOMPATIBLE_FEAT:
2746+
printf_color(ANSI_BOLD, gettext("status: "));
2747+
printf_color(ANSI_YELLOW, gettext("One or more features "
2748+
"are enabled on the pool despite not being\n"
2749+
"requested by the 'compatibility' property.\n"));
2750+
break;
2751+
27312752
case ZPOOL_STATUS_UNSUP_FEAT_READ:
27322753
printf_color(ANSI_BOLD, gettext("status: "));
27332754
printf_color(ANSI_YELLOW, gettext("The pool uses the following "
@@ -8055,7 +8076,8 @@ status_callback(zpool_handle_t *zhp, void *data)
80558076
(reason == ZPOOL_STATUS_OK ||
80568077
reason == ZPOOL_STATUS_VERSION_OLDER ||
80578078
reason == ZPOOL_STATUS_FEAT_DISABLED ||
8058-
reason == ZPOOL_STATUS_COMPATIBILITY_ERR)) {
8079+
reason == ZPOOL_STATUS_COMPATIBILITY_ERR ||
8080+
reason == ZPOOL_STATUS_INCOMPATIBLE_FEAT)) {
80598081
if (!cbp->cb_allpools) {
80608082
(void) printf(gettext("pool '%s' is healthy\n"),
80618083
zpool_get_name(zhp));
@@ -8254,6 +8276,18 @@ status_callback(zpool_handle_t *zhp, void *data)
82548276
ZPOOL_DATA_COMPAT_D ".\n"));
82558277
break;
82568278

8279+
case ZPOOL_STATUS_INCOMPATIBLE_FEAT:
8280+
printf_color(ANSI_BOLD, gettext("status: "));
8281+
printf_color(ANSI_YELLOW, gettext("One or more features "
8282+
"are enabled on the pool despite not being\n\t"
8283+
"requested by the 'compatibility' property.\n"));
8284+
printf_color(ANSI_BOLD, gettext("action: "));
8285+
printf_color(ANSI_YELLOW, gettext("Consider setting "
8286+
"'compatibility' to an appropriate value, or\n\t"
8287+
"adding needed features to the relevant file in\n\t"
8288+
ZPOOL_SYSCONF_COMPAT_D " or " ZPOOL_DATA_COMPAT_D ".\n"));
8289+
break;
8290+
82578291
case ZPOOL_STATUS_UNSUP_FEAT_READ:
82588292
printf_color(ANSI_BOLD, gettext("status: "));
82598293
printf_color(ANSI_YELLOW, gettext("The pool cannot be accessed "
@@ -8713,6 +8747,11 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version)
87138747
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
87148748
&oldversion) == 0);
87158749

8750+
char compat[ZFS_MAXPROPLEN];
8751+
if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY, compat,
8752+
ZFS_MAXPROPLEN, NULL, B_FALSE) != 0)
8753+
compat[0] = '\0';
8754+
87168755
assert(SPA_VERSION_IS_SUPPORTED(oldversion));
87178756
assert(oldversion < version);
87188757

@@ -8727,6 +8766,13 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version)
87278766
return (1);
87288767
}
87298768

8769+
if (strcmp(compat, ZPOOL_COMPAT_LEGACY) == 0) {
8770+
(void) fprintf(stderr, gettext("Upgrade not performed because "
8771+
"'compatibility' property set to '"
8772+
ZPOOL_COMPAT_LEGACY "'.\n"));
8773+
return (1);
8774+
}
8775+
87308776
ret = zpool_upgrade(zhp, version);
87318777
if (ret != 0)
87328778
return (ret);
@@ -8868,7 +8914,10 @@ upgrade_list_older_cb(zpool_handle_t *zhp, void *arg)
88688914
"be upgraded to use feature flags. After "
88698915
"being upgraded, these pools\nwill no "
88708916
"longer be accessible by software that does not "
8871-
"support feature\nflags.\n\n"));
8917+
"support feature\nflags.\n\n"
8918+
"Note that setting a pool's 'compatibility' "
8919+
"feature to '" ZPOOL_COMPAT_LEGACY "' will\n"
8920+
"inhibit upgrades.\n\n"));
88728921
(void) printf(gettext("VER POOL\n"));
88738922
(void) printf(gettext("--- ------------\n"));
88748923
cbp->cb_first = B_FALSE;
@@ -8914,7 +8963,11 @@ upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg)
89148963
"software\nthat does not support "
89158964
"the feature. See "
89168965
"zpool-features(5) for "
8917-
"details.\n\n"));
8966+
"details.\n\n"
8967+
"Note that the pool "
8968+
"'compatibility' feature can be "
8969+
"used to inhibit\nfeature "
8970+
"upgrades.\n\n"));
89188971
(void) printf(gettext("POOL "
89198972
"FEATURE\n"));
89208973
(void) printf(gettext("------"
@@ -9970,6 +10023,63 @@ set_callback(zpool_handle_t *zhp, void *data)
997010023
int error;
997110024
set_cbdata_t *cb = (set_cbdata_t *)data;
997210025

10026+
/* Check if we have out-of-bounds features */
10027+
if (strcmp(cb->cb_propname, ZPOOL_CONFIG_COMPATIBILITY) == 0) {
10028+
boolean_t features[SPA_FEATURES];
10029+
if (zpool_do_load_compat(cb->cb_value, features) !=
10030+
ZPOOL_COMPATIBILITY_OK)
10031+
return (-1);
10032+
10033+
nvlist_t *enabled = zpool_get_features(zhp);
10034+
spa_feature_t i;
10035+
for (i = 0; i < SPA_FEATURES; i++) {
10036+
const char *fguid = spa_feature_table[i].fi_guid;
10037+
if (nvlist_exists(enabled, fguid) && !features[i])
10038+
break;
10039+
}
10040+
if (i < SPA_FEATURES)
10041+
(void) fprintf(stderr, gettext("Warning: one or "
10042+
"more features already enabled on pool '%s'\n"
10043+
"are not present in this compatibility set.\n"),
10044+
zpool_get_name(zhp));
10045+
}
10046+
10047+
/* if we're setting a feature, check it's in compatibility set */
10048+
if (zpool_prop_feature(cb->cb_propname) &&
10049+
strcmp(cb->cb_value, ZFS_FEATURE_ENABLED) == 0) {
10050+
char *fname = strchr(cb->cb_propname, '@') + 1;
10051+
spa_feature_t f;
10052+
10053+
if (zfeature_lookup_name(fname, &f) == 0) {
10054+
char compat[ZFS_MAXPROPLEN];
10055+
if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY,
10056+
compat, ZFS_MAXPROPLEN, NULL, B_FALSE) != 0)
10057+
compat[0] = '\0';
10058+
10059+
boolean_t features[SPA_FEATURES];
10060+
if (zpool_do_load_compat(compat, features) !=
10061+
ZPOOL_COMPATIBILITY_OK) {
10062+
(void) fprintf(stderr, gettext("Error: "
10063+
"cannot enable feature '%s' on pool '%s'\n"
10064+
"because the pool's 'compatibility' "
10065+
"property cannot be parsed.\n"),
10066+
fname, zpool_get_name(zhp));
10067+
return (-1);
10068+
}
10069+
10070+
if (!features[f]) {
10071+
(void) fprintf(stderr, gettext("Error: "
10072+
"cannot enable feature '%s' on pool '%s'\n"
10073+
"as it is not specified in this pool's "
10074+
"current compatibility set.\n"
10075+
"Consider setting 'compatibility' to a "
10076+
"less restrictive set, or to 'off'.\n"),
10077+
fname, zpool_get_name(zhp));
10078+
return (-1);
10079+
}
10080+
}
10081+
}
10082+
997310083
error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value);
997410084

997510085
if (!error)
@@ -10492,28 +10602,25 @@ zpool_do_version(int argc, char **argv)
1049210602
static zpool_compat_status_t
1049310603
zpool_do_load_compat(const char *compat, boolean_t *list)
1049410604
{
10495-
char badword[ZFS_MAXPROPLEN];
10496-
char badfile[MAXPATHLEN];
10605+
char report[1024];
10606+
1049710607
zpool_compat_status_t ret;
1049810608

10499-
switch (ret = zpool_load_compat(compat, list, badword, badfile)) {
10609+
ret = zpool_load_compat(compat, list, report, 1024);
10610+
switch (ret) {
10611+
1050010612
case ZPOOL_COMPATIBILITY_OK:
1050110613
break;
10502-
case ZPOOL_COMPATIBILITY_READERR:
10503-
(void) fprintf(stderr, gettext("error reading compatibility "
10504-
"file '%s'\n"), badfile);
10505-
break;
10614+
10615+
case ZPOOL_COMPATIBILITY_NOFILES:
1050610616
case ZPOOL_COMPATIBILITY_BADFILE:
10507-
(void) fprintf(stderr, gettext("compatibility file '%s' "
10508-
"too large or not newline-terminated\n"), badfile);
10509-
break;
10510-
case ZPOOL_COMPATIBILITY_BADWORD:
10511-
(void) fprintf(stderr, gettext("unknown feature '%s' in "
10512-
"compatibility file '%s'\n"), badword, badfile);
10617+
case ZPOOL_COMPATIBILITY_BADTOKEN:
10618+
(void) fprintf(stderr, "Error: %s\n", report);
1051310619
break;
10514-
case ZPOOL_COMPATIBILITY_NOFILES:
10515-
(void) fprintf(stderr, gettext("no compatibility files "
10516-
"specified\n"));
10620+
10621+
case ZPOOL_COMPATIBILITY_WARNTOKEN:
10622+
(void) fprintf(stderr, "Warning: %s\n", report);
10623+
ret = ZPOOL_COMPATIBILITY_OK;
1051710624
break;
1051810625
}
1051910626
return (ret);

include/libzfs.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ typedef enum {
393393
ZPOOL_STATUS_REBUILD_SCRUB, /* recommend scrubbing the pool */
394394
ZPOOL_STATUS_NON_NATIVE_ASHIFT, /* (e.g. 512e dev with ashift of 9) */
395395
ZPOOL_STATUS_COMPATIBILITY_ERR, /* bad 'compatibility' property */
396+
ZPOOL_STATUS_INCOMPATIBLE_FEAT, /* feature set outside compatibility */
396397

397398
/*
398399
* Finally, the following indicates a healthy pool.
@@ -922,14 +923,14 @@ extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
922923
*/
923924
typedef enum {
924925
ZPOOL_COMPATIBILITY_OK,
925-
ZPOOL_COMPATIBILITY_READERR,
926+
ZPOOL_COMPATIBILITY_WARNTOKEN,
927+
ZPOOL_COMPATIBILITY_BADTOKEN,
926928
ZPOOL_COMPATIBILITY_BADFILE,
927-
ZPOOL_COMPATIBILITY_BADWORD,
928929
ZPOOL_COMPATIBILITY_NOFILES
929930
} zpool_compat_status_t;
930931

931932
extern zpool_compat_status_t zpool_load_compat(const char *,
932-
boolean_t *, char *, char *);
933+
boolean_t *, char *, size_t);
933934

934935
#ifdef __FreeBSD__
935936

0 commit comments

Comments
 (0)