????
| Current Path : /var/lib/dkms/file_protector/1.1-1578/source/ |
| Current File : //var/lib/dkms/file_protector/1.1-1578/source/si_writer.h |
/**
@file SiPropertyWriter.hpp
@brief Properties Writer for SI
@details Copyright (c) 2024 Acronis International GmbH
@author Denis Kopyrin (Denis.Kopyrin@acronis.com)
@since $Id: $
*/
#pragma once
#include "file_handle_tools.h"
#include "si_common.h"
#include "si_size.h"
#ifndef BPF_PROGRAM
#include <linux/compiler.h>
#include <linux/string.h>
#include "debug.h"
#endif
typedef struct
{
int properties_written;
#ifndef BPF_PROGRAM
uint32_t max_size;
uint8_t* buffer;
#endif
uint8_t* curr;
} si_property_writer_t;
static inline void si_property_writer_init(si_property_writer_t* writer, SiProperty* buffer, uint32_t size)
{
writer->properties_written = 0;
#ifndef BPF_PROGRAM
writer->max_size = size;
writer->buffer = (uint8_t*) buffer;
#endif
writer->curr = (uint8_t*) buffer;
}
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);
}
#ifndef BPF_PROGRAM
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);
}
#endif
// Compiler may erroneously choose not inline 'si_property_writer_peek' and 'si_property_writer_commit' functions.
// Those functions expect that its parameters are const propagated, so they must be always inlined.
// 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 __attribute__((always_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);
#ifndef BPF_PROGRAM
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;
}
#endif
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;
}
#ifdef BPF_PROGRAM
// Verifier will complain if we will overrun the buffer so help clang remove extra checks.
__builtin_assume(data != NULL);
#endif
return data;
}
// Commits the previously allocated buffer with 'si_property_write_peek'.
static inline __attribute__((always_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);
#ifndef BPF_PROGRAM
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;
}
#endif
// 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++;
}
#ifdef BPF_PROGRAM
static inline void si_write(void *res, const void *p, uint32_t size)
{
switch (size) {
case 1: *(uint8_t *)res = *(uint8_t *)p; break;
case 2: *(uint16_t *)res = *(uint16_t *)p; break;
case 4: *(uint32_t *)res = *(uint32_t *)p; break;
case 8: *(uint64_t *)res = *(uint64_t *)p; break;
case 16: { *(uint64_t *)res = *(uint64_t *)p; *(uint64_t *)(res + 8) = *(uint64_t *)(p + 8); break; }
default:
__builtin_memcpy(res, p, size);
}
}
#else
#define si_write __builtin_memcpy
#endif
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))
si_write(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))
si_write(buf, data1, size1);
if (unlikely(0 != size2))
si_write(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;
}