@@ -786,7 +786,7 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
786
786
787
787
if (poolprop ) {
788
788
const char * vname = zpool_prop_to_name (ZPOOL_PROP_VERSION );
789
- const char * fname =
789
+ const char * cname =
790
790
zpool_prop_to_name (ZPOOL_PROP_COMPATIBILITY );
791
791
792
792
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,
811
811
}
812
812
813
813
/*
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
816
816
*/
817
817
if ((prop == ZPOOL_PROP_COMPATIBILITY &&
818
+ strcmp (propval , ZPOOL_COMPAT_LEGACY ) != 0 &&
818
819
nvlist_exists (proplist , vname )) ||
819
820
(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" ));
824
827
return (2 );
825
828
}
826
829
@@ -1674,6 +1677,7 @@ zpool_do_create(int argc, char **argv)
1674
1677
* - enable_pool_features (ie: no '-d' or '-o version')
1675
1678
* - it's supported by the kernel module
1676
1679
* - it's in the requested feature set
1680
+ * - warn if it's enabled but not in compat
1677
1681
*/
1678
1682
for (spa_feature_t i = 0 ; i < SPA_FEATURES ; i ++ ) {
1679
1683
char propname [MAXPATHLEN ];
@@ -1687,6 +1691,14 @@ zpool_do_create(int argc, char **argv)
1687
1691
if (strcmp (propval , ZFS_FEATURE_DISABLED ) == 0 )
1688
1692
(void ) nvlist_remove_all (props ,
1689
1693
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 );
1690
1702
} else if (
1691
1703
enable_pool_features &&
1692
1704
feat -> fi_zfs_mod_supported &&
@@ -2717,8 +2729,10 @@ show_import(nvlist_t *config, boolean_t report_error)
2717
2729
2718
2730
case ZPOOL_STATUS_FEAT_DISABLED :
2719
2731
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" ));
2722
2736
break ;
2723
2737
2724
2738
case ZPOOL_STATUS_COMPATIBILITY_ERR :
@@ -2728,6 +2742,13 @@ show_import(nvlist_t *config, boolean_t report_error)
2728
2742
"property.\n" ));
2729
2743
break ;
2730
2744
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
+
2731
2752
case ZPOOL_STATUS_UNSUP_FEAT_READ :
2732
2753
printf_color (ANSI_BOLD , gettext ("status: " ));
2733
2754
printf_color (ANSI_YELLOW , gettext ("The pool uses the following "
@@ -8055,7 +8076,8 @@ status_callback(zpool_handle_t *zhp, void *data)
8055
8076
(reason == ZPOOL_STATUS_OK ||
8056
8077
reason == ZPOOL_STATUS_VERSION_OLDER ||
8057
8078
reason == ZPOOL_STATUS_FEAT_DISABLED ||
8058
- reason == ZPOOL_STATUS_COMPATIBILITY_ERR )) {
8079
+ reason == ZPOOL_STATUS_COMPATIBILITY_ERR ||
8080
+ reason == ZPOOL_STATUS_INCOMPATIBLE_FEAT )) {
8059
8081
if (!cbp -> cb_allpools ) {
8060
8082
(void ) printf (gettext ("pool '%s' is healthy\n" ),
8061
8083
zpool_get_name (zhp ));
@@ -8254,6 +8276,18 @@ status_callback(zpool_handle_t *zhp, void *data)
8254
8276
ZPOOL_DATA_COMPAT_D ".\n" ));
8255
8277
break ;
8256
8278
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
+
8257
8291
case ZPOOL_STATUS_UNSUP_FEAT_READ :
8258
8292
printf_color (ANSI_BOLD , gettext ("status: " ));
8259
8293
printf_color (ANSI_YELLOW , gettext ("The pool cannot be accessed "
@@ -8713,6 +8747,11 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version)
8713
8747
verify (nvlist_lookup_uint64 (config , ZPOOL_CONFIG_VERSION ,
8714
8748
& oldversion ) == 0 );
8715
8749
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
+
8716
8755
assert (SPA_VERSION_IS_SUPPORTED (oldversion ));
8717
8756
assert (oldversion < version );
8718
8757
@@ -8727,6 +8766,13 @@ upgrade_version(zpool_handle_t *zhp, uint64_t version)
8727
8766
return (1 );
8728
8767
}
8729
8768
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
+
8730
8776
ret = zpool_upgrade (zhp , version );
8731
8777
if (ret != 0 )
8732
8778
return (ret );
@@ -8868,7 +8914,10 @@ upgrade_list_older_cb(zpool_handle_t *zhp, void *arg)
8868
8914
"be upgraded to use feature flags. After "
8869
8915
"being upgraded, these pools\nwill no "
8870
8916
"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" ));
8872
8921
(void ) printf (gettext ("VER POOL\n" ));
8873
8922
(void ) printf (gettext ("--- ------------\n" ));
8874
8923
cbp -> cb_first = B_FALSE ;
@@ -8914,7 +8963,11 @@ upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg)
8914
8963
"software\nthat does not support "
8915
8964
"the feature. See "
8916
8965
"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" ));
8918
8971
(void ) printf (gettext ("POOL "
8919
8972
"FEATURE\n" ));
8920
8973
(void ) printf (gettext ("------"
@@ -9970,6 +10023,63 @@ set_callback(zpool_handle_t *zhp, void *data)
9970
10023
int error ;
9971
10024
set_cbdata_t * cb = (set_cbdata_t * )data ;
9972
10025
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
+
9973
10083
error = zpool_set_prop (zhp , cb -> cb_propname , cb -> cb_value );
9974
10084
9975
10085
if (!error )
@@ -10492,28 +10602,25 @@ zpool_do_version(int argc, char **argv)
10492
10602
static zpool_compat_status_t
10493
10603
zpool_do_load_compat (const char * compat , boolean_t * list )
10494
10604
{
10495
- char badword [ ZFS_MAXPROPLEN ];
10496
- char badfile [ MAXPATHLEN ];
10605
+ char report [ 1024 ];
10606
+
10497
10607
zpool_compat_status_t ret ;
10498
10608
10499
- switch (ret = zpool_load_compat (compat , list , badword , badfile )) {
10609
+ ret = zpool_load_compat (compat , list , report , 1024 );
10610
+ switch (ret ) {
10611
+
10500
10612
case ZPOOL_COMPATIBILITY_OK :
10501
10613
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 :
10506
10616
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 );
10513
10619
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 ;
10517
10624
break ;
10518
10625
}
10519
10626
return (ret );
0 commit comments