#include "entry.h" #include "disk.h" #include "backing.h" #include #include #include #include #include #include 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; }