Files
linuxinstall/src/entry.c

434 lines
11 KiB
C

#include "entry.h"
#include "disk.h"
#include "backing.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
bool IsEntryIdentifierValid(const char* entry_identifier) {
if (entry_identifier == NULL)
return false;
// Check that the identifier is not empty or too long
size_t length = strlen(entry_identifier);
if (length == 0 || length > 64)
return false;
// Check that the identifier does not contain any slashes
for (size_t i = 0; i < length; i++)
if (entry_identifier[i] == '/')
return false;
// Check that the identifier is not "." or ".."
if (strcmp(entry_identifier, ".") == 0 || strcmp(entry_identifier, "..") == 0)
return false;
return true;
}
Status GetEntryPoolPath(char** _entry_pool_path) {
return Format(_entry_pool_path, "/var/lib/sandbox/entries");
}
Status GetEntryPath(const char* entry_identifier, char** _entry_path) {
// Check that the identifier is valid, as it will be used in a path
if (!IsEntryIdentifierValid(entry_identifier)) {
Log(LOG_LEVEL_ERROR, "Invalid entry identifier '%s'.", entry_identifier);
return FAILURE;
}
// Get the entry pool path
char* entry_pool_path = NULL;
Status status = GetEntryPoolPath(&entry_pool_path);
if (status != SUCCESS)
return status;
// Format the entry path
status = Format(_entry_path, "%s/%s", entry_pool_path, entry_identifier);
free(entry_pool_path);
return status;
}
Status GetEntryDiskPath(const char* entry_identifier, char** _entry_disk_path) {
// Get the entry path
char* entry_path = NULL;
Status status = GetEntryPath(entry_identifier, &entry_path);
if (status != SUCCESS)
return status;
// Format the disk path
status = Format(_entry_disk_path, "%s/disk", entry_path);
free(entry_path);
return status;
}
Status DoesEntryExist(const char* entry_identifier, bool* _result) {
// Get the entry path
char* entry_path = NULL;
Status status = GetEntryPath(entry_identifier, &entry_path);
if (status != SUCCESS)
return status;
// Check if the entry exists and is a directory
struct stat st;
*_result = stat(entry_path, &st) == 0 && S_ISDIR(st.st_mode);
free(entry_path);
return SUCCESS;
}
Status DoesEntryDiskExist(const char* entry_identifier, bool* _result) {
// Get the disk path
char* entry_disk_path = NULL;
Status status = GetEntryDiskPath(entry_identifier, &entry_disk_path);
if (status != SUCCESS)
return status;
// Check if the disk exists and is a regular file
struct stat st;
*_result = stat(entry_disk_path, &st) == 0 && S_ISREG(st.st_mode);
free(entry_disk_path);
return SUCCESS;
}
Status AddEntry(const char* entry_identifier) {
// Check if the entry already exists
bool exists;
Status status = DoesEntryExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (exists) {
Log(LOG_LEVEL_ERROR, "The entry '%s' already exists.", entry_identifier);
return FAILURE;
}
// Create the entry directory
char* entry_path = NULL;
status = GetEntryPath(entry_identifier, &entry_path);
if (status != SUCCESS)
return status;
if (mkdir(entry_path, 0755) != 0) {
Log(LOG_LEVEL_ERROR, "Failed to create the entry directory '%s' (%s).", entry_path, strerror(errno));
free(entry_path);
return FAILURE;
}
free(entry_path);
return SUCCESS;
}
Status RemoveEntry(const char* entry_identifier) {
// Check if the entry exists
bool exists;
Status status = DoesEntryExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (!exists) {
Log(LOG_LEVEL_ERROR, "The entry '%s' does not exist.", entry_identifier);
return FAILURE;
}
// Check if the disk exists, and remove it if it does
bool disk_exists;
status = DoesEntryDiskExist(entry_identifier, &disk_exists);
if (status != SUCCESS)
return status;
if (disk_exists) {
status = RemoveEntryDisk(entry_identifier);
if (status != SUCCESS)
return status;
}
// Remove the entry directory
char* entry_path = NULL;
status = GetEntryPath(entry_identifier, &entry_path);
if (status != SUCCESS)
return status;
if (rmdir(entry_path) != 0) {
Log(LOG_LEVEL_ERROR, "Failed to remove the entry directory '%s' (%s).", entry_path, strerror(errno));
free(entry_path);
return FAILURE;
}
free(entry_path);
return SUCCESS;
}
Status ListEntries(char*** _entries) {
*_entries = NULL;
// Open the entry pool directory
char* entry_pool_path = NULL;
Status status = GetEntryPoolPath(&entry_pool_path);
if (status != SUCCESS)
return status;
DIR* dir = opendir(entry_pool_path);
if (dir == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to open the entry pool directory '%s' (%s).", entry_pool_path, strerror(errno));
free(entry_pool_path);
return FAILURE;
}
free(entry_pool_path);
// Allocate memory for at least one entry (the NULL terminator)
*_entries = malloc(sizeof(char*));
if (*_entries == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the entries list (%s).", strerror(errno));
closedir(dir);
return FAILURE;
}
(*_entries)[0] = NULL;
// Read the entries from the directory
size_t count = 0;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
// Check if the entry exists (this checks for validity)
bool exists;
Status status = DoesEntryExist(entry->d_name, &exists);
if (status != SUCCESS || !exists)
continue;
// Allocate memory for the new entry list
char** new_entries = realloc(*_entries, (count + 2) * sizeof(char*)); // +2 for the new entry and the NULL terminator
if (new_entries == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to reallocate memory for the entries list (%s).", strerror(errno));
closedir(dir);
for (size_t i = 0; i < count; i++)
free((*_entries)[i]);
free(*_entries);
return FAILURE;
}
*_entries = new_entries;
// Duplicate the entry name and add it to the list
(*_entries)[count] = strdup(entry->d_name);
if ((*_entries)[count] == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to duplicate the entry name (%s).", strerror(errno));
closedir(dir);
for (size_t i = 0; i < count; i++)
free((*_entries)[i]);
free(*_entries);
return FAILURE;
}
// Add the NULL terminator
(*_entries)[count + 1] = NULL;
count++;
}
closedir(dir);
return SUCCESS;
}
Status ClearEntries() {
// Get the entries
char** entries = NULL;
Status status = ListEntries(&entries);
if (status != SUCCESS)
return status;
// Remove the entries
for (size_t i = 0; entries[i] != NULL; i++) {
status = RemoveEntry(entries[i]);
if (status != SUCCESS)
Log(LOG_LEVEL_WARNING, "Failed to remove the entry '%s'. Non-critical error.", entries[i]);
}
// Clean up
for (size_t i = 0; entries[i] != NULL; i++)
free(entries[i]);
free(entries);
return SUCCESS;
}
Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size) {
// Check if the disk already exists
bool exists;
Status status = DoesEntryDiskExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (exists) {
Log(LOG_LEVEL_ERROR, "The disk for the entry '%s' already exists.", entry_identifier);
return FAILURE;
}
// Get the disk path
char* entry_disk_path = NULL;
status = GetEntryDiskPath(entry_identifier, &entry_disk_path);
if (status != SUCCESS)
return status;
// Create the disk
status = CreateRootDisk(entry_disk_path, disk_size);
free(entry_disk_path);
return status;
}
Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_identifier) {
// Check if the disk already exists
bool exists;
Status status = DoesEntryDiskExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (exists) {
Log(LOG_LEVEL_ERROR, "The disk for the entry '%s' already exists.", entry_identifier);
return FAILURE;
}
// Get the backing disk path
char* backing_disk_path = NULL;
status = GetBackingDiskPath(backing_identifier, &backing_disk_path);
if (status != SUCCESS)
return status;
// Get the disk path
char* entry_disk_path = NULL;
status = GetEntryDiskPath(entry_identifier, &entry_disk_path);
if (status != SUCCESS) {
free(backing_disk_path);
return status;
}
// Create the disk
status = CreateBackedDisk(entry_disk_path, backing_disk_path);
free(backing_disk_path);
free(entry_disk_path);
return status;
}
Status RemoveEntryDisk(const char* entry_identifier) {
// Check if the disk exists
bool exists;
Status status = DoesEntryDiskExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (!exists) {
Log(LOG_LEVEL_ERROR, "The disk for the entry '%s' does not exist.", entry_identifier);
return FAILURE;
}
// Get the disk path
char* entry_disk_path = NULL;
status = GetEntryDiskPath(entry_identifier, &entry_disk_path);
if (status != SUCCESS)
return status;
// Remove the disk
if (unlink(entry_disk_path) != 0) {
Log(LOG_LEVEL_ERROR, "Failed to remove the disk for the entry '%s' (%s).", entry_identifier, strerror(errno));
free(entry_disk_path);
return FAILURE;
}
free(entry_disk_path);
return SUCCESS;
}
Status ResetEntryDisk(const char* entry_identifier, const char* backing_identifier) {
// Check if the disk exists
bool exists;
Status status = DoesEntryDiskExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (!exists) {
Log(LOG_LEVEL_ERROR, "The disk for the entry '%s' does not exist.", entry_identifier);
return FAILURE;
}
// Get the backing disk path
char* backing_disk_path = NULL;
if (backing_identifier != NULL) {
status = GetBackingDiskPath(backing_identifier, &backing_disk_path);
if (status != SUCCESS)
return status;
}
// Get the disk path
char* entry_disk_path = NULL;
status = GetEntryDiskPath(entry_identifier, &entry_disk_path);
if (status != SUCCESS) {
free(backing_disk_path);
return status;
}
// Get the disk info
DiskInfo disk_info;
status = GetDiskInfo(entry_disk_path, &disk_info);
if (status != SUCCESS) {
free(backing_disk_path);
free(entry_disk_path);
return status;
}
// If no backing disk is specified, use the current backing disk
if (backing_disk_path == NULL)
backing_disk_path = disk_info.backing_file;
// Reset the disk
if (backing_disk_path != NULL)
status = CreateBackedDisk(entry_disk_path, backing_disk_path);
else
status = CreateRootDisk(entry_disk_path, disk_info.size);
FreeDiskInfo(&disk_info);
free(backing_disk_path);
free(entry_disk_path);
return status;
}
Status TrimEntryDisk(const char* entry_identifier) {
// Check if the disk exists
bool exists;
Status status = DoesEntryDiskExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (!exists) {
Log(LOG_LEVEL_ERROR, "The disk for the entry '%s' does not exist.", entry_identifier);
return FAILURE;
}
// Get the disk path
char* entry_disk_path = NULL;
status = GetEntryDiskPath(entry_identifier, &entry_disk_path);
if (status != SUCCESS)
return status;
// Trim the disk
status = TrimDisk(entry_disk_path);
free(entry_disk_path);
return status;
}