From: benhanokh Date: Mon, 16 Feb 2026 09:23:33 +0000 (+0200) Subject: rgw/dedup crypt support X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=05b42fae543cf8a0a31d9d4797c43502d3f75f5b;p=ceph-ci.git rgw/dedup crypt support Signed-off-by: benhanokh --- diff --git a/src/rgw/driver/rados/rgw_dedup.cc b/src/rgw/driver/rados/rgw_dedup.cc index f841e8aad5a..cd9f737fc9d 100644 --- a/src/rgw/driver/rados/rgw_dedup.cc +++ b/src/rgw/driver/rados/rgw_dedup.cc @@ -434,10 +434,41 @@ namespace rgw::dedup { } ldpp_dout(dpp, 20) << __func__ << "::" << p_bucket->get_name() << "/" << obj_name << " was written to block_idx=" - << rec_info.block_id << " rec_id=" << rec_info.rec_id << dendl; + << rec_info.block_id << " rec_id=" << (int)rec_info.rec_id << dendl; return 0; } + //--------------------------------------------------------------------------- + static void report_failed_add_entry(const DoutPrefixProvider* const dpp, + const std::string &obj_name, + md5_stats_t *p_stats) + { + // We allocate memory for the dedup on startup based on the existing obj count + // If the system grew significantly since that point we won't be able to + // accommodate all the objects in the hash-table. + // Please keep in mind that it is very unlikely since duplicates objects will + // consume a single entry and since we skip small objects so in reality + // I expect the allocation to be more than sufficient. + // + // However, if we filled up the system there is still value is continuing + // with this process since we might find duplicates to existing object (which + // don't take extra space) + + int level = 15; + if (p_stats->failed_table_load % 0x10000 == 0) { + level = 5; + } + else if (p_stats->failed_table_load % 0x100 == 0) { + level = 10; + } + ldpp_dout(dpp, level) << __func__ << "::Failed p_table->add_entry (overflow) " + << obj_name << "::loaded_objects=" << p_stats->loaded_objects + << "::failed_table_load=" << p_stats->failed_table_load + << dendl; + + p_stats->failed_table_load++; + } + //--------------------------------------------------------------------------- int Background::add_record_to_dedup_table(dedup_table_t *p_table, const disk_record_t *p_rec, @@ -453,12 +484,14 @@ namespace rgw::dedup { // TBD: need stat counters return -EOVERFLOW; } + // at this stage we don't know the crypto-mode, will update later + crypt_mode_t crypt_mode; // init by default to CRYPT_MODE_NONE key_t key(p_rec->s.md5_high, p_rec->s.md5_low, size_4k_units, - p_rec->s.num_parts, sc_idx); + p_rec->s.num_parts, sc_idx, crypt_mode); bool has_shared_manifest = p_rec->has_shared_manifest(); ldpp_dout(dpp, 20) << __func__ << "::bucket=" << p_rec->bucket_name << ", obj=" << p_rec->obj_name << ", block_id=" - << (uint32_t)block_id << ", rec_id=" << (uint32_t)rec_id + << block_id << ", rec_id=" << (int)rec_id << ", shared_manifest=" << has_shared_manifest << "::num_parts=" << p_rec->s.num_parts << "::size_4k_units=" << key.size_4k_units @@ -475,30 +508,7 @@ namespace rgw::dedup { << "::loaded_objects=" << p_stats->loaded_objects << dendl; } else { - // We allocate memory for the dedup on startup based on the existing obj count - // If the system grew significantly since that point we won't be able to - // accommodate all the objects in the hash-table. - // Please keep in mind that it is very unlikely since duplicates objects will - // consume a single entry and since we skip small objects so in reality - // I expect the allocation to be more than sufficient. - // - // However, if we filled up the system there is still value is continuing - // with this process since we might find duplicates to existing object (which - // don't take extra space) - - int level = 15; - if (p_stats->failed_table_load % 0x10000 == 0) { - level = 5; - } - else if (p_stats->failed_table_load % 0x100 == 0) { - level = 10; - } - ldpp_dout(dpp, level) << __func__ << "::Failed p_table->add_entry (overflow)" - << "::loaded_objects=" << p_stats->loaded_objects - << "::failed_table_load=" << p_stats->failed_table_load - << dendl; - - p_stats->failed_table_load++; + report_failed_add_entry(dpp, p_rec->obj_name, p_stats); } return ret; } @@ -892,7 +902,9 @@ namespace rgw::dedup { << ", obj=" << p_tgt_rec->obj_name << ", block_id=" << block_id << ", rec_id=" << (int)rec_id - << ", md5_shard=" << (int)md5_shard << dendl; + << ", md5_shard=" << (int)md5_shard + << ", crypt_mode=" << p_tgt_rec->s.crypt_mode.get_crypt_mode_str() + << dendl; ldpp_dout(dpp, 20) << __func__ << "::md5_shard=" << (int)md5_shard << "::" << p_tgt_rec->bucket_name @@ -907,8 +919,10 @@ namespace rgw::dedup { disk_record_t *p_rec, const rgw::sal::Attrs &attrs, dedup_table_t *p_table, + crypt_mode_t crypt_mode, md5_stats_t *p_stats) /*IN-OUT*/ { + p_rec->s.crypt_mode = crypt_mode; // if TAIL_TAG exists -> use it as ref-tag, eitherwise take ID_TAG auto itr = attrs.find(RGW_ATTR_TAIL_TAG); if (itr != attrs.end()) { @@ -1017,8 +1031,8 @@ namespace rgw::dedup { // Need to read attributes from the Head-Object and output them to a new SLAB int Background::read_object_attribute(dedup_table_t *p_table, disk_record_t *p_rec, - disk_block_id_t old_block_id, - record_id_t old_rec_id, + disk_block_id_t block_id, + record_id_t rec_id, md5_shard_t md5_shard, md5_stats_t *p_stats /* IN-OUT */, disk_block_seq_t *p_disk, @@ -1026,7 +1040,7 @@ namespace rgw::dedup { { bool should_print_debug = cct->_conf->subsys.should_gather(); if (unlikely(should_print_debug)) { - print_record(dpp, p_rec, old_block_id, old_rec_id, md5_shard); + print_record(dpp, p_rec, block_id, rec_id, md5_shard); } p_stats->processed_objects ++; @@ -1038,8 +1052,10 @@ namespace rgw::dedup { // TBD: need stat counters return -EOVERFLOW; } + // Prev stage added keys without crypto-mode key + crypt_mode_t crypt_mode; // init by default to CRYPT_MODE_NONE key_t key_from_bucket_index(p_rec->s.md5_high, p_rec->s.md5_low, size_4k_units, - p_rec->s.num_parts, sc_idx); + p_rec->s.num_parts, sc_idx, crypt_mode); dedup_table_t::value_t src_val; int ret = p_table->get_val(&key_from_bucket_index, &src_val); if (ret != 0) { @@ -1063,24 +1079,6 @@ namespace rgw::dedup { return 0; } - // limit the number of ref_count in the SRC-OBJ to MAX_COPIES_PER_OBJ - // check <= because we also count the SRC-OBJ - if (src_val.get_count() <= MAX_COPIES_PER_OBJ) { - disk_block_id_t src_block_id = src_val.get_src_block_id(); - record_id_t src_rec_id = src_val.get_src_rec_id(); - // update the number of identical copies we got - ldpp_dout(dpp, 20) << __func__ << "::Obj " << p_rec->obj_name - << " has " << src_val.get_count() << " copies" << dendl; - p_table->inc_count(&key_from_bucket_index, src_block_id, src_rec_id); - } - else { - // We don't want more than @MAX_COPIES_PER_OBJ to prevent OMAP overload - p_stats->skipped_too_many_copies++; - ldpp_dout(dpp, 10) << __func__ << "::Obj " << p_rec->obj_name - << " has too many copies already" << dendl; - return 0; - } - // Every object after this point was counted as a dedup potential // If we conclude that it can't be dedup it should be accounted for rgw_bucket b{p_rec->tenant_name, p_rec->bucket_name, p_rec->bucket_id}; @@ -1113,13 +1111,6 @@ namespace rgw::dedup { } const rgw::sal::Attrs& attrs = p_obj->get_attrs(); - if (attrs.find(RGW_ATTR_CRYPT_MODE) != attrs.end()) { - p_stats->ingress_skip_encrypted++; - p_stats->ingress_skip_encrypted_bytes += ondisk_byte_size; - ldpp_dout(dpp, 20) <<__func__ << "::Skipping encrypted object " - << p_rec->obj_name << dendl; - return 0; - } // TBD: We should be able to support RGW_ATTR_COMPRESSION when all copies are compressed if (attrs.find(RGW_ATTR_COMPRESSION) != attrs.end()) { @@ -1158,7 +1149,8 @@ namespace rgw::dedup { sc_idx = remapper->remap(storage_class, dpp, &p_stats->failed_map_overflow); key_t key_from_obj(parsed_etag.md5_high, parsed_etag.md5_low, byte_size_to_disk_blocks(p_obj->get_size()), - parsed_etag.num_parts, sc_idx); + parsed_etag.num_parts, sc_idx, + crypt_mode /* still set to CRYPT_MODE_NONE */); if (unlikely(key_from_obj != key_from_bucket_index || p_rec->s.obj_bytes_size != p_obj->get_size())) { ldpp_dout(dpp, 15) <<__func__ << "::Skipping changed object " @@ -1167,15 +1159,87 @@ namespace rgw::dedup { return 0; } + // commit to a single key + key_t *p_key = &key_from_obj; + //=========================================================================== + // Crypt Mode is not reported in bucket-index, only now we can add it + bool crypt_mode_load = false; + itr = attrs.find(RGW_ATTR_CRYPT_MODE); + if (itr != attrs.end()) { + const std::string &crypt_mode_str = itr->second.to_str(); + ldpp_dout(dpp, 20) <<__func__ << "::encrypted object " << p_rec->obj_name + << " crypt_mode=" << crypt_mode_str << dendl; + crypt_mode.set_crypt_mode(crypt_mode_str); + crypt_mode_t::crypt_mode_id_t crypt_mode_id = crypt_mode.get_crypt_mode_id(); + if (crypt_mode_id == crypt_mode_t::CRYPT_MODE_ID_AES256) { + ldpp_dout(dpp, 20) << __func__ << "::CRYPT_MODE_ID_AES256" << dendl; + // TBD - stat_counter + } + else if (crypt_mode_id == crypt_mode_t::CRYPT_MODE_ID_RGW_AUTO) { + ldpp_dout(dpp, 20) << __func__ << "::CRYPT_MODE_ID_RGW_AUTO" << dendl; + // TBD - stat_counter + } + else { + p_stats->ingress_skip_encrypted++; + p_stats->ingress_skip_encrypted_bytes += ondisk_byte_size; + ldpp_dout(dpp, 20) <<__func__ << "::Skipping encrypted object " + << p_rec->obj_name << dendl; + return 0; + } + + // update the table with crypt_mode + p_key->set_crypt_mode(crypt_mode); + // now get the correct value with crypt_mode set in key + ret = p_table->get_val(p_key, &src_val); + if (ret != 0) { + crypt_mode_load = true; + ldpp_dout(dpp, 20) << __func__ << "::add_entry: " << p_rec->obj_name + << "::block_id=" << block_id << dendl; + ret = p_table->add_entry(p_key, block_id, rec_id, p_rec->has_shared_manifest(), + nullptr, nullptr, nullptr); // no need to update stats! + if (ret == 0) { + ret = p_table->get_val(p_key, &src_val); + ceph_assert(ret == 0); + } + else { + report_failed_add_entry(dpp, p_rec->obj_name, p_stats); + return ret; + } + } + } // CRYPT_MODE + + // limit the number of ref_count in the SRC-OBJ to MAX_COPIES_PER_OBJ + // check <= because we also count the SRC-OBJ + if (src_val.get_count() <= MAX_COPIES_PER_OBJ) { + // crypt_mode_reload already inc the counter, don't do it twice + if (!crypt_mode_load) { + disk_block_id_t src_block_id = src_val.get_src_block_id(); + record_id_t src_rec_id = src_val.get_src_rec_id(); + // update the number of identical copies we got + ldpp_dout(dpp, 20) << __func__ << "::Obj " << p_rec->obj_name + << " has " << src_val.get_count() << " copies" + << "::block_id=" << src_block_id << dendl; + + p_table->inc_count(p_key, src_block_id, src_rec_id); + } + } + else { + // We don't want more than @MAX_COPIES_PER_OBJ to prevent OMAP overload + p_stats->skipped_too_many_copies++; + ldpp_dout(dpp, 10) << __func__ << "::Obj " << p_rec->obj_name + << " has too many copies already" << dendl; + return 0; + } + //=========================================================================== + // reset flags p_rec->s.flags.clear(); - ret = add_obj_attrs_to_record(&b, p_rec, attrs, p_table, p_stats); + ret = add_obj_attrs_to_record(&b, p_rec, attrs, p_table, crypt_mode, p_stats); if (unlikely(ret != 0)) { ldpp_dout(dpp, 5) << __func__ << "::ERR: failed add_obj_attrs_to_record() ret=" << ret << "::" << cpp_strerror(-ret) << dendl; return ret; } - disk_block_seq_t::record_info_t rec_info; ret = p_disk->add_record(d_dedup_cluster_ioctx, p_rec, &rec_info); if (ret == 0) { @@ -1185,7 +1249,7 @@ namespace rgw::dedup { << p_rec->obj_name << " was written to block_idx=" << rec_info.block_id << "::rec_id=" << (int)rec_info.rec_id << "::shared_manifest=" << p_rec->has_shared_manifest() << dendl; - p_table->update_entry(&key_from_bucket_index, rec_info.block_id, + p_table->update_entry(p_key, rec_info.block_id, rec_info.rec_id, p_rec->has_shared_manifest()); } else { @@ -1256,7 +1320,7 @@ namespace rgw::dedup { &p_stats->failed_map_overflow); ceph_assert(sc_idx != remapper_t::NULL_IDX); key_t key(p_tgt_rec->s.md5_high, p_tgt_rec->s.md5_low, size_4k_units, - p_tgt_rec->s.num_parts, sc_idx); + p_tgt_rec->s.num_parts, sc_idx, p_tgt_rec->s.crypt_mode); dedup_table_t::value_t src_val; int ret = p_table->get_val(&key, &src_val); if (ret != 0) { @@ -1300,7 +1364,7 @@ namespace rgw::dedup { p_stats->failed_src_load++; // we can withstand most errors moving to the next object ldpp_dout(dpp, 5) << __func__ << "::ERR: Failed load_record(" - << src_block_id << ", " << src_rec_id << ")" << dendl; + << src_block_id << ", " << (int)src_rec_id << ")" << dendl; return 0; } diff --git a/src/rgw/driver/rados/rgw_dedup.h b/src/rgw/driver/rados/rgw_dedup.h index b1df56249e8..9ce0dfe9706 100644 --- a/src/rgw/driver/rados/rgw_dedup.h +++ b/src/rgw/driver/rados/rgw_dedup.h @@ -187,6 +187,7 @@ namespace rgw::dedup { disk_record_t *p_rec, const rgw::sal::Attrs &attrs, dedup_table_t *p_table, + crypt_mode_t crypt_mode, md5_stats_t *p_stats); /* IN-OUT */ int read_object_attribute(dedup_table_t *p_table, diff --git a/src/rgw/driver/rados/rgw_dedup_store.cc b/src/rgw/driver/rados/rgw_dedup_store.cc index d2b62651c6c..a50808d7ca4 100644 --- a/src/rgw/driver/rados/rgw_dedup_store.cc +++ b/src/rgw/driver/rados/rgw_dedup_store.cc @@ -88,6 +88,7 @@ namespace rgw::dedup { this->s.md5_high = CEPHTOH_64(p_rec->s.md5_high); this->s.md5_low = CEPHTOH_64(p_rec->s.md5_low); this->s.obj_bytes_size = CEPHTOH_64(p_rec->s.obj_bytes_size); + this->s.crypt_mode = p_rec->s.crypt_mode; this->s.bucket_id_len = CEPHTOH_16(p_rec->s.bucket_id_len); this->s.tenant_name_len = CEPHTOH_16(p_rec->s.tenant_name_len); @@ -149,6 +150,7 @@ namespace rgw::dedup { p_rec->s.md5_high = HTOCEPH_64(this->s.md5_high); p_rec->s.md5_low = HTOCEPH_64(this->s.md5_low); p_rec->s.obj_bytes_size = HTOCEPH_64(this->s.obj_bytes_size); + p_rec->s.crypt_mode = this->s.crypt_mode; p_rec->s.bucket_id_len = HTOCEPH_16(this->bucket_id.length()); p_rec->s.tenant_name_len = HTOCEPH_16(this->tenant_name.length()); @@ -267,6 +269,7 @@ namespace rgw::dedup { stream << rec.ref_tag << "::" << rec.s.ref_tag_len << "\n"; stream << "num_parts = " << rec.s.num_parts << "\n"; stream << "obj_size = " << rec.s.obj_bytes_size/1024 <<" KiB" << "\n"; + stream << "Crypt Mode= " << rec.s.crypt_mode.get_crypt_mode_str() << "\n"; stream << "MD5 = " << std::hex << rec.s.md5_high << rec.s.md5_low << "\n"; stream << "HASH = "; // BLAKE3 hash has 256 bit splitted into multiple 64bit units diff --git a/src/rgw/driver/rados/rgw_dedup_store.h b/src/rgw/driver/rados/rgw_dedup_store.h index 7bca5d4e70e..c6d156ebb42 100644 --- a/src/rgw/driver/rados/rgw_dedup_store.h +++ b/src/rgw/driver/rados/rgw_dedup_store.h @@ -170,7 +170,8 @@ namespace rgw::dedup { uint16_t ref_tag_len; uint16_t manifest_len; - uint8_t pad[6]; + uint8_t pad[5]; + crypt_mode_t crypt_mode; uint64_t shared_manifest; // 64bit hash of the SRC object manifest uint64_t hash[4]; // 4 * 8 Bytes of BLAKE3 diff --git a/src/rgw/driver/rados/rgw_dedup_table.h b/src/rgw/driver/rados/rgw_dedup_table.h index 4a46db6e5b7..f82394a1b13 100644 --- a/src/rgw/driver/rados/rgw_dedup_table.h +++ b/src/rgw/driver/rados/rgw_dedup_table.h @@ -22,18 +22,63 @@ namespace rgw::dedup { // 24 Bytes key struct key_t { + private: + struct __attribute__ ((packed)) key_flags_t { + private: + static constexpr uint8_t KEY_FLAG_CRYPT_MODE_AES256 = 0x01; + static constexpr uint8_t KEY_FLAG_CRYPT_MODE_RGW_AUTO = 0x02; + public: + key_flags_t() : flags(0) {} + key_flags_t(uint8_t _flags) : flags(_flags) {} + inline void clear() { this->flags = 0; } + inline bool is_crypt_mode_aes256() const { + return ((flags & KEY_FLAG_CRYPT_MODE_AES256) != 0); + } + inline void set_crypt_mode_aes256() { + ceph_assert(!is_crypt_mode_rgw_auto()); + flags |= KEY_FLAG_CRYPT_MODE_AES256; + } + inline bool is_crypt_mode_rgw_auto() const { + return ((flags & KEY_FLAG_CRYPT_MODE_RGW_AUTO) != 0); + } + inline void set_crypt_mode_rgw_auto() { + ceph_assert(!is_crypt_mode_aes256()); + flags |= KEY_FLAG_CRYPT_MODE_RGW_AUTO; + } + inline bool is_crypt_mode_none() const { + return ((flags & (KEY_FLAG_CRYPT_MODE_AES256|KEY_FLAG_CRYPT_MODE_RGW_AUTO)) == 0); + } + private: + uint8_t flags; + }; + public: key_t() { ;} key_t(uint64_t _md5_high, uint64_t _md5_low, uint32_t _size_4k_units, uint16_t _num_parts, - uint8_t _stor_class_idx) { + uint8_t _stor_class_idx, + crypt_mode_t crypt_mode) { md5_high = _md5_high; md5_low = _md5_low; size_4k_units = _size_4k_units; num_parts = _num_parts; stor_class_idx = _stor_class_idx; - pad8 = 0; + set_crypt_mode(crypt_mode); + } + + void set_crypt_mode(crypt_mode_t crypt_mode) { + crypt_mode_t::crypt_mode_id_t crypt_mode_id = crypt_mode.get_crypt_mode_id(); + if (crypt_mode_id == crypt_mode_t::CRYPT_MODE_ID_AES256) { + key_flags.set_crypt_mode_aes256(); + } + else if (crypt_mode_id == crypt_mode_t::CRYPT_MODE_ID_RGW_AUTO) { + key_flags.set_crypt_mode_rgw_auto(); + } + else if (crypt_mode_id != crypt_mode_t::CRYPT_MODE_ID_NONE) { + //ceph_abort("unsupported_crypt_mode"); + // what should we do here ??? + } } bool operator==(const struct key_t& other) const { @@ -58,7 +103,7 @@ namespace rgw::dedup { uint32_t size_4k_units; // Object size in 4KB units max out at 16TB (AWS MAX-SIZE is 5TB) uint16_t num_parts; // How many parts were used in multipart upload (AWS MAX-PART is 10,000) uint8_t stor_class_idx;// storage class id - uint8_t pad8; + key_flags_t key_flags; } __attribute__((__packed__)); static_assert(sizeof(key_t) == 24); diff --git a/src/rgw/driver/rados/rgw_dedup_utils.h b/src/rgw/driver/rados/rgw_dedup_utils.h index abe62432122..0ea80554c3b 100644 --- a/src/rgw/driver/rados/rgw_dedup_utils.h +++ b/src/rgw/driver/rados/rgw_dedup_utils.h @@ -92,6 +92,64 @@ namespace rgw::dedup { uint8_t flags; }; + struct __attribute__ ((packed)) crypt_mode_t { + enum class crypt_mode_id_t : uint8_t { + CRYPT_MODE_ID_NONE, + CRYPT_MODE_ID_AES256, + CRYPT_MODE_ID_RGW_AUTO, + CRYPT_MODE_ID_SSE_C_AES256, + CRYPT_MODE_ID_SSE_KMS, + CRYPT_MODE_ID_INVALID + }; + + using enum crypt_mode_id_t; + + static constexpr std::string_view CRYPT_MODE_STR_NONE = ""; + static constexpr std::string_view CRYPT_MODE_STR_AES256 = "AES256"; + static constexpr std::string_view CRYPT_MODE_STR_RGW_AUTO = "RGW-AUTO"; + static constexpr std::string_view CRYPT_MODE_STR_SSE_C_AES256 = "SSE-C-AES256"; + static constexpr std::string_view CRYPT_MODE_STR_SSE_KMS = "SSE-KMS"; + static constexpr std::string_view CRYPT_MODE_STR_INVALID = "INVALID"; + + crypt_mode_t() : crypt_mode_id(crypt_mode_id_t::CRYPT_MODE_ID_NONE) {} + + crypt_mode_t(const std::string &crypt_mode_str) + : crypt_mode_id(parse_crypt_mode(crypt_mode_str)) {} + + void set_crypt_mode(const std::string &crypt_mode_str) { + crypt_mode_id = parse_crypt_mode(crypt_mode_str); + } + + void set_crypt_mode(crypt_mode_id_t _crypt_mode_id) { + crypt_mode_id = _crypt_mode_id; + } + + crypt_mode_id_t get_crypt_mode_id() const { + return crypt_mode_id; + } + + const std::string_view& get_crypt_mode_str() const { + if (crypt_mode_id == CRYPT_MODE_ID_NONE) return CRYPT_MODE_STR_NONE; + if (crypt_mode_id == CRYPT_MODE_ID_AES256) return CRYPT_MODE_STR_AES256; + if (crypt_mode_id == CRYPT_MODE_ID_RGW_AUTO) return CRYPT_MODE_STR_RGW_AUTO; + if (crypt_mode_id == CRYPT_MODE_ID_SSE_C_AES256) return CRYPT_MODE_STR_SSE_C_AES256; + if (crypt_mode_id == CRYPT_MODE_ID_SSE_KMS) return CRYPT_MODE_STR_SSE_KMS; + return CRYPT_MODE_STR_INVALID; + } + + private: + static crypt_mode_id_t parse_crypt_mode(const std::string &crypt_mode_str) { + if (crypt_mode_str.empty()) return CRYPT_MODE_ID_NONE; + if (crypt_mode_str == CRYPT_MODE_STR_AES256) return CRYPT_MODE_ID_AES256; + if (crypt_mode_str == CRYPT_MODE_STR_RGW_AUTO) return CRYPT_MODE_ID_RGW_AUTO; + if (crypt_mode_str == CRYPT_MODE_STR_SSE_C_AES256) return CRYPT_MODE_ID_SSE_C_AES256; + if (crypt_mode_str == CRYPT_MODE_STR_SSE_KMS) return CRYPT_MODE_ID_SSE_KMS; + return CRYPT_MODE_ID_INVALID; + } + + crypt_mode_id_t crypt_mode_id; + }; + class alignas(8) Throttle { friend void validate_max_calls_offset(); public: