2024-02-17 00:40:09 +01:00
|
|
|
#include "entry.h"
|
|
|
|
|
|
|
|
#include "utils.h"
|
2024-02-17 02:28:17 +01:00
|
|
|
#include "disk.h"
|
2024-02-17 00:40:09 +01:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2024-02-17 00:57:30 +01:00
|
|
|
#include <errno.h>
|
2024-02-17 02:28:17 +01:00
|
|
|
#include <unistd.h>
|
2024-02-17 12:34:24 +01:00
|
|
|
#include <libgen.h>
|
2024-02-17 12:55:21 +01:00
|
|
|
#include <dirent.h>
|
2024-02-17 00:40:09 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
bool is_entry_id_valid(const char* entry_id) {
|
|
|
|
if (entry_id == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
size_t length = strlen(entry_id);
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Check that the length is valid
|
2024-02-17 00:40:09 +01:00
|
|
|
if (length == 0 || length > MAX_ENTRY_LENGTH)
|
|
|
|
return false;
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Check that the entry id does not contain any slashes
|
2024-02-17 00:40:09 +01:00
|
|
|
for (size_t i = 0; i < length; i++)
|
|
|
|
if (entry_id[i] == '/')
|
|
|
|
return false;
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Check that the entry id is not a reserved name
|
|
|
|
if (strcmp(entry_id, ".") == 0 || strcmp(entry_id, "..") == 0)
|
|
|
|
return false;
|
|
|
|
|
2024-02-17 00:40:09 +01:00
|
|
|
return true;
|
2024-02-17 00:45:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Result get_entry_path(const char* entry_id, char** out_path) {
|
|
|
|
*out_path = NULL;
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Check that the entry id is valid
|
|
|
|
if (!is_entry_id_valid(entry_id)) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Invalid entry id '%s'.", entry_id);
|
2024-02-17 00:45:44 +01:00
|
|
|
return FAILURE;
|
2024-02-17 00:57:30 +01:00
|
|
|
}
|
2024-02-17 00:45:44 +01:00
|
|
|
|
|
|
|
return format(out_path, "%s/%s", ENTRY_POOL_DIR, entry_id);
|
|
|
|
}
|
|
|
|
|
2024-02-17 12:34:24 +01:00
|
|
|
Result get_entry_disk_path(const char* entry_id, char** out_path) {
|
|
|
|
*out_path = NULL;
|
|
|
|
|
|
|
|
// Get the path of the entry
|
|
|
|
char* entry_path;
|
|
|
|
Result result = get_entry_path(entry_id, &entry_path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Get the path of the disk
|
|
|
|
result = format(out_path, "%s/disk", entry_path);
|
|
|
|
|
|
|
|
// Free the entry path
|
|
|
|
free(entry_path);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result get_entry_type_path(const char* entry_id, char** out_path) {
|
|
|
|
*out_path = NULL;
|
|
|
|
|
|
|
|
// Get the path of the entry
|
|
|
|
char* entry_path;
|
|
|
|
Result result = get_entry_path(entry_id, &entry_path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Get the path of the type file
|
|
|
|
result = format(out_path, "%s/type", entry_path);
|
|
|
|
|
|
|
|
// Free the entry path
|
|
|
|
free(entry_path);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-02-17 00:45:44 +01:00
|
|
|
Result entry_exists(const char* entry_id, bool* out_exists) {
|
|
|
|
*out_exists = false;
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Get the path of the entry
|
2024-02-17 00:45:44 +01:00
|
|
|
char* path;
|
|
|
|
Result result = get_entry_path(entry_id, &path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Check if the entry exists and is a directory
|
2024-02-17 00:45:44 +01:00
|
|
|
struct stat st;
|
|
|
|
bool exists = stat(path, &st) == 0 && S_ISDIR(st.st_mode);
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Free the path
|
2024-02-17 00:45:44 +01:00
|
|
|
free(path);
|
|
|
|
|
2024-02-17 00:57:30 +01:00
|
|
|
// Output the result
|
2024-02-17 00:45:44 +01:00
|
|
|
*out_exists = exists;
|
|
|
|
|
|
|
|
return SUCCESS;
|
2024-02-17 12:34:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Result add_entry(const char* entry_id, EntryType type) {
|
|
|
|
// Check that it does not exist
|
|
|
|
bool exists;
|
|
|
|
Result result = entry_exists(entry_id, &exists);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (exists) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "The entry '%s' already exists.", entry_id);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the path of the entry
|
|
|
|
char* entry_path;
|
|
|
|
result = get_entry_path(entry_id, &entry_path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Create the directory
|
|
|
|
if (mkdir(entry_path, 0755) == -1) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to create the directory '%s' (%s).", entry_path, strerror(errno));
|
|
|
|
|
|
|
|
free(entry_path);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free the entry path
|
|
|
|
free(entry_path);
|
|
|
|
|
|
|
|
// Get the path of the type file
|
|
|
|
char* type_path;
|
|
|
|
result = get_entry_type_path(entry_id, &type_path);
|
|
|
|
if (result != SUCCESS) {
|
|
|
|
remove_entry(entry_id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the type file
|
|
|
|
const char* type_string;
|
|
|
|
switch (type) {
|
|
|
|
case ENTRY_TYPE_ROOT:
|
|
|
|
type_string = ENTRY_TYPE_ROOT_STRING;
|
|
|
|
break;
|
|
|
|
case ENTRY_TYPE_BACKED:
|
|
|
|
type_string = ENTRY_TYPE_BACKED_STRING;
|
|
|
|
break;
|
|
|
|
case ENTRY_TYPE_AUTOMATIC:
|
|
|
|
type_string = ENTRY_TYPE_AUTOMATIC_STRING;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Invalid entry type.");
|
|
|
|
remove_entry(entry_id);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = write_file(type_path, type_string);
|
|
|
|
|
|
|
|
// Free the type path
|
|
|
|
free(type_path);
|
|
|
|
|
|
|
|
if (result != SUCCESS) {
|
|
|
|
remove_entry(entry_id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result add_root_entry(const char* entry_id, uint64_t size) {
|
|
|
|
// Add the entry
|
|
|
|
Result result = add_entry(entry_id, ENTRY_TYPE_ROOT);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Get the path of the disk
|
|
|
|
char* disk_path;
|
|
|
|
result = get_entry_disk_path(entry_id, &disk_path);
|
|
|
|
if (result != SUCCESS) {
|
|
|
|
remove_entry(entry_id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the disk
|
|
|
|
result = create_root_disk(disk_path, size, 0644);
|
|
|
|
|
|
|
|
// Free the disk path
|
|
|
|
free(disk_path);
|
|
|
|
|
|
|
|
if (result != SUCCESS) {
|
|
|
|
remove_entry(entry_id);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-02-17 14:38:52 +01:00
|
|
|
Result add_backed_entry(const char* entry_id, const char* backing_id) {
|
|
|
|
// TODO: Implement
|
|
|
|
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result add_automatic_entry(const char* entry_id) {
|
|
|
|
// TODO: Implement
|
|
|
|
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
2024-02-17 12:34:24 +01:00
|
|
|
Result remove_entry(const char* entry_id) {
|
|
|
|
// Check that it exists
|
|
|
|
bool exists;
|
|
|
|
Result result = entry_exists(entry_id, &exists);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (!exists) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "The entry '%s' does not exist.", entry_id);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the path of the entry
|
|
|
|
char* entry_path;
|
|
|
|
result = get_entry_path(entry_id, &entry_path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
2024-02-17 14:38:52 +01:00
|
|
|
// Remove all the files in the directory
|
|
|
|
DIR* dir = opendir(entry_path);
|
|
|
|
if (dir == NULL) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to open the directory '%s' (%s).", entry_path, strerror(errno));
|
|
|
|
|
|
|
|
free(entry_path);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
// Skip the current and parent directories
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Get the path of the file
|
|
|
|
char* file_path;
|
|
|
|
result = format(&file_path, "%s/%s", entry_path, entry->d_name);
|
|
|
|
if (result != SUCCESS) {
|
|
|
|
closedir(dir);
|
|
|
|
free(entry_path);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the file
|
|
|
|
if (unlink(file_path) == -1) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to remove the file '%s' (%s).", file_path, strerror(errno));
|
|
|
|
|
|
|
|
free(file_path);
|
|
|
|
closedir(dir);
|
|
|
|
free(entry_path);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free the entry file path
|
|
|
|
free(file_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the directory
|
|
|
|
closedir(dir);
|
|
|
|
|
2024-02-17 12:34:24 +01:00
|
|
|
// Remove the directory
|
|
|
|
if (rmdir(entry_path) == -1) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to remove the directory '%s' (%s).", entry_path, strerror(errno));
|
|
|
|
|
|
|
|
free(entry_path);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free the entry path
|
|
|
|
free(entry_path);
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-02-17 12:55:21 +01:00
|
|
|
Result list_entries(char*** out_entries) {
|
|
|
|
*out_entries = malloc(sizeof(char*));
|
|
|
|
if (*out_entries == NULL) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the entries.");
|
|
|
|
return OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*out_entries)[0] = NULL;
|
|
|
|
|
|
|
|
// Open the directory
|
|
|
|
DIR* dir = opendir(ENTRY_POOL_DIR);
|
|
|
|
if (dir == NULL) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to open the directory '%s' (%s).", ENTRY_POOL_DIR, strerror(errno));
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the directory
|
|
|
|
int entry_count = 0;
|
|
|
|
|
|
|
|
struct dirent* entry;
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
// Skip the current and parent directories
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Check that the entry exists
|
|
|
|
bool exists;
|
|
|
|
Result result = entry_exists(entry->d_name, &exists);
|
|
|
|
if (result != SUCCESS || !exists)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Reallocate the entries array
|
|
|
|
// TODO: More efficient reallocation
|
|
|
|
char** new_entries = realloc(*out_entries, (entry_count + 2) * sizeof(char*));
|
|
|
|
if (new_entries == NULL) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the entries.");
|
|
|
|
closedir(dir);
|
|
|
|
|
|
|
|
for (int i = 0; i < entry_count; i++)
|
|
|
|
free((*out_entries)[i]);
|
|
|
|
free(*out_entries);
|
|
|
|
|
|
|
|
return OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_entries = new_entries;
|
|
|
|
|
|
|
|
// Copy the entry name
|
|
|
|
(*out_entries)[entry_count] = strdup(entry->d_name);
|
|
|
|
if ((*out_entries)[entry_count] == NULL) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "Failed to allocate memory for the entry name.");
|
|
|
|
closedir(dir);
|
|
|
|
|
|
|
|
for (int i = 0; i < entry_count; i++)
|
|
|
|
free((*out_entries)[i]);
|
|
|
|
free(*out_entries);
|
|
|
|
|
|
|
|
return OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Null-terminate the array
|
|
|
|
(*out_entries)[entry_count + 1] = NULL;
|
|
|
|
|
|
|
|
entry_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the directory
|
|
|
|
closedir(dir);
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-02-17 13:01:40 +01:00
|
|
|
Result clear_entries(void) {
|
|
|
|
char** entries;
|
|
|
|
Result result = list_entries(&entries);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Remove each entry
|
|
|
|
for (int i = 0; entries[i] != NULL; i++) {
|
|
|
|
result = remove_entry(entries[i]);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
log_message(LOG_LEVEL_WARNING, "Failed to remove the entry '%s'.", entries[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free the entries
|
|
|
|
for (int i = 0; entries[i] != NULL; i++)
|
|
|
|
free(entries[i]);
|
|
|
|
free(entries);
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2024-02-17 12:35:33 +01:00
|
|
|
Result reset_entry(const char* entry_id) {
|
|
|
|
// Check that it exists
|
|
|
|
bool exists;
|
|
|
|
Result result = entry_exists(entry_id, &exists);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (!exists) {
|
|
|
|
log_message(LOG_LEVEL_ERROR, "The entry '%s' does not exist.", entry_id);
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the path of the disk
|
|
|
|
char* disk_path;
|
|
|
|
result = get_entry_disk_path(entry_id, &disk_path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Reset the disk
|
|
|
|
result = reset_disk(disk_path);
|
|
|
|
|
|
|
|
// Free the disk path
|
|
|
|
free(disk_path);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-02-17 12:34:24 +01:00
|
|
|
Result get_entry_info(const char* entry_id, EntryInfo* out_info) {
|
|
|
|
out_info->backing_id = NULL;
|
|
|
|
out_info->type = ENTRY_TYPE_UNKNOWN;
|
|
|
|
|
|
|
|
// Get the path of the type file
|
|
|
|
char* type_path;
|
|
|
|
Result result = get_entry_type_path(entry_id, &type_path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Read the type file
|
|
|
|
char* type;
|
|
|
|
result = read_file(type_path, &type);
|
|
|
|
if (result != SUCCESS) {
|
|
|
|
free(type_path);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free the type path
|
|
|
|
free(type_path);
|
|
|
|
|
|
|
|
// Check the type
|
|
|
|
if (strcmp(type, ENTRY_TYPE_ROOT_STRING) == 0)
|
|
|
|
out_info->type = ENTRY_TYPE_ROOT;
|
|
|
|
else if (strcmp(type, ENTRY_TYPE_BACKED_STRING) == 0)
|
|
|
|
out_info->type = ENTRY_TYPE_BACKED;
|
|
|
|
else if (strcmp(type, ENTRY_TYPE_AUTOMATIC_STRING) == 0)
|
|
|
|
out_info->type = ENTRY_TYPE_AUTOMATIC;
|
|
|
|
|
|
|
|
// Free the type
|
|
|
|
free(type);
|
|
|
|
|
|
|
|
// Get the path of the entry disk
|
|
|
|
char* disk_path;
|
|
|
|
result = get_entry_disk_path(entry_id, &disk_path);
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Get the information about the disk
|
|
|
|
result = get_disk_info(disk_path, &out_info->disk_info);
|
|
|
|
|
|
|
|
// Free the disk path
|
|
|
|
free(disk_path);
|
|
|
|
|
|
|
|
if (result != SUCCESS)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Check if the disk is backed
|
|
|
|
if (out_info->disk_info.backing_file != NULL) {
|
|
|
|
out_info->backing_id = strdup(basename(out_info->disk_info.backing_file));
|
|
|
|
if (out_info->backing_id == NULL) {
|
|
|
|
free_disk_info(&out_info->disk_info);
|
|
|
|
return OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_entry_info(EntryInfo* info) {
|
|
|
|
free(info->backing_id);
|
|
|
|
free_disk_info(&info->disk_info);
|
|
|
|
}
|