434 lines
11 KiB
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;
|
|
}
|