/** @file SiPropertyWriter.hpp @brief Properties Writer for SI @details Copyright (c) 2024 Acronis International GmbH @author Denis Kopyrin ([email protected]) @since $Id: $ */ #pragma once #include "debug.h" #include "file_handle_tools.h" #include "si_common.h" #include "si_size.h" #include <linux/compiler.h> #include <linux/string.h> typedef struct { uint8_t* buffer; uint8_t* properties; uint8_t* curr; uint32_t max_size; int properties_written; } si_property_writer_t; static inline void si_property_writer_init(si_property_writer_t* writer, SiProperty* buffer, uint32_t size) { writer->max_size = size; writer->buffer = (uint8_t*) buffer; writer->properties = writer->buffer; writer->curr = writer->properties; writer->properties_written = 0; } static inline uint32_t si_property_writer_size_to_data_end(const si_property_writer_t* writer, uint8_t* ptr) { return (uint32_t) (writer->curr - ptr); } static inline uint8_t* si_property_writer_get_data(const si_property_writer_t* writer) { return writer->buffer; } static inline uint32_t si_property_writer_get_size(const si_property_writer_t* writer) { return si_property_writer_size_to_data_end(writer, writer->buffer); } // Peeks buffer for given property id and returns buffer where value of 'size' can be written to. // Commit the change with 'si_property_writer_commit' when data will be written. // If 'si_property_writer_commit' is not going to be called, next 'peek' call will overwrite the previous one. // 'size' parameter is only used for validation purposes. static inline void* si_property_writer_peek(const si_property_writer_t* writer, fp_si_property_id_t prop_id, uint32_t size) { fp_si_property_value_type_t value_type = fp_si_property_id_to_fp_value_type(prop_id); SiPropertyValueType value_type_pub = fp_si_property_value_type_to_public_type(value_type); bool wants_vector_wrap = si_property_value_type_wants_vector_wrap(value_type_pub); uint32_t total_size; uint32_t curr_size; SiProperty* prop; void* data; total_size = size + sizeof(SiProperty) + (wants_vector_wrap ? sizeof(SiVector) : 0); curr_size = si_property_writer_get_size(writer); #ifdef KERNEL_MOCK BUG_ON(curr_size + total_size > writer->max_size); #endif if (unlikely(curr_size + total_size > writer->max_size)) { WPRINTF_RATELIMITED("Buffer too small for prop_id=%d: curr_size=%u total_size=%u max_size=%u", prop_id, curr_size, total_size, writer->max_size); return NULL; } prop = (SiProperty*) writer->curr; // Varsized fields are presented as SiVector if (!wants_vector_wrap) { data = prop->ValueBuffer; } else { SiVector* vector = (SiVector*) prop->ValueBuffer; data = vector->VectorBuffer; } return data; } // Commits the previously allocated buffer with 'si_property_write_peek'. static inline void si_property_writer_commit(si_property_writer_t* writer, fp_si_property_id_t prop_id, uint32_t size) { SiProperty* prop; fp_si_property_value_type_t value_type = fp_si_property_id_to_fp_value_type(prop_id); SiPropertyValueType value_type_pub = fp_si_property_value_type_to_public_type(value_type); bool wants_vector_wrap = si_property_value_type_wants_vector_wrap(value_type_pub); uint32_t total_size; uint32_t curr_size; total_size = size + sizeof(SiProperty) + (wants_vector_wrap ? sizeof(SiVector) : 0); curr_size = si_property_writer_get_size(writer); #ifdef KERNEL_MOCK BUG_ON(curr_size + total_size > writer->max_size); #endif if (unlikely(curr_size + total_size > writer->max_size)) { WPRINTF_RATELIMITED("Buffer too small for prop_id=%d: curr_size=%u total_size=%u max_size=%u", prop_id, curr_size, total_size, writer->max_size); return; } // Fill in SiProperty prop = (SiProperty*) writer->curr; prop->Size = total_size; prop->PropertyId = fp_si_property_id_to_public_type(prop_id); prop->ValueType = value_type_pub; // Adjust size of SiVector if (wants_vector_wrap) { SiVector* vector = (SiVector*) prop->ValueBuffer; vector->SizeInBytes = size; } writer->curr += total_size; writer->properties_written++; } static inline void si_property_writer_write(si_property_writer_t* writer, fp_si_property_id_t prop_id, const void* data, uint32_t size) { void* buf = si_property_writer_peek(writer, prop_id, size); if (unlikely(!buf)) return; if (likely(0 != size)) __builtin_memcpy(buf, data, size); si_property_writer_commit(writer, prop_id, size); } static inline void si_property_writer_write2(si_property_writer_t* writer, fp_si_property_id_t prop_id, const void* data1, uint32_t size1, const void* data2, uint32_t size2) { uint32_t size = size1 + size2; uint8_t* buf = (uint8_t*) si_property_writer_peek(writer, prop_id, size); if (unlikely(!buf)) return; if (unlikely(0 != size1)) __builtin_memcpy(buf , data1, size1); if (unlikely(0 != size2)) __builtin_memcpy(buf + size1, data2, size2); si_property_writer_commit(writer, prop_id, size); } #define SI_FP_PROP(propId, propIdPub, valueType, type, name) \ static inline void si_property_writer_write_##name(si_property_writer_t* writer, type val) \ { return si_property_writer_write(writer, propId, &val, sizeof(val)); } #define SI_FP_PROP_SIZED(propId, propIdPub, valueType, type, name) \ static inline void si_property_writer_write_##name(si_property_writer_t* writer, type val) \ { return si_property_writer_write(writer, propId, val.value, val.length); } #define SI_FP_PROP_HANDLE(propId, propIdPub, valueType, type, name) \ static inline void si_property_writer_write_##name(si_property_writer_t* writer, type val) \ { return si_property_writer_write2(writer, propId, &val->handle_type, sizeof(val->handle_type), val->f_handle, val->handle_bytes); } #include "si_fp_properties_x.h" #undef SI_FP_PROP #undef SI_FP_PROP_SIZED #undef SI_FP_PROP_HANDLE static inline void si_event_writer_init(si_property_writer_t* writer, SiEvent* buffer, uint32_t size) { si_property_writer_init(writer, buffer->FirstProperty, size); } static inline void si_event_writer_finalize(SiEvent* event , const si_property_writer_t* writer) { event->Size = si_property_writer_size_to_data_end(writer, (uint8_t*) event); event->PropertiesNumber = writer->properties_written; } static inline void si_info_writer_init(si_property_writer_t* writer, SiInfo* buffer, uint32_t size) { si_property_writer_init(writer, buffer->FirstProperty, size); } static inline void si_info_writer_finalize(SiInfo* info , const si_property_writer_t* writer) { info->Size = si_property_writer_size_to_data_end(writer, (uint8_t*) info); info->PropertiesNumber = writer->properties_written; }