302 строки
9.3 KiB
C
302 строки
9.3 KiB
C
/*
|
|
* Copyright (c) Microsoft Corporation
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
#include "ebpf_pinning_table.h"
|
|
|
|
#include "ebpf_object.h"
|
|
|
|
typedef struct _ebpf_pinning_table
|
|
{
|
|
_Requires_lock_held_(&lock) ebpf_hash_table_t* hash_table;
|
|
ebpf_lock_t lock;
|
|
} ebpf_pinning_table_t;
|
|
|
|
static ebpf_hash_table_compare_result_t
|
|
_ebpf_pining_table_compare_function(const uint8_t* key1, const uint8_t* key2)
|
|
{
|
|
const ebpf_utf8_string_t* first_key = *(ebpf_utf8_string_t**)key1;
|
|
const ebpf_utf8_string_t* second_key = *(ebpf_utf8_string_t**)key2;
|
|
size_t min_length = min(first_key->length, second_key->length);
|
|
|
|
// Note: This is not a lexicographical sort order.
|
|
int compare_result = memcmp(first_key->value, second_key->value, min_length);
|
|
if (compare_result < 0)
|
|
return EBPF_HASH_TABLE_LESS_THAN;
|
|
else if (compare_result > 0)
|
|
return EBPF_HASH_TABLE_GREATER_THAN;
|
|
else if (first_key->length < second_key->length)
|
|
return EBPF_HASH_TABLE_LESS_THAN;
|
|
else if (first_key->length > second_key->length)
|
|
return EBPF_HASH_TABLE_GREATER_THAN;
|
|
else
|
|
return EBPF_HASH_TABLE_EQUAL;
|
|
}
|
|
|
|
static void
|
|
_ebpf_pinning_entry_free(ebpf_pinning_entry_t* pinning_entry)
|
|
{
|
|
if (!pinning_entry) {
|
|
return;
|
|
}
|
|
ebpf_object_release_reference(pinning_entry->object);
|
|
ebpf_free(pinning_entry->name.value);
|
|
ebpf_free(pinning_entry);
|
|
}
|
|
|
|
ebpf_result_t
|
|
ebpf_pinning_table_allocate(ebpf_pinning_table_t** pinning_table)
|
|
{
|
|
ebpf_result_t return_value;
|
|
*pinning_table = ebpf_allocate(sizeof(ebpf_pinning_table_t));
|
|
if (*pinning_table == NULL) {
|
|
return_value = EBPF_NO_MEMORY;
|
|
goto Done;
|
|
}
|
|
|
|
memset(*pinning_table, 0, sizeof(ebpf_pinning_table_t));
|
|
|
|
ebpf_lock_create(&(*pinning_table)->lock);
|
|
|
|
return_value = ebpf_hash_table_create(
|
|
&(*pinning_table)->hash_table,
|
|
ebpf_allocate,
|
|
ebpf_free,
|
|
sizeof(ebpf_utf8_string_t*),
|
|
sizeof(ebpf_pinning_entry_t*),
|
|
_ebpf_pining_table_compare_function);
|
|
|
|
if (return_value != EBPF_SUCCESS)
|
|
goto Done;
|
|
|
|
return_value = EBPF_SUCCESS;
|
|
Done:
|
|
if (return_value != EBPF_SUCCESS) {
|
|
if ((*pinning_table))
|
|
ebpf_hash_table_destroy((*pinning_table)->hash_table);
|
|
|
|
ebpf_free(*pinning_table);
|
|
}
|
|
|
|
return return_value;
|
|
}
|
|
|
|
void
|
|
ebpf_pinning_table_free(ebpf_pinning_table_t* pinning_table)
|
|
{
|
|
ebpf_result_t return_value;
|
|
ebpf_utf8_string_t* key;
|
|
|
|
for (;;) {
|
|
return_value = ebpf_hash_table_next_key(pinning_table->hash_table, NULL, (uint8_t*)&key);
|
|
if (return_value != EBPF_SUCCESS) {
|
|
break;
|
|
}
|
|
ebpf_pinning_table_delete(pinning_table, key);
|
|
}
|
|
|
|
ebpf_hash_table_destroy(pinning_table->hash_table);
|
|
ebpf_free(pinning_table);
|
|
}
|
|
|
|
ebpf_result_t
|
|
ebpf_pinning_table_insert(ebpf_pinning_table_t* pinning_table, const ebpf_utf8_string_t* name, ebpf_object_t* object)
|
|
{
|
|
ebpf_lock_state_t state;
|
|
ebpf_result_t return_value;
|
|
ebpf_utf8_string_t* new_key;
|
|
ebpf_pinning_entry_t* new_pinning_entry;
|
|
const ebpf_utf8_string_t* existing_key = name;
|
|
ebpf_pinning_entry_t** existing_pinning_entry;
|
|
|
|
new_pinning_entry = ebpf_allocate(sizeof(ebpf_pinning_entry_t));
|
|
if (!new_pinning_entry) {
|
|
return_value = EBPF_NO_MEMORY;
|
|
goto Done;
|
|
}
|
|
|
|
return_value = ebpf_duplicate_utf8_string(&new_pinning_entry->name, name);
|
|
if (return_value != EBPF_SUCCESS)
|
|
goto Done;
|
|
|
|
new_pinning_entry->object = object;
|
|
ebpf_object_acquire_reference(object);
|
|
new_key = &new_pinning_entry->name;
|
|
|
|
state = ebpf_lock_lock(&pinning_table->lock);
|
|
|
|
return_value = ebpf_hash_table_find(
|
|
pinning_table->hash_table, (const uint8_t*)&existing_key, (uint8_t**)&existing_pinning_entry);
|
|
if (return_value == EBPF_SUCCESS) {
|
|
return_value = EBPF_OBJECT_ALREADY_EXISTS;
|
|
} else {
|
|
return_value = ebpf_hash_table_update(
|
|
pinning_table->hash_table, (const uint8_t*)&new_key, (const uint8_t*)&new_pinning_entry);
|
|
if (return_value == EBPF_SUCCESS)
|
|
new_pinning_entry = NULL;
|
|
}
|
|
|
|
ebpf_lock_unlock(&pinning_table->lock, state);
|
|
|
|
Done:
|
|
_ebpf_pinning_entry_free(new_pinning_entry);
|
|
|
|
return return_value;
|
|
}
|
|
|
|
ebpf_result_t
|
|
ebpf_pinning_table_find(ebpf_pinning_table_t* pinning_table, const ebpf_utf8_string_t* name, ebpf_object_t** object)
|
|
{
|
|
ebpf_lock_state_t state;
|
|
ebpf_result_t return_value;
|
|
const ebpf_utf8_string_t* existing_key = name;
|
|
ebpf_pinning_entry_t** existing_pinning_entry;
|
|
|
|
state = ebpf_lock_lock(&pinning_table->lock);
|
|
return_value = ebpf_hash_table_find(
|
|
pinning_table->hash_table, (const uint8_t*)&existing_key, (uint8_t**)&existing_pinning_entry);
|
|
|
|
if (return_value == EBPF_SUCCESS) {
|
|
*object = (*existing_pinning_entry)->object;
|
|
ebpf_object_acquire_reference(*object);
|
|
}
|
|
|
|
ebpf_lock_unlock(&pinning_table->lock, state);
|
|
|
|
return return_value;
|
|
}
|
|
|
|
ebpf_result_t
|
|
ebpf_pinning_table_delete(ebpf_pinning_table_t* pinning_table, const ebpf_utf8_string_t* name)
|
|
{
|
|
ebpf_lock_state_t state;
|
|
ebpf_result_t return_value;
|
|
const ebpf_utf8_string_t* existing_key = name;
|
|
ebpf_pinning_entry_t** existing_pinning_entry;
|
|
|
|
state = ebpf_lock_lock(&pinning_table->lock);
|
|
return_value = ebpf_hash_table_find(
|
|
pinning_table->hash_table, (const uint8_t*)&existing_key, (uint8_t**)&existing_pinning_entry);
|
|
if (return_value == EBPF_SUCCESS) {
|
|
ebpf_pinning_entry_t* entry = *existing_pinning_entry;
|
|
// Note: Can't fail because we first checked the entry exists.
|
|
ebpf_hash_table_delete(pinning_table->hash_table, (const uint8_t*)&existing_key);
|
|
_ebpf_pinning_entry_free(entry);
|
|
}
|
|
ebpf_lock_unlock(&pinning_table->lock, state);
|
|
|
|
return return_value;
|
|
}
|
|
|
|
ebpf_result_t
|
|
ebpf_pinning_table_enumerate_entries(
|
|
_In_ ebpf_pinning_table_t* pinning_table,
|
|
ebpf_object_type_t object_type,
|
|
_Out_ uint16_t* entry_count,
|
|
_Outptr_result_buffer_maybenull_(*entry_count) ebpf_pinning_entry_t** pinning_entries)
|
|
{
|
|
ebpf_result_t result = EBPF_SUCCESS;
|
|
ebpf_lock_state_t state = 0;
|
|
bool lock_held = FALSE;
|
|
uint16_t local_entry_count = 0;
|
|
uint16_t entries_array_length = 0;
|
|
ebpf_pinning_entry_t* local_pinning_entries = NULL;
|
|
ebpf_utf8_string_t* next_object_name;
|
|
ebpf_pinning_entry_t* new_entry = NULL;
|
|
|
|
if ((entry_count == NULL) || (pinning_entries == NULL)) {
|
|
result = EBPF_INVALID_ARGUMENT;
|
|
goto Exit;
|
|
}
|
|
|
|
state = ebpf_lock_lock(&pinning_table->lock);
|
|
lock_held = TRUE;
|
|
|
|
// Get output array length by finding how many entries are there in the pinning table.
|
|
entries_array_length = (uint16_t)ebpf_hash_table_key_count(pinning_table->hash_table);
|
|
|
|
// Exit if there are no entries.
|
|
if (entries_array_length == 0)
|
|
goto Exit;
|
|
|
|
// Allocate the output array for storing the pinning entries.
|
|
local_pinning_entries = (ebpf_pinning_entry_t*)ebpf_allocate(sizeof(ebpf_pinning_entry_t) * entries_array_length);
|
|
if (local_pinning_entries == NULL) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// Loop through the entries in the hashtable.
|
|
next_object_name = NULL;
|
|
for (;;) {
|
|
ebpf_pinning_entry_t** next_pinning_entry = NULL;
|
|
|
|
// Find next pinning entry, if any.
|
|
result = ebpf_hash_table_next_key_and_value(
|
|
pinning_table->hash_table,
|
|
(const uint8_t*)((next_object_name == NULL) ? NULL : &next_object_name),
|
|
(uint8_t*)&next_object_name,
|
|
(uint8_t**)&next_pinning_entry);
|
|
|
|
if (result == EBPF_NO_MORE_KEYS) {
|
|
// Reached end of hashtable.
|
|
result = EBPF_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (result != EBPF_SUCCESS)
|
|
goto Exit;
|
|
|
|
// Skip entries that don't match the input object type.
|
|
if (object_type != ebpf_object_get_type((*next_pinning_entry)->object)) {
|
|
continue;
|
|
}
|
|
|
|
local_entry_count++;
|
|
ebpf_assert(local_entry_count <= entries_array_length);
|
|
|
|
// Copy the next pinning entry to a new entry in the output array.
|
|
new_entry = &local_pinning_entries[local_entry_count - 1];
|
|
new_entry->object = (*next_pinning_entry)->object;
|
|
|
|
// Take reference on underlying ebpf_object.
|
|
ebpf_object_acquire_reference(new_entry->object);
|
|
|
|
// Duplicate pinning object name.
|
|
result = ebpf_duplicate_utf8_string(&new_entry->name, &(*next_pinning_entry)->name);
|
|
if (result != EBPF_SUCCESS)
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
// Release lock if held.
|
|
if (lock_held)
|
|
ebpf_lock_unlock(&pinning_table->lock, state);
|
|
|
|
if (result != EBPF_SUCCESS) {
|
|
ebpf_pinning_entries_release(local_entry_count, local_pinning_entries);
|
|
local_entry_count = 0;
|
|
local_pinning_entries = NULL;
|
|
}
|
|
|
|
// Set output parameters.
|
|
*entry_count = local_entry_count;
|
|
*pinning_entries = local_pinning_entries;
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
ebpf_pinning_entries_release(uint16_t entry_count, _In_count_(entry_count) ebpf_pinning_entry_t* pinning_entries)
|
|
{
|
|
uint16_t index;
|
|
|
|
for (index = 0; index < entry_count; index++) {
|
|
ebpf_pinning_entry_t* entry = &pinning_entries[index];
|
|
ebpf_free(entry->name.value);
|
|
entry->name.value = NULL;
|
|
ebpf_object_release_reference(entry->object);
|
|
}
|
|
ebpf_free(pinning_entries);
|
|
}
|