2323 * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
2424 * Copyright (c) 2013 Martin Matuska. All rights reserved.
2525 * Copyright 2019 Joyent, Inc.
26+ * Copyright (c) 2022 Hewlett Packard Enterprise Development LP.
2627 */
2728
2829#include <sys/zfs_context.h>
4142
4243#define ZPROP_INHERIT_SUFFIX "$inherit"
4344#define ZPROP_RECVD_SUFFIX "$recvd"
45+ #define ZPROP_IUV_SUFFIX "$iuv"
4446
4547static int
4648dodefault (zfs_prop_t prop , int intsz , int numints , void * buf )
@@ -69,6 +71,16 @@ dodefault(zfs_prop_t prop, int intsz, int numints, void *buf)
6971 return (0 );
7072}
7173
74+ static int
75+ dsl_prop_known_index (zfs_prop_t prop , uint64_t value )
76+ {
77+ const char * str = NULL ;
78+ if (zfs_prop_get_type (prop ) == PROP_TYPE_INDEX )
79+ return (!zfs_prop_index_to_string (prop , value , & str ));
80+
81+ return (-1 );
82+ }
83+
7284int
7385dsl_prop_get_dd (dsl_dir_t * dd , const char * propname ,
7486 int intsz , int numints , void * buf , char * setpoint , boolean_t snapshot )
@@ -81,6 +93,7 @@ dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
8193 boolean_t inheriting = B_FALSE ;
8294 char * inheritstr ;
8395 char * recvdstr ;
96+ char * iuvstr ;
8497
8598 ASSERT (dsl_pool_config_held (dd -> dd_pool ));
8699
@@ -91,6 +104,7 @@ dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
91104 inheritable = (prop == ZPROP_USERPROP || zfs_prop_inheritable (prop ));
92105 inheritstr = kmem_asprintf ("%s%s" , propname , ZPROP_INHERIT_SUFFIX );
93106 recvdstr = kmem_asprintf ("%s%s" , propname , ZPROP_RECVD_SUFFIX );
107+ iuvstr = kmem_asprintf ("%s%s" , propname , ZPROP_IUV_SUFFIX );
94108
95109 /*
96110 * Note: dd may become NULL, therefore we shouldn't dereference it
@@ -105,6 +119,18 @@ dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
105119 inheriting = B_TRUE ;
106120 }
107121
122+ /* Check for a iuv value. */
123+ err = zap_lookup (mos , dsl_dir_phys (dd )-> dd_props_zapobj ,
124+ iuvstr , intsz , numints , buf );
125+ if (dsl_prop_known_index (zfs_name_to_prop (propname ),
126+ * (uint64_t * )buf ) != 1 )
127+ err = ENOENT ;
128+ if (err != ENOENT ) {
129+ if (setpoint != NULL && err == 0 )
130+ dsl_dir_name (dd , setpoint );
131+ break ;
132+ }
133+
108134 /* Check for a local value. */
109135 err = zap_lookup (mos , dsl_dir_phys (dd )-> dd_props_zapobj ,
110136 propname , intsz , numints , buf );
@@ -155,6 +181,7 @@ dsl_prop_get_dd(dsl_dir_t *dd, const char *propname,
155181
156182 kmem_strfree (inheritstr );
157183 kmem_strfree (recvdstr );
184+ kmem_strfree (iuvstr );
158185
159186 return (err );
160187}
@@ -647,6 +674,45 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj,
647674 dsl_dir_rele (dd , FTAG );
648675}
649676
677+
678+ /*
679+ * For newer values in zfs index type properties, we add a new key
680+ * propname$iuv (iuv = Ignore Unknown Values) to the properties zap object
681+ * to store the new property value and store the default value in the
682+ * existing prop key. So that the propname$iuv key is ignored by the older zfs
683+ * versions and the default property value from the existing prop key is
684+ * used.
685+ */
686+ static void
687+ dsl_prop_set_iuv (objset_t * mos , uint64_t zapobj , const char * propname ,
688+ int intsz , int numints , const void * value , dmu_tx_t * tx )
689+ {
690+ char * iuvstr = kmem_asprintf ("%s%s" , propname , ZPROP_IUV_SUFFIX );
691+ boolean_t iuv = B_FALSE ;
692+ zfs_prop_t prop = zfs_name_to_prop (propname );
693+
694+ switch (prop ) {
695+ case ZFS_PROP_REDUNDANT_METADATA :
696+ if (* (uint64_t * )value == ZFS_REDUNDANT_METADATA_SOME ||
697+ * (uint64_t * )value == ZFS_REDUNDANT_METADATA_NONE )
698+ iuv = B_TRUE ;
699+ break ;
700+ default :
701+ break ;
702+ }
703+
704+ if (iuv ) {
705+ VERIFY0 (zap_update (mos , zapobj , iuvstr , intsz , numints ,
706+ value , tx ));
707+ uint64_t val = zfs_prop_default_numeric (prop );
708+ VERIFY0 (zap_update (mos , zapobj , propname , intsz , numints ,
709+ & val , tx ));
710+ } else {
711+ zap_remove (mos , zapobj , iuvstr , tx );
712+ }
713+ kmem_strfree (iuvstr );
714+ }
715+
650716void
651717dsl_prop_set_sync_impl (dsl_dataset_t * ds , const char * propname ,
652718 zprop_source_t source , int intsz , int numints , const void * value ,
@@ -659,6 +725,7 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,
659725 const char * valstr = NULL ;
660726 char * inheritstr ;
661727 char * recvdstr ;
728+ char * iuvstr ;
662729 char * tbuf = NULL ;
663730 int err ;
664731 uint64_t version = spa_version (ds -> ds_dir -> dd_pool -> dp_spa );
@@ -692,6 +759,7 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,
692759
693760 inheritstr = kmem_asprintf ("%s%s" , propname , ZPROP_INHERIT_SUFFIX );
694761 recvdstr = kmem_asprintf ("%s%s" , propname , ZPROP_RECVD_SUFFIX );
762+ iuvstr = kmem_asprintf ("%s%s" , propname , ZPROP_IUV_SUFFIX );
695763
696764 switch ((int )source ) {
697765 case ZPROP_SRC_NONE :
@@ -709,11 +777,14 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,
709777 /*
710778 * remove propname$inherit
711779 * set propname -> value
780+ * set propname$iuv -> new property value
712781 */
713782 err = zap_remove (mos , zapobj , inheritstr , tx );
714783 ASSERT (err == 0 || err == ENOENT );
715784 VERIFY0 (zap_update (mos , zapobj , propname ,
716785 intsz , numints , value , tx ));
786+ (void ) dsl_prop_set_iuv (mos , zapobj , propname , intsz ,
787+ numints , value , tx );
717788 break ;
718789 case ZPROP_SRC_INHERITED :
719790 /*
@@ -723,6 +794,8 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,
723794 */
724795 err = zap_remove (mos , zapobj , propname , tx );
725796 ASSERT (err == 0 || err == ENOENT );
797+ err = zap_remove (mos , zapobj , iuvstr , tx );
798+ ASSERT (err == 0 || err == ENOENT );
726799 if (version >= SPA_VERSION_RECVD_PROPS &&
727800 dsl_prop_get_int_ds (ds , ZPROP_HAS_RECVD , & dummy ) == 0 ) {
728801 dummy = 0 ;
@@ -763,6 +836,7 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname,
763836
764837 kmem_strfree (inheritstr );
765838 kmem_strfree (recvdstr );
839+ kmem_strfree (iuvstr );
766840
767841 /*
768842 * If we are left with an empty snap zap we can destroy it.
@@ -1012,6 +1086,14 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj,
10121086
10131087 propname = za .za_name ;
10141088 source = setpoint ;
1089+
1090+ /* Skip if iuv entries are preset. */
1091+ valstr = kmem_asprintf ("%s%s" , propname ,
1092+ ZPROP_IUV_SUFFIX );
1093+ err = zap_contains (mos , propobj , valstr );
1094+ kmem_strfree (valstr );
1095+ if (err == 0 )
1096+ continue ;
10151097 } else if (strcmp (suffix , ZPROP_INHERIT_SUFFIX ) == 0 ) {
10161098 /* Skip explicitly inherited entries. */
10171099 continue ;
@@ -1044,6 +1126,16 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj,
10441126
10451127 source = ((flags & DSL_PROP_GET_INHERITING ) ?
10461128 setpoint : ZPROP_SOURCE_VAL_RECVD );
1129+ } else if (strcmp (suffix , ZPROP_IUV_SUFFIX ) == 0 ) {
1130+ (void ) strlcpy (buf , za .za_name ,
1131+ MIN (sizeof (buf ), suffix - za .za_name + 1 ));
1132+ propname = buf ;
1133+ source = setpoint ;
1134+ prop = zfs_name_to_prop (propname );
1135+
1136+ if (dsl_prop_known_index (prop ,
1137+ za .za_first_integer ) != 1 )
1138+ continue ;
10471139 } else {
10481140 /*
10491141 * For backward compatibility, skip suffixes we don't
0 commit comments