diff --git a/src/backing.c b/src/backing.c deleted file mode 100644 index 71a58ae..0000000 --- a/src/backing.c +++ /dev/null @@ -1,332 +0,0 @@ -#include "backing.h" - -#include "disk.h" -#include "entry.h" - -#include -#include -#include -#include -#include -#include -#include - -bool IsBackingIdentifierValid(const char* backing_identifier) { - if (backing_identifier == NULL) - return false; - - // Check that the identifier is not empty or too long - size_t length = strlen(backing_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 (backing_identifier[i] == '/') - return false; - - // Check that the identifier is not "." or ".." - if (strcmp(backing_identifier, ".") == 0 || strcmp(backing_identifier, "..") == 0) - return false; - - // Check that the identifier starts with a number - if (backing_identifier[0] < '0' || backing_identifier[0] > '9') - return false; - - return true; -} - -int GetBackingIndex(const char* backing_identifier) { - // Check that the identifier is valid - if (!IsBackingIdentifierValid(backing_identifier)) - return -1; - - // Get the index - return atoi(backing_identifier); -} - -Status GetBackingPoolPath(char** _backing_pool_path) { - return Format(_backing_pool_path, "/var/lib/sandbox/backings"); -} - -Status GetBackingPath(const char* backing_identifier, char** _backing_path) { - // Check that the identifier is valid, as it will be used in a path - if (!IsBackingIdentifierValid(backing_identifier)) { - Log(LOG_LEVEL_ERROR, "Invalid backing identifier '%s'.", backing_identifier); - return FAILURE; - } - - // Get the backing pool path - char* backing_pool_path = NULL; - Status status = GetBackingPoolPath(&backing_pool_path); - if (status != SUCCESS) - return status; - - // Format the backing path - status = Format(_backing_path, "%s/%s", backing_pool_path, backing_identifier); - free(backing_pool_path); - - return status; -} - -Status DoesBackingExist(const char* backing_identifier, bool* _result) { - // Get the backing path - char* backing_path = NULL; - Status status = GetBackingPath(backing_identifier, &backing_path); - if (status != SUCCESS) - return status; - - // Check if the backing exists and is a regular file - struct stat st; - *_result = stat(backing_path, &st) == 0 && S_ISREG(st.st_mode); - free(backing_path); - - return SUCCESS; -} - -Status AddBacking(const char* backing_identifier, const char* entry_identifier) { - // Check that the backing does not already exist - bool exists; - Status status = DoesBackingExist(backing_identifier, &exists); - if (status != SUCCESS) - return status; - - if (exists) { - Log(LOG_LEVEL_ERROR, "The backing '%s' already exists.", backing_identifier); - return FAILURE; - } - - // Check that the entry disk exists - status = DoesEntryDiskExist(entry_identifier, &exists); - if (status != SUCCESS) - return status; - - if (!exists) { - Log(LOG_LEVEL_ERROR, "The entry disk '%s' does not exist. Cannot create backing from it.", entry_identifier); - return FAILURE; - } - - // Get information about the entry disk - DiskInfo entry_info; - status = GetEntryDiskInfo(entry_identifier, &entry_info); - if (status != SUCCESS) - return status; - - // Check that the backing used by the entry actually exists (broken backing chain) - if (entry_info.backing_identifier != NULL) { - status = DoesBackingExist(entry_info.backing_identifier, &exists); - if (status != SUCCESS) { - FreeDiskInfo(&entry_info); - return status; - } - - if (!exists) { - Log(LOG_LEVEL_ERROR, "The backing '%s' of the entry disk '%s' does not exist.", entry_info.backing_identifier, entry_identifier); - - FreeDiskInfo(&entry_info); - return FAILURE; - } - } - - // Copy the entry disk to the backing disk - char* entry_disk_path = NULL; - status = GetEntryDiskPath(entry_identifier, &entry_disk_path); - if (status != SUCCESS) { - FreeDiskInfo(&entry_info); - return status; - } - - char* backing_path = NULL; - status = GetBackingPath(backing_identifier, &backing_path); - if (status != SUCCESS) { - free(entry_disk_path); - FreeDiskInfo(&entry_info); - return status; - } - - status = CopyFile(entry_disk_path, backing_path); - if (status != SUCCESS) { - free(entry_disk_path); - free(backing_path); - FreeDiskInfo(&entry_info); - return status; - } - - free(entry_disk_path); - - // If the entry disk has a backing, rebase the backing to the new backing - if (entry_info.backing_identifier != NULL) { - status = RebaseDisk(backing_path, entry_info.backing_identifier); - if (status != SUCCESS) { - RemoveBacking(backing_identifier); - - free(backing_path); - FreeDiskInfo(&entry_info); - return status; - } - } - - free(backing_path); - FreeDiskInfo(&entry_info); - return SUCCESS; -} - -Status RemoveBacking(const char* backing_identifier) { - // Check that the backing exists - bool exists; - Status status = DoesBackingExist(backing_identifier, &exists); - if (status != SUCCESS) - return status; - - if (!exists) { - Log(LOG_LEVEL_ERROR, "The backing '%s' does not exist.", backing_identifier); - return FAILURE; - } - - // Get the backing path - char* backing_path = NULL; - status = GetBackingPath(backing_identifier, &backing_path); - if (status != SUCCESS) - return status; - - // Remove the backing - if (unlink(backing_path) != 0) { - Log(LOG_LEVEL_ERROR, "Failed to remove the backing '%s' (%s).", backing_identifier, strerror(errno)); - free(backing_path); - return FAILURE; - } - free(backing_path); - - // TODO: Remove all the backings that use this backing as a base - - return SUCCESS; -} - -Status ListBackings(char*** _backings) { - *_backings = NULL; - - // Open the backing pool directory - char* backing_pool_path = NULL; - Status status = GetBackingPoolPath(&backing_pool_path); - if (status != SUCCESS) - return status; - - DIR* dir = opendir(backing_pool_path); - if (dir == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to open the backing pool directory %s (%s).", backing_pool_path, strerror(errno)); - free(backing_pool_path); - return FAILURE; - } - free(backing_pool_path); - - // Allocate memory for at least one backing (the NULL terminator) - *_backings = malloc(sizeof(char*)); - if (*_backings == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the backings list (%s).", strerror(errno)); - closedir(dir); - return FAILURE; - } - (*_backings)[0] = NULL; - - // Read the files in the backing pool directory - size_t count = 0; - struct dirent* file; - while ((file = readdir(dir)) != NULL) { - if (strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0) - continue; - - // Check if the backing exists (this checks for validity) - bool exists; - Status status = DoesBackingExist(file->d_name, &exists); - if (status != SUCCESS || !exists) - continue; - - // Allocate memory for the new backing list - char** new_backings = realloc(*_backings, (count + 2) * sizeof(char*)); // +2 for the new entry and the NULL terminator - if (new_backings == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to reallocate memory for the backing list (%s).", strerror(errno)); - closedir(dir); - - for (size_t i = 0; i < count; i++) - free((*_backings)[i]); - free(*_backings); - - return FAILURE; - } - *_backings = new_backings; - - // Duplicate the file name and add it to the list - (*_backings)[count] = strdup(file->d_name); - if ((*_backings)[count] == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to duplicate the backing name (%s).", strerror(errno)); - closedir(dir); - - for (size_t i = 0; i < count; i++) - free((*_backings)[i]); - free(*_backings); - - return FAILURE; - } - - // Add the NULL terminator - (*_backings)[count + 1] = NULL; - count++; - } - - closedir(dir); - - return SUCCESS; -} - -Status GetLatestBacking(char** _backing_identifier) { - // List the backings - char** backings = NULL; - Status status = ListBackings(&backings); - if (status != SUCCESS) - return status; - - // Find the latest backing - int latest_identifier_index = -1; - int latest_index = -1; - for (int i = 0; backings[i] != NULL; i++) { - int index = GetBackingIndex(backings[i]); - if (index > latest_index) { - latest_identifier_index = i; - latest_index = index; - } - } - - // Return the latest backing - if (latest_identifier_index == -1) { - *_backing_identifier = NULL; - } else { - *_backing_identifier = strdup(backings[latest_identifier_index]); - if (*_backing_identifier == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to duplicate the backing identifier (%s).", strerror(errno)); - status = FAILURE; - } - } - - // Free the backings list - for (int i = 0; backings[i] != NULL; i++) - free(backings[i]); - free(backings); - - return status; -} - -Status GetBackingDiskInfo(const char* backing_identifier, DiskInfo* _info) { - *_info = (DiskInfo){0}; - - // Get the backing path - char* backing_path = NULL; - Status status = GetBackingPath(backing_identifier, &backing_path); - if (status != SUCCESS) - return status; - - // Get the disk info - status = GetDiskInfo(backing_path, _info); - free(backing_path); - - return status; -} diff --git a/src/backing.h b/src/backing.h deleted file mode 100644 index 9a3c819..0000000 --- a/src/backing.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "utils.h" -#include "disk.h" - -bool IsBackingIdentifierValid(const char* backing_identifier); -int GetBackingIndex(const char* backing_identifier); - -Status GetBackingPoolPath(char** _backing_pool_path); -Status GetBackingPath(const char* backing_identifier, char** _backing_path); - -Status DoesBackingExist(const char* backing_identifier, bool* _result); - -Status AddBacking(const char* backing_identifier, const char* entry_identifier); -Status RemoveBacking(const char* backing_identifier); - -Status ListBackings(char*** _backings); -Status GetLatestBacking(char** _backing_identifier); - -Status GetBackingDiskInfo(const char* backing_identifier, DiskInfo* _info); \ No newline at end of file diff --git a/src/disk.c b/src/disk.c index f29c224..ca042a1 100644 --- a/src/disk.c +++ b/src/disk.c @@ -1,185 +1,219 @@ #include "disk.h" +#include #include #include #include -#include #include -#include #include +#include +#include +#include +#include #include -Status CreateRootDisk(const char* path, uint64_t disk_size) { +Result CreateRootDisk(const char* disk_path, uint64_t size) { // Convert the size to a string char* size_str; - Status status = Format(&size_str, "%lu", disk_size); - if (status != SUCCESS) - return status; + if (Format(&size_str, "%lu", size) == FAILURE) + return FAILURE; - // Create the disk + // Create the root disk int exit_code; - // char* stdout; + // char* stdoutb; No need to capture stdout char* stderrb; - status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", path, size_str, NULL); + Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "create", "-f", "qcow2", disk_path, size_str, NULL); + // Free the size string free(size_str); - // Check if the disk was created successfully - if (status != SUCCESS) - return status; + // If the execution failed, return the error + if (result != SUCCESS) + return result; + // If the exit code is non-zero, return the error if (exit_code != 0) { - if (stderrb != NULL) { - // Remove newlines from the stderr buffer - for (int i = 0; stderrb[i] != '\0'; i++) - if (stderrb[i] == '\n') - stderrb[i] = ' '; + // If the error message is empty, return a generic error + if (stderrb == NULL) + Log(LOG_LEVEL_ERROR, "Failed to create root disk '%s'.", disk_path); + else { + // Remove all newlines from the error message + for (char* c = stderrb; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; - Log(LOG_LEVEL_ERROR, "Failed to create disk at '%s' (%s).", path, stderrb); - free(stderrb); - } else - Log(LOG_LEVEL_ERROR, "Failed to create disk"); - - return FAILURE; - } - - return SUCCESS; -} - -Status CreateBackedDisk(const char* path, const char* backing_file) { - // Create the disk - int exit_code; - // char* stdout; - char* stderrb; - - Status status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_file, path, NULL); - - // Check if the disk was created successfully - if (status != SUCCESS) - return status; - - if (exit_code != 0) { - if (stderrb != NULL) { - // Remove newlines from the stderr buffer - for (int i = 0; stderrb[i] != '\0'; i++) - if (stderrb[i] == '\n') - stderrb[i] = ' '; - - Log(LOG_LEVEL_ERROR, "Failed to create disk at '%s' (%s).", path, stderrb); - free(stderrb); - } else - Log(LOG_LEVEL_ERROR, "Failed to create disk"); - - return FAILURE; - } - - return SUCCESS; -} - -Status TrimDisk(const char* path) { - char* tmp_path = NULL; - Status status = Format(&tmp_path, "%s.tmp", path); - if (status != SUCCESS) - return status; - - DiskInfo info; - status = GetDiskInfo(path, &info); - if (status != SUCCESS) { - free(tmp_path); - return status; - } - - // Create the temporary disk - int exit_code; - // char* stdout; - char* stderrb; - - if (info.backing_file != NULL) { - char* backing_file_opt = NULL; - status = Format(&backing_file_opt, "backing_file=%s", info.backing_file); - if (status != SUCCESS) { - free(tmp_path); - FreeDiskInfo(&info); - return status; + Log(LOG_LEVEL_ERROR, "Failed to create root disk '%s' (%s).", disk_path, stderrb); } - status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_file_opt, path, tmp_path, NULL); - - free(backing_file_opt); - } else - status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, tmp_path, NULL); - - FreeDiskInfo(&info); - - // Check if the disk was created successfully - if (status != SUCCESS) { - free(tmp_path); - return status; - } - - if (exit_code != 0) { - if (stderrb != NULL) { - // Remove newlines from the stderr buffer - for (int i = 0; stderrb[i] != '\0'; i++) - if (stderrb[i] == '\n') - stderrb[i] = ' '; - - Log(LOG_LEVEL_ERROR, "Failed to create temporary disk at '%s' (%s).", tmp_path, stderrb); - free(stderrb); - } else - Log(LOG_LEVEL_ERROR, "Failed to create temporary disk"); - - free(tmp_path); + // Free the error message + free(stderrb); return FAILURE; } - // Try to move the temporary disk to the original path - if (rename(tmp_path, path) != 0) { - Log(LOG_LEVEL_ERROR, "Failed to move the temporary disk to the original path '%s' (%s).", path, strerror(errno)); - - unlink(tmp_path); - - free(tmp_path); - return FAILURE; - } - - free(tmp_path); - + // As there can still be an error message for a successful exit code, we need to free it + free(stderrb); return SUCCESS; } -Status RebaseDisk(const char* path, const char* backing_file) { - // Create the disk +Result CreateBackedDisk(const char* disk_path, const char* backing_disk_path) { + // Create the backed disk int exit_code; - // char* stdout; + // char* stdoutb; No need to capture stdout char* stderrb; - Status status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "rebase", "-f", "qcow2", "-F", "qcow2", "-u", "-b", backing_file, path, NULL); + Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_disk_path, disk_path, NULL); - // Check if the disk was created successfully - if (status != SUCCESS) - return status; + // If the execution failed, return the error + if (result != SUCCESS) + return result; + // If the exit code is non-zero, return the error if (exit_code != 0) { - if (stderrb != NULL) { - // Remove newlines from the stderr buffer - for (int i = 0; stderrb[i] != '\0'; i++) - if (stderrb[i] == '\n') - stderrb[i] = ' '; + // If the error message is empty, return a generic error + if (stderrb == NULL) + Log(LOG_LEVEL_ERROR, "Failed to create backed disk '%s' with backing disk '%s'.", disk_path, backing_disk_path); + else { + // Remove all newlines from the error message + for (char* c = stderrb; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; - Log(LOG_LEVEL_ERROR, "Failed to rebase disk at '%s' (%s).", path, stderrb); - free(stderrb); - } else - Log(LOG_LEVEL_ERROR, "Failed to rebase disk"); + Log(LOG_LEVEL_ERROR, "Failed to create backed disk '%s' with backing disk '%s' (%s).", disk_path, backing_disk_path, stderrb); + } + // Free the error message + free(stderrb); return FAILURE; } + // As there can still be an error message for a successful exit code, we need to free it + free(stderrb); return SUCCESS; } -Status GetDiskInfo(const char* path, DiskInfo* _info) { +Result TrimDisk(const char* disk_path) { + // Get a temporary file path to store the trimmed disk + char* trimmed_disk_path; + if (Format(&trimmed_disk_path, "%s.trimmed", disk_path) == FAILURE) + return FAILURE; + + // Get info about the disk + DiskInfo info; + if (GetDiskInfo(disk_path, &info) == FAILURE) { + free(trimmed_disk_path); + return FAILURE; + } + + // Trim the disk + int exit_code; + // char* stdoutb; No need to capture stdout + char* stderrb; + + // If the disk is not backed, we can just use the "convert" command to trim it + Result result; + + if (info.backing_file_path == NULL) + result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", disk_path, trimmed_disk_path, NULL); + else { + // We need to get the option to specify the backing file + char* backing_option; + if (Format(&backing_option, "backing_file=%s", info.backing_file_path) == FAILURE) { + free(trimmed_disk_path); + FreeDiskInfo(&info); + return FAILURE; + } + + // Trim the disk using the "convert" command + result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_option, disk_path, trimmed_disk_path, NULL); + + // Free the backing option + free(backing_option); + } + + // Free the disk info + FreeDiskInfo(&info); + + // If the execution failed, return the error + if (result != SUCCESS) { + free(trimmed_disk_path); + return result; + } + + // If the exit code is non-zero, return the error + if (exit_code != 0) { + // If the error message is empty, return a generic error + if (stderrb == NULL) + Log(LOG_LEVEL_ERROR, "Failed to trim disk '%s'.", disk_path); + else { + // Remove all newlines from the error message + for (char* c = stderrb; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; + + Log(LOG_LEVEL_ERROR, "Failed to trim disk '%s' (%s).", disk_path, stderrb); + } + + // Free the error message + free(stderrb); + free(trimmed_disk_path); + return FAILURE; + } + + // As there can still be an error message for a successful exit code, we need to free it + free(stderrb); + + // Move the trimmed disk to the original path + if (rename(trimmed_disk_path, disk_path) != 0) { + Log(LOG_LEVEL_ERROR, "Failed to move trimmed disk '%s' to original path '%s' (%s).", trimmed_disk_path, disk_path, strerror(errno)); + unlink(trimmed_disk_path); + free(trimmed_disk_path); + return FAILURE; + } + + // Free the temporary file path + free(trimmed_disk_path); + return SUCCESS; +} + +Result RebackDisk(const char* disk_path, const char* backing_disk_path) { + // Reback the disk + int exit_code; + // char* stdoutb; No need to capture stdout + char* stderrb; + + Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "rebase", "-f", "qcow2", "-F", "qcow2", "-u", "-b", backing_disk_path, disk_path, NULL); + + // If the execution failed, return the error + if (result != SUCCESS) + return result; + + // If the exit code is non-zero, return the error + if (exit_code != 0) { + // If the error message is empty, return a generic error + if (stderrb == NULL) + Log(LOG_LEVEL_ERROR, "Failed to reback disk '%s' with backing disk '%s'.", disk_path, backing_disk_path); + else { + // Remove all newlines from the error message + for (char* c = stderrb; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; + + Log(LOG_LEVEL_ERROR, "Failed to reback disk '%s' with backing disk '%s' (%s).", disk_path, backing_disk_path, stderrb); + } + + // Free the error message + free(stderrb); + return FAILURE; + } + + // As there can still be an error message for a successful exit code, we need to free it + free(stderrb); + return SUCCESS; +} + +Result GetDiskInfo(const char* disk_path, DiskInfo* _info) { + // Initialize the disk info *_info = (DiskInfo){0}; // Get the disk info @@ -187,66 +221,116 @@ Status GetDiskInfo(const char* path, DiskInfo* _info) { char* stdoutb; char* stderrb; - Status status = RunExecutable(&exit_code, &stdoutb, &stderrb, "/usr/bin/qemu-img", "info", "--output", "json", path, NULL); - if (status != SUCCESS) - return status; + Result result = RunExecutable(&exit_code, &stdoutb, &stderrb, "qemu-img", "info", "--output", "json", disk_path, NULL); + // If the execution failed, return the error + if (result != SUCCESS) + return result; + + // If the exit code is non-zero, return the error if (exit_code != 0) { - if (stderrb != NULL) { - // Remove newlines from the stderr buffer - for (int i = 0; stderrb[i] != '\0'; i++) - if (stderrb[i] == '\n') - stderrb[i] = ' '; + // If the error message is empty, return a generic error + if (stderrb == NULL) + Log(LOG_LEVEL_ERROR, "Failed to get info for disk '%s'.", disk_path); + else { + // Remove all newlines from the error message + for (char* c = stderrb; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; - Log(LOG_LEVEL_ERROR, "Failed to get disk info at '%s' (%s).", path, stderrb); - free(stderrb); - } else - Log(LOG_LEVEL_ERROR, "Failed to get disk info"); + Log(LOG_LEVEL_ERROR, "Failed to get info for disk '%s' (%s).", disk_path, stderrb); + } + // Free the error message free(stdoutb); + free(stderrb); return FAILURE; } + + // No need for the error message anymore free(stderrb); // Parse the JSON output json_object* root = json_tokener_parse(stdoutb); - free(stdoutb); - if (root == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to parse the JSON output"); + Log(LOG_LEVEL_ERROR, "Failed to parse JSON output for disk '%s'.", disk_path); + free(stdoutb); return FAILURE; } - json_object* virtual_size = NULL; - json_object* actual_size = NULL; - json_object* backing_file = NULL; + // No need for the JSON string anymore + free(stdoutb); - json_object_object_get_ex(root, "virtual-size", &virtual_size); - json_object_object_get_ex(root, "actual-size", &actual_size); - json_object_object_get_ex(root, "backing-filename", &backing_file); + // Get the size of the disk + json_object* size_obj; + if (!json_object_object_get_ex(root, "virtual-size", &size_obj)) { + Log(LOG_LEVEL_ERROR, "Failed to get virtual size for disk '%s'.", disk_path); + json_object_put(root); + FreeDiskInfo(_info); + return FAILURE; + } - if (virtual_size != NULL) - _info->size = json_object_get_int64(virtual_size); - if (actual_size != NULL) - _info->allocated = json_object_get_int64(actual_size); - if (backing_file != NULL) - _info->backing_file = strdup(json_object_get_string(backing_file)); + errno = 0; + _info->size = json_object_get_int64(size_obj); + if (errno != 0) { + Log(LOG_LEVEL_ERROR, "Failed to parse virtual size for disk '%s'.", disk_path); + json_object_put(root); + FreeDiskInfo(_info); + return FAILURE; + } - json_object_put(root); + // Get the allocated size of the disk + json_object* allocation_obj; + if (!json_object_object_get_ex(root, "actual-size", &allocation_obj)) { + Log(LOG_LEVEL_ERROR, "Failed to get actual size for disk '%s'.", disk_path); + json_object_put(root); + FreeDiskInfo(_info); + return FAILURE; + } - if (_info->backing_file != NULL) { - _info->backing_identifier = strdup(basename(_info->backing_file)); - if (_info->backing_identifier == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to duplicate the backing identifier (%s).", strerror(errno)); + errno = 0; + _info->allocated = json_object_get_int64(allocation_obj); + if (errno != 0) { + Log(LOG_LEVEL_ERROR, "Failed to parse actual size for disk '%s'.", disk_path); + json_object_put(root); + FreeDiskInfo(_info); + return FAILURE; + } + + // Get the backing file of the disk + json_object* backing_file_obj; + if (!json_object_object_get_ex(root, "backing-filename", &backing_file_obj)) + _info->backing_file_path = NULL; + else { + const char* backing_file_path = json_object_get_string(backing_file_obj); + if (backing_file_path == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to parse backing file for disk '%s'.", disk_path); + json_object_put(root); + FreeDiskInfo(_info); + return FAILURE; + } + + if (Duplicate(backing_file_path, &_info->backing_file_path) == FAILURE) { + json_object_put(root); + FreeDiskInfo(_info); + return FAILURE; + } + + // Get the backing identifier of the disk (basename) + if (Duplicate(basename(_info->backing_file_path), &_info->backing_identifier) == FAILURE) { + json_object_put(root); FreeDiskInfo(_info); return FAILURE; } } + // Free the JSON object + json_object_put(root); return SUCCESS; } -void FreeDiskInfo(DiskInfo* info) { - free(info->backing_file); +Result FreeDiskInfo(DiskInfo* info) { + free(info->backing_file_path); free(info->backing_identifier); -} \ No newline at end of file + return SUCCESS; +} diff --git a/src/disk.h b/src/disk.h index 80c3572..3bb71e8 100644 --- a/src/disk.h +++ b/src/disk.h @@ -5,15 +5,14 @@ typedef struct { uint64_t size; uint64_t allocated; - char* backing_file; + char* backing_file_path; char* backing_identifier; } DiskInfo; -Status CreateRootDisk(const char* path, uint64_t disk_size); -Status CreateBackedDisk(const char* path, const char* backing_file); +Result CreateRootDisk(const char* disk_path, uint64_t size); +Result CreateBackedDisk(const char* disk_path, const char* backing_disk_path); +Result TrimDisk(const char* disk_path); +Result RebackDisk(const char* disk_path, const char* backing_disk_path); -Status TrimDisk(const char* path); -Status RebaseDisk(const char* path, const char* backing_file); - -Status GetDiskInfo(const char* path, DiskInfo* _info); -void FreeDiskInfo(DiskInfo* info); +Result GetDiskInfo(const char* disk_path, DiskInfo* _info); +Result FreeDiskInfo(DiskInfo* info); \ No newline at end of file diff --git a/src/entry.c b/src/entry.c deleted file mode 100644 index c32d1af..0000000 --- a/src/entry.c +++ /dev/null @@ -1,507 +0,0 @@ -#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 files in the entry pool directory - size_t count = 0; - struct dirent* file; - while ((file = readdir(dir)) != NULL) { - if (strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0) - continue; - - // Check if the entry exists (this checks for validity) - bool exists; - Status status = DoesEntryExist(file->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 entry 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(file->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 that 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 already exists - 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 that 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 that the backing exists - status = DoesBackingExist(backing_identifier, &exists); - if (status != SUCCESS) - return status; - - if (!exists) { - Log(LOG_LEVEL_ERROR, "The backing '%s' does not exist.", backing_identifier); - return FAILURE; - } - - // Check if the disk already exists - 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 = GetBackingPath(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 that 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 - 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 = GetBackingPath(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 = strdup(disk_info.backing_file); - if (backing_disk_path == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the backing disk path (%s).", strerror(errno)); - - FreeDiskInfo(&disk_info); - free(entry_disk_path); - return FAILURE; - } - } - - // 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 entry 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; - } - - // Check if the disk exists - 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; -} - -Status GetEntryDiskInfo(const char* entry_identifier, DiskInfo* _info) { - *_info = (DiskInfo){0}; - - // Get the disk path - char* entry_disk_path = NULL; - Status status = GetEntryDiskPath(entry_identifier, &entry_disk_path); - if (status != SUCCESS) - return status; - - // Get the disk info - status = GetDiskInfo(entry_disk_path, _info); - free(entry_disk_path); - - return status; -} diff --git a/src/entry.h b/src/entry.h deleted file mode 100644 index f686a6c..0000000 --- a/src/entry.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "utils.h" -#include "disk.h" - -bool IsEntryIdentifierValid(const char* entry_identifier); - -Status GetEntryPoolPath(char** _entry_pool_path); -Status GetEntryPath(const char* entry_identifier, char** _entry_path); -Status GetEntryDiskPath(const char* entry_identifier, char** _entry_disk_path); - -Status DoesEntryExist(const char* entry_identifier, bool* _result); -Status DoesEntryDiskExist(const char* entry_identifier, bool* _result); - -Status AddEntry(const char* entry_identifier); -Status RemoveEntry(const char* entry_identifier); - -Status ListEntries(char*** _entries); -Status ClearEntries(); - -Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size); -Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_identifier); -Status RemoveEntryDisk(const char* entry_identifier); -Status ResetEntryDisk(const char* entry_identifier, const char* backing_identifier); -Status TrimEntryDisk(const char* entry_identifier); - -Status GetEntryDiskInfo(const char* entry_identifier, DiskInfo* _info); diff --git a/src/image.c b/src/image.c new file mode 100644 index 0000000..108dd36 --- /dev/null +++ b/src/image.c @@ -0,0 +1,415 @@ +#include "image.h" + +#include "disk.h" +#include "template.h" + +#include +#include +#include +#include +#include +#include + +Result IsImageIdentifierValid(const char* image_id, bool* _valid) { + *_valid = false; + + // Check that the image identifier is not NULL + if (image_id == NULL) + return SUCCESS; + + // Check that the image identifier is not empty or too long + size_t length = strlen(image_id); + if (length == 0 || length > MAX_IMAGE_ID_LENGTH) + return SUCCESS; + + // Check that the image identifier contains characters that are allowed as a user name + for (size_t i = 0; i < length; i++) { + char c = image_id[i]; + if (c >= 'a' && c <= 'z') + continue; + if (c >= 'A' && c <= 'Z') + continue; + if (c >= '0' && c <= '9') + continue; + if (c == '_' || c == '-' || c == '.') + continue; + + return SUCCESS; + } + + // Check that the image identifier is not a reserved name + if (strcmp(image_id, ".") == 0 || strcmp(image_id, "..") == 0) + return SUCCESS; + + *_valid = true; + return SUCCESS; +} + +Result GetImagePoolPath(char** _path) { + return Format(_path, "/var/lib/sandbox/images"); +} + +Result GetImageDirectoryPath(const char* image_id, char** _path) { + // Check that the image identifier is valid + if (IsImageIdentifierValid(image_id, NULL) != SUCCESS) + return FAILURE; + + // Get the path to the image pool + char* pool_path; + if (GetImagePoolPath(&pool_path) != SUCCESS) + return FAILURE; + + // Format the image directory path + Result result = Format(_path, "%s/%s", pool_path, image_id); + + free(pool_path); + return result; +} + +Result GetImageDiskPath(const char* image_id, char** _path) { + // Get the path to the image directory + char* directory_path; + if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS) + return FAILURE; + + // Format the image disk path + Result result = Format(_path, "%s/disk", directory_path); + + free(directory_path); + return result; +} + +Result DoesImageDirectoryExist(const char* image_id, bool* _exists) { + // Get the path to the image directory + char* directory_path; + if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS) + return FAILURE; + + // Check if the image directory exists + struct stat st; + *_exists = stat(directory_path, &st) == 0 && S_ISDIR(st.st_mode); + + free(directory_path); + return SUCCESS; +} + +Result DoesImageDiskExist(const char* image_id, bool* _exists) { + // Get the path to the image disk + char* disk_path; + if (GetImageDiskPath(image_id, &disk_path) != SUCCESS) + return FAILURE; + + // Check if the image disk exists + struct stat st; + *_exists = stat(disk_path, &st) == 0 && S_ISREG(st.st_mode); + + free(disk_path); + return SUCCESS; +} + +Result AddImage(const char* image_id) { + // Check if the image directory already exists + bool directory_exists; + if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS) + return FAILURE; + + if (directory_exists) { + Log(LOG_LEVEL_ERROR, "Image '%s' already exists", image_id); + return FAILURE; + } + + // Get the path to the image directory + char* directory_path; + if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS) + return FAILURE; + + // Create the image directory + Result result = mkdir(directory_path, 0755) == 0 ? SUCCESS : FAILURE; + + if (result == FAILURE) + Log(LOG_LEVEL_ERROR, "Failed to create image '%s' (%s)", directory_path, strerror(errno)); + + free(directory_path); + return result; +} + +Result RemoveImage(const char* image_id) { + // Check if the image directory exists + bool directory_exists; + if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS) + return FAILURE; + + if (!directory_exists) { + Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id); + return FAILURE; + } + + // Check if there is a disk for the image and remove it if it exists + bool disk_exists; + if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS) + return FAILURE; + if (disk_exists) + RemoveImageDisk(image_id); + + // Get the path to the image directory + char* directory_path; + if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS) + return FAILURE; + + // Remove the image directory + Result result = rmdir(directory_path) == 0 ? SUCCESS : FAILURE; + + if (result == FAILURE) + Log(LOG_LEVEL_ERROR, "Failed to remove image '%s' (%s)", directory_path, strerror(errno)); + + free(directory_path); + return result; +} + +Result ListImages(char*** _image_ids) { + // Get the path to the image pool + char* pool_path; + if (GetImagePoolPath(&pool_path) != SUCCESS) + return FAILURE; + + // Open the image pool directory + DIR* pool_dir = opendir(pool_path); + if (pool_dir == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to open image pool directory '%s' (%s)", pool_path, strerror(errno)); + free(pool_path); + return FAILURE; + } + free(pool_path); + + // Allocate an initial array, one element is reserved for the NULL terminator + *_image_ids = malloc(sizeof(char*)); + if (*_image_ids == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to allocate memory for image identifiers"); + closedir(pool_dir); + return FAILURE; + } + (*_image_ids)[0] = NULL; + + // Iterate over the image pool directory + int count = 0; + struct dirent* entry; + while ((entry = readdir(pool_dir)) != NULL) { + // Skip the current and parent directory + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Check that the entry actually exists + bool exists; + if (DoesImageDirectoryExist(entry->d_name, &exists) != SUCCESS || !exists) + continue; + + // Allocate a new element in the array + char** new_image_ids = realloc(*_image_ids, (count + 2) * sizeof(char*)); // One element is reserved for the NULL terminator + if (new_image_ids == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to allocate memory for image identifiers"); + closedir(pool_dir); + for (int i = 0; i < count; i++) + free((*_image_ids)[i]); + free(*_image_ids); + return FAILURE; + } + *_image_ids = new_image_ids; + + // Copy the entry name into the array + if (Duplicate(entry->d_name, &(*_image_ids)[count]) != SUCCESS) { + Log(LOG_LEVEL_ERROR, "Failed to allocate memory for image identifier"); + closedir(pool_dir); + for (int i = 0; i < count; i++) + free((*_image_ids)[i]); + free(*_image_ids); + return FAILURE; + } + + // Null-terminate the array + (*_image_ids)[count + 1] = NULL; + + // Increment the count + count++; + } + + // Close the image pool directory + closedir(pool_dir); + + return SUCCESS; +} + +Result ClearImages(void) { + // Get the image identifiers + char** image_ids; + if (ListImages(&image_ids) != SUCCESS) + return FAILURE; + + // Remove each image + for (int i = 0; image_ids[i] != NULL; i++) + RemoveImage(image_ids[i]); + + // Free the image identifiers + for (int i = 0; image_ids[i] != NULL; i++) + free(image_ids[i]); + free(image_ids); + + return SUCCESS; +} + +Result AddRootImageDisk(const char* image_id, uint64_t size) { + // Check that the image directory exists + bool directory_exists; + if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS) + return FAILURE; + + if (!directory_exists) { + Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id); + return FAILURE; + } + + // Check that the image disk does not exist + bool disk_exists; + if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS) + return FAILURE; + + if (disk_exists) { + Log(LOG_LEVEL_ERROR, "Disk for image '%s' already exists", image_id); + return FAILURE; + } + + // Get the path to the image disk + char* disk_path; + if (GetImageDiskPath(image_id, &disk_path) != SUCCESS) + return FAILURE; + + // Create the root disk + Result result = CreateRootDisk(disk_path, size); + + free(disk_path); + + return result; +} + +Result AddTemplatedImageDisk(const char* image_id, const char* template_id) { + // Check that the image directory exists + bool directory_exists; + if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS) + return FAILURE; + + if (!directory_exists) { + Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id); + return FAILURE; + } + + // Check that the image disk does not exist + bool disk_exists; + if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS) + return FAILURE; + + if (disk_exists) { + Log(LOG_LEVEL_ERROR, "Disk for image '%s' already exists", image_id); + return FAILURE; + } + + // Check that the template exists + bool template_exists; + if (DoesTemplateExist(template_id, &template_exists) != SUCCESS) + return FAILURE; + + if (!template_exists) { + Log(LOG_LEVEL_ERROR, "Template '%s' does not exist", template_id); + return FAILURE; + } + + // Get the path to the template disk + char* template_disk_path; + if (GetTemplateDiskPath(template_id, &template_disk_path) != SUCCESS) + return FAILURE; + + // Get the path to the image disk + char* disk_path; + if (GetImageDiskPath(image_id, &disk_path) != SUCCESS) { + free(template_disk_path); + return FAILURE; + } + + // Create the templated disk + Result result = CreateBackedDisk(disk_path, template_disk_path); + + free(template_disk_path); + free(disk_path); + return result; +} + +Result RemoveImageDisk(const char* image_id) { + // Check that the image directory exists + bool directory_exists; + if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS) + return FAILURE; + + if (!directory_exists) { + Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id); + return FAILURE; + } + + // Check that the image disk exists + bool disk_exists; + if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS) + return FAILURE; + + if (!disk_exists) { + Log(LOG_LEVEL_ERROR, "Disk for image '%s' does not exist", image_id); + return FAILURE; + } + + // Get the path to the image disk + char* disk_path; + if (GetImageDiskPath(image_id, &disk_path) != SUCCESS) + return FAILURE; + + // Remove the image disk + int result = unlink(disk_path); + + free(disk_path); + + if (result != 0) { + Log(LOG_LEVEL_ERROR, "Failed to remove disk for image '%s' (%s)", image_id, strerror(errno)); + return FAILURE; + } + + return SUCCESS; +} + +Result TrimImageDisk(const char* image_id) { + // Check that the image directory exists + bool directory_exists; + if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS) + return FAILURE; + + if (!directory_exists) { + Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id); + return FAILURE; + } + + // Check that the image disk exists + bool disk_exists; + if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS) + return FAILURE; + + if (!disk_exists) { + Log(LOG_LEVEL_ERROR, "Disk for image '%s' does not exist", image_id); + return FAILURE; + } + + // Get the path to the image disk + char* disk_path; + if (GetImageDiskPath(image_id, &disk_path) != SUCCESS) + return FAILURE; + + // Trim the image disk + Result result = TrimDisk(disk_path); + + free(disk_path); + + return result; +} diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..fd992c9 --- /dev/null +++ b/src/image.h @@ -0,0 +1,24 @@ +#pragma once + +#include "utils.h" + +#define MAX_IMAGE_ID_LENGTH 64 + +Result IsImageIdentifierValid(const char* image_id, bool* _valid); + +Result GetImagePoolPath(char** _path); +Result GetImageDirectoryPath(const char* image_id, char** _path); +Result GetImageDiskPath(const char* image_id, char** _path); + +Result DoesImageDirectoryExist(const char* image_id, bool* _exists); +Result DoesImageDiskExist(const char* image_id, bool* _exists); + +Result AddImage(const char* image_id); +Result RemoveImage(const char* image_id); +Result ListImages(char*** _image_ids); +Result ClearImages(void); + +Result AddRootImageDisk(const char* image_id, uint64_t size); +Result AddTemplatedImageDisk(const char* image_id, const char* template_id); +Result RemoveImageDisk(const char* image_id); +Result TrimImageDisk(const char* image_id); diff --git a/src/sandbox.c b/src/sandbox.c index f5c3016..be5df54 100644 --- a/src/sandbox.c +++ b/src/sandbox.c @@ -1,618 +1,36 @@ #include "sandbox.h" -#include "entry.h" -#include "backing.h" +#include "utils.h" -#include -#include -#include -#include #include #include -#include -Command COMMANDS[] = { - {CommandHelp, "help", "[command]", "Prints this help message.", - "TODO: Add details."}, - {CommandVersion, "version", NULL, "Prints the version of the program.", - "TODO: Add details."}, - {}, - {CommandAddEntry, "add-entry", "", "Adds a new entry to the sandbox.", - "TODO: Add details."}, - {CommandRemoveEntry, "remove-entry", "", "Removes an entry from the sandbox.", - "TODO: Add details."}, - {CommandListEntries, "list-entries", NULL, "Lists all the entries in the sandbox.", - "TODO: Add details."}, - {CommandClearEntries, "clear-entries", NULL, "Clears all the entries from the sandbox.", - "TODO: Add details."}, - {}, - {CommandAddDisk, "add-disk", " [--root|-r ] [--backed|-b ]", "Adds a new disk to an entry.", - "TODO: Add details."}, - {CommandRemoveDisk, "remove-disk", "", "Removes the disk from an entry.", - "TODO: Add details."}, - {CommandResetDisk, "reset-disk", " [--update|-u] [--backed|-b ]", "Resets the disk of an entry.", - "TODO: Add details."}, - {CommandTrimDisk, "trim-disk", "", "Trims the disk of an entry.", - "TODO: Add details."}, - {}, - {CommandAddBacking, "add-backing", " ", "Adds a new backing to the sandbox.", - "TODO: Add details."}, - {CommandRemoveBacking, "remove-backing", "", "Removes a backing from the sandbox.", - "TODO: Add details."}, - {CommandListBackings, "list-backings", NULL, "Lists all the backings in the sandbox.", - "TODO: Add details."}, -}; - -int main(int argc, char* argv[]) { +int main(int argc, char** argv) { + // Ensure the sandbox user exists struct passwd* pw = getpwnam(SANDBOX_USER); if (pw == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to get the 'sandbox' user (%s).", strerror(errno)); + Log(LOG_LEVEL_ERROR, "User '%s' does not exist. Please check that the program is installed correctly.", SANDBOX_USER); return EXIT_FAILURE; } - // Check if the current user is root or the 'sandbox' user - if (getuid() != 0 && getuid() != pw->pw_uid) { - Log(LOG_LEVEL_ERROR, "You must be root or the 'sandbox' user to use this program."); + // Check that the program is either run as root or as the sandbox user + if (geteuid() != 0 && geteuid() != pw->pw_uid) { + Log(LOG_LEVEL_ERROR, "This program must be run as root or as the user '%s'.", SANDBOX_USER); return EXIT_FAILURE; } - // Try and switch to the 'sandbox' user if we are root + // If the program is run as root, switch to the sandbox user if (geteuid() == 0) { - if (setregid(pw->pw_gid, pw->pw_gid) == -1) { - Log(LOG_LEVEL_ERROR, "Failed to set the real and effective group ID to the 'sandbox' user (%s).", strerror(errno)); + if (setregid(pw->pw_gid, pw->pw_gid) != 0) { + Log(LOG_LEVEL_ERROR, "Failed to switch to the group '%s'.", pw->pw_name); return EXIT_FAILURE; } - if (setreuid(pw->pw_uid, pw->pw_uid) == -1) { - Log(LOG_LEVEL_ERROR, "Failed to set the real and effective user ID to the 'sandbox' user (%s).", strerror(errno)); + if (setreuid(pw->pw_uid, pw->pw_uid) != 0) { + Log(LOG_LEVEL_ERROR, "Failed to switch to the user '%s'.", pw->pw_name); return EXIT_FAILURE; } } - // If there are no arguments, print the help message - if (argc == 1) - return CommandHelp(0, NULL); - - const char* input = argv[1]; - size_t input_length = strlen(input); - - // Try and find the best matching command - const Command* command = NULL; - for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) { - if (COMMANDS[i].name == NULL) - continue; - - if (input_length > strlen(COMMANDS[i].name)) - continue; - - if (strncmp(input, COMMANDS[i].name, input_length) == 0) { - // If we have already found a matching command, then the input is ambiguous - if (command != NULL) { - Log(LOG_LEVEL_ERROR, "Ambiguous command '%s'.", input); - return EXIT_FAILURE; - } - - command = &COMMANDS[i]; - } - } - - if (command == NULL) { - Log(LOG_LEVEL_ERROR, "Unknown command '%s'.", input); - return EXIT_FAILURE; - } - - return command->handler(argc - 2, argv + 2); -} - -int CommandHelp(int argc, char* argv[]) { - // If there are no arguments, print the general help message - if (argc == 0) { - fprintf(stdout, "Usage: sandbox [arguments] [options]\n"); - fprintf(stdout, "\n"); - fprintf(stdout, "Commands:\n"); - for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) { - if (COMMANDS[i].name == NULL) { - fprintf(stdout, "\n"); - continue; - } - - fprintf(stdout, " %s", COMMANDS[i].name); - if (COMMANDS[i].arguments != NULL) - fprintf(stdout, " %s", COMMANDS[i].arguments); - fprintf(stdout, " - %s\n", COMMANDS[i].description); - } - - fprintf(stdout, "\nFor more information, run 'sandbox help '.\n"); - } else if (argc == 1) { // If there is one argument, print the help message for the command - const char* input = argv[0]; - - for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) { - if (COMMANDS[i].name == NULL) - continue; - - if (strcmp(input, COMMANDS[i].name) == 0) { - fprintf(stdout, "Usage: sandbox %s", COMMANDS[i].name); - if (COMMANDS[i].arguments != NULL) - fprintf(stdout, " %s", COMMANDS[i].arguments); - fprintf(stdout, "\n %s\n\n %s\n", COMMANDS[i].description, COMMANDS[i].details); - - return EXIT_SUCCESS; - } - } - - Log(LOG_LEVEL_ERROR, "Unknown command '%s'.", input); - return EXIT_FAILURE; - } else { // If there are too many arguments, print an error message - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'help'."); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -int CommandVersion(int argc, char* argv[]) { - if (argc > 0) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'version'."); - return EXIT_FAILURE; - } - - fprintf(stdout, "Sandbox utility v%s\n", VERSION); - - AddBacking("2 - Test", "test"); - - return EXIT_SUCCESS; -} - -int CommandAddEntry(int argc, char* argv[]) { - if (argc < 1) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'add-entry'."); - return EXIT_FAILURE; - } else if (argc > 1) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'add-entry'."); - return EXIT_FAILURE; - } - - // Extract the entry identifier - const char* entry_id = argv[0]; - - // Create the entry - Status status = AddEntry(entry_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully added entry '%s'.", entry_id); - return EXIT_SUCCESS; -} - -int CommandRemoveEntry(int argc, char* argv[]) { - if (argc < 1) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'remove-entry'."); - return EXIT_FAILURE; - } else if (argc > 1) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'remove-entry'."); - return EXIT_FAILURE; - } - - // Extract the entry identifier - const char* entry_id = argv[0]; - - // Remove the entry - Status status = RemoveEntry(entry_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully removed entry '%s'.", entry_id); - return EXIT_SUCCESS; -} - -int CommandListEntries(int argc, char* argv[]) { - if (argc > 0) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'list-entries'."); - return EXIT_FAILURE; - } - - // List the entries - char** entries = NULL; - Status status = ListEntries(&entries); - if (status != SUCCESS) - return EXIT_FAILURE; - - // Calculate the maximum length of the entries - size_t max_length = 0; - for (size_t i = 0; entries[i] != NULL; i++) { - size_t length = strlen(entries[i]); - if (length > max_length) - max_length = length; - } - - // Print the entries - for (size_t i = 0; entries[i] != NULL; i++) { - bool has_disk; - status = DoesEntryDiskExist(entries[i], &has_disk); - if (status != SUCCESS) - return EXIT_FAILURE; - - if (has_disk) { - DiskInfo info; - status = GetEntryDiskInfo(entries[i], &info); - if (status != SUCCESS) - continue; - - // Format the size - char* size_fmt; - status = FormatSize(info.allocated, &size_fmt); - if (status != SUCCESS) - continue; - - fprintf(stdout, "%-*s | %-10s | %s%s\n", (int)max_length, entries[i], size_fmt, info.backing_identifier == NULL ? "No backing" : "Backed by ", info.backing_identifier == NULL ? "" : info.backing_identifier); - } else - fprintf(stdout, "%-*s | %s\n", (int)max_length, entries[i], "No disk "); - } - - // Free the entries - for (size_t i = 0; entries[i] != NULL; i++) - free(entries[i]); - free(entries); - - return EXIT_SUCCESS; -} - -int CommandClearEntries(int argc, char* argv[]) { - if (argc > 0) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'clear-entries'."); - return EXIT_FAILURE; - } - - // Clear the entries - Status status = ClearEntries(); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully cleared all entries."); - return EXIT_SUCCESS; -} - -int CommandAddDisk(int argc, char* argv[]) { - if (argc < 1) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'add-disk'."); - return EXIT_FAILURE; - } - - // Extract the entry identifier - const char* entry_id = argv[0]; - - // Extract the options - bool root = false; - const char* size_str = NULL; - - bool backed = false; - const char* backing_id = NULL; - - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--root") == 0 || strcmp(argv[i], "-r") == 0) { - // Check if the option is duplicated - if (root) { - Log(LOG_LEVEL_ERROR, "Duplicate option '--root'."); - return EXIT_FAILURE; - } - - // Check if the option is the last argument - if (i + 1 == argc) { - Log(LOG_LEVEL_ERROR, "Missing argument for option '--root '."); - return EXIT_FAILURE; - } - - // Extract the size - size_str = argv[i + 1]; - root = true; - - // Skip the next argument as it is the size - i++; - } else if (strcmp(argv[i], "--backed") == 0 || strcmp(argv[i], "-b") == 0) { - // Check if the option is duplicated - if (backed) { - Log(LOG_LEVEL_ERROR, "Duplicate option '--backed'."); - return EXIT_FAILURE; - } - - // Check if the option is the last argument - if (i + 1 == argc) { - Log(LOG_LEVEL_ERROR, "Missing argument for option '--backed '."); - return EXIT_FAILURE; - } - - // Extract the backing identifier - backing_id = argv[i + 1]; - backed = true; - - // Skip the next argument as it is the backing identifier - i++; - } else { - Log(LOG_LEVEL_ERROR, "Unknown option '%s'.", argv[i]); - return EXIT_FAILURE; - } - } - - // Don't allow both root and backed options - if (root && backed) { - Log(LOG_LEVEL_ERROR, "Cannot use both '--root' and '--backed' options."); - return EXIT_FAILURE; - } - - if (root) { - // Parse the size - uint64_t size; - Status status = ParseSize(size_str, &size); - if (status != SUCCESS) - return EXIT_FAILURE; - - // Add the root disk - status = AddRootEntryDisk(entry_id, size); - if (status != SUCCESS) - return EXIT_FAILURE; - - // Format the size - char* size_fmt; - status = FormatSize(size, &size_fmt); - - Log(LOG_LEVEL_INFO, "Successfully added root disk to entry '%s' of size %s.", entry_id, size_fmt); - - free(size_fmt); - return EXIT_SUCCESS; - } else if (backed) { - // Check if the backing exists - Status status = AddBackedEntryDisk(entry_id, backing_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully added backed disk to entry '%s' with backing '%s'.", entry_id, backing_id); - return EXIT_SUCCESS; - } else { - // Get the latest backing to use as the default backing - char* backing_id; - Status status = GetLatestBacking(&backing_id); - if (status != SUCCESS) { - Log(LOG_LEVEL_ERROR, "Failed to get the latest backing to use as the default backing."); - return EXIT_FAILURE; - } - - if (backing_id == NULL) { - Log(LOG_LEVEL_ERROR, "No backings exist to use as the default backing."); - return EXIT_FAILURE; - } - - // Add the backed disk - status = AddBackedEntryDisk(entry_id, backing_id); - if (status != SUCCESS) { - free(backing_id); - return EXIT_FAILURE; - } - - Log(LOG_LEVEL_INFO, "Successfully added backed disk to entry '%s' with backing '%s'.", entry_id, backing_id); - - free(backing_id); - return EXIT_SUCCESS; - } -} - -int CommandRemoveDisk(int argc, char* argv[]) { - if (argc < 1) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'remove-disk'."); - return EXIT_FAILURE; - } else if (argc > 1) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'remove-disk'."); - return EXIT_FAILURE; - } - - // Extract the entry identifier - const char* entry_id = argv[0]; - - // Remove the disk - Status status = RemoveEntryDisk(entry_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully removed disk from entry '%s'.", entry_id); - return EXIT_SUCCESS; -} - -int CommandResetDisk(int argc, char* argv[]) { - if (argc < 1) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'reset-disk'."); - return EXIT_FAILURE; - } - - // Extract the entry identifier - const char* entry_id = argv[0]; - - // Extract the options - bool update = false; - bool backed = false; - const char* backing_id = NULL; - - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--update") == 0 || strcmp(argv[i], "-u") == 0) { - // Check if the option is duplicated - if (update) { - Log(LOG_LEVEL_ERROR, "Duplicate option '--update'."); - return EXIT_FAILURE; - } - - update = true; - } else if (strcmp(argv[i], "--backed") == 0 || strcmp(argv[i], "-b") == 0) { - // Check if the option is duplicated - if (backed) { - Log(LOG_LEVEL_ERROR, "Duplicate option '--backed'."); - return EXIT_FAILURE; - } - - // Check if the option is the last argument - if (i + 1 == argc) { - Log(LOG_LEVEL_ERROR, "Missing argument for option '--backed '."); - return EXIT_FAILURE; - } - - // Extract the backing identifier - backing_id = argv[i + 1]; - backed = true; - - // Skip the next argument as it is the backing identifier - i++; - } else { - Log(LOG_LEVEL_ERROR, "Unknown option '%s'.", argv[i]); - return EXIT_FAILURE; - } - } - - // Don't allow both update and backed options - if (update && backed) { - Log(LOG_LEVEL_ERROR, "Cannot use both '--update' and '--backed' options."); - return EXIT_FAILURE; - } - - if (update) { - char* backing_id; - Status status = GetLatestBacking(&backing_id); - if (status != SUCCESS) { - Log(LOG_LEVEL_ERROR, "Failed to get the latest backing to use as the default backing."); - return EXIT_FAILURE; - } - - if (backing_id == NULL) { - Log(LOG_LEVEL_ERROR, "No backings exist to use as the default backing."); - return EXIT_FAILURE; - } - - // Reset the disk - status = ResetEntryDisk(entry_id, backing_id); - if (status != SUCCESS) { - free(backing_id); - return EXIT_FAILURE; - } - - Log(LOG_LEVEL_INFO, "Successfully reset disk from entry '%s' with backing '%s'.", entry_id, backing_id); - - free(backing_id); - return EXIT_SUCCESS; - } else if (backed) { - // Reset the disk with the specified backing - Status status = ResetEntryDisk(entry_id, backing_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully reset disk from entry '%s' with backing '%s'.", entry_id, backing_id); - return EXIT_SUCCESS; - } else { - // Reset the disk - Status status = ResetEntryDisk(entry_id, NULL); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully reset disk from entry '%s'.", entry_id); - return EXIT_SUCCESS; - } -} -int CommandTrimDisk(int argc, char* argv[]) { - if (argc < 1) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'trim-disk'."); - return EXIT_FAILURE; - } else if (argc > 1) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'trim-disk'."); - return EXIT_FAILURE; - } - - // Extract the entry identifier - const char* entry_id = argv[0]; - - // Trim the disk - Status status = TrimEntryDisk(entry_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully trimmed disk from entry '%s'.", entry_id); - return EXIT_SUCCESS; -} - -int CommandAddBacking(int argc, char* argv[]) { - if (argc < 2) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'add-backing'."); - return EXIT_FAILURE; - } else if (argc > 2) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'add-backing'."); - return EXIT_FAILURE; - } - - // Extract the backing identifier and entry identifier - const char* entry_id = argv[0]; - const char* backing_description = argv[1]; - - // Generate the backing id by incrementing the latest backing index - char* latest_backing; - Status status = GetLatestBacking(&latest_backing); - if (status != SUCCESS) - return EXIT_FAILURE; - - int backing_index = 0; - if (latest_backing != NULL) { - backing_index = GetBackingIndex(latest_backing) + 1; - free(latest_backing); - } - - char* backing_id; - status = Format(&backing_id, "%d - %s", backing_index, backing_description); - if (status != SUCCESS) - return EXIT_FAILURE; - - // Add the backing - Status status = AddBacking(backing_id, entry_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully added backing '%s' from entry '%s'.", backing_id, entry_id); - return EXIT_SUCCESS; -} - -int CommandRemoveBacking(int argc, char* argv[]) { - if (argc < 1) { - Log(LOG_LEVEL_ERROR, "Too few arguments supplied to command 'remove-backing'."); - return EXIT_FAILURE; - } else if (argc > 1) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'remove-backing'."); - return EXIT_FAILURE; - } - - // Extract the backing identifier - const char* backing_id = argv[0]; - - // Remove the backing - Status status = RemoveBacking(backing_id); - if (status != SUCCESS) - return EXIT_FAILURE; - - Log(LOG_LEVEL_INFO, "Successfully removed backing '%s'.", backing_id); - return EXIT_SUCCESS; -} - -int CommandListBackings(int argc, char* argv[]) { - if (argc > 0) { - Log(LOG_LEVEL_ERROR, "Too many arguments supplied to command 'list-backings'."); - return EXIT_FAILURE; - } - - // List the backings - char** backings = NULL; - Status status = ListBackings(&backings); - if (status != SUCCESS) - return EXIT_FAILURE; - - // Print the backings - for (size_t i = 0; backings[i] != NULL; i++) - fprintf(stdout, "%s\n", backings[i]); - - // Free the backings - for (size_t i = 0; backings[i] != NULL; i++) - free(backings[i]); - free(backings); - - return EXIT_SUCCESS; -} + // TODO: Parse commands from the command line +} \ No newline at end of file diff --git a/src/sandbox.h b/src/sandbox.h index a81831a..cfc0918 100644 --- a/src/sandbox.h +++ b/src/sandbox.h @@ -1,32 +1,6 @@ #pragma once -#define VERSION "0.0.9" - +#define SANDBOX_VERSION "0.1.2" #define SANDBOX_USER "sandbox" -typedef struct { - int (*handler)(int argc, char* argv[]); - const char* name; - const char* arguments; - const char* description; - const char* details; -} Command; - -int main(int argc, char* argv[]); - -int CommandHelp(int argc, char* argv[]); -int CommandVersion(int argc, char* argv[]); - -int CommandAddEntry(int argc, char* argv[]); -int CommandRemoveEntry(int argc, char* argv[]); -int CommandListEntries(int argc, char* argv[]); -int CommandClearEntries(int argc, char* argv[]); - -int CommandAddDisk(int argc, char* argv[]); -int CommandRemoveDisk(int argc, char* argv[]); -int CommandResetDisk(int argc, char* argv[]); -int CommandTrimDisk(int argc, char* argv[]); - -int CommandAddBacking(int argc, char* argv[]); -int CommandRemoveBacking(int argc, char* argv[]); -int CommandListBackings(int argc, char* argv[]); +int main(int argc, char** argv); diff --git a/src/template.c b/src/template.c new file mode 100644 index 0000000..7ecc565 --- /dev/null +++ b/src/template.c @@ -0,0 +1,353 @@ +#include "template.h" + +#include "image.h" +#include "disk.h" + +#include +#include +#include +#include +#include +#include +#include + +Result IsTemplateIdentifierValid(const char* template_id, bool* _valid) { + *_valid = false; + + // Check that the template identifier is not NULL + if (template_id == NULL) + return SUCCESS; + + // Check that the template identifier is not empty or too long + size_t length = strlen(template_id); + if (length == 0 || length > MAX_TEMPLATE_ID_LENGTH) + return SUCCESS; + + // Check that the template identifier only contains allowed characters + for (size_t i = 0; i < length; i++) { + char c = template_id[i]; + if (c >= 'a' && c <= 'z') + continue; + if (c >= 'A' && c <= 'Z') + continue; + if (c >= '0' && c <= '9') + continue; + if (c == '_' || c == '-' || c == '.' || c == ' ') + continue; + + return SUCCESS; + } + + // Check that the template identifier is not a reserved name + if (strcmp(template_id, ".") == 0 || strcmp(template_id, "..") == 0) + return SUCCESS; + + // Check that the template identifier starts with a number (the order) + if (template_id[0] < '0' || template_id[0] > '9') + return SUCCESS; + + *_valid = true; + return SUCCESS; +} + +Result GetTemplateIdentifierOrder(const char* template_id, uint64_t* _order) { + // Check that the template identifier is valid + bool valid; + if (IsTemplateIdentifierValid(template_id, &valid) != SUCCESS || !valid) + return FAILURE; + + // Get the order from the template identifier + *_order = strtoull(template_id, NULL, 10); + return SUCCESS; +} + +Result GetTemplateIdentifierDescription(const char* template_id, char** _description) { + // Check that the template identifier is valid + bool valid; + if (IsTemplateIdentifierValid(template_id, &valid) != SUCCESS || !valid) + return FAILURE; + + // Get the length of the order + size_t length = 0; + while (template_id[length] >= '0' && template_id[length] <= '9') + length++; + + // Get the substring after the order + return Substring(template_id, length, strlen(template_id), _description); +} + +Result CreateTemplateIdentifier(uint64_t order, const char* template_description, char** _template_id) { + // Check that the description does not start with a number + if (template_description[0] >= '0' && template_description[0] <= '9') + return FAILURE; + + // Format the template identifier + if (Format(_template_id, "%llu%s", order, template_description) != SUCCESS) + return FAILURE; + + // Check that the template identifier is valid + bool valid; + return IsTemplateIdentifierValid(*_template_id, &valid) == SUCCESS && valid ? SUCCESS : FAILURE; +} + +Result GetTemplatePoolPath(char** _path) { + return Format(_path, "/var/lib/sandbox/templates"); +} + +Result GetTemplateDiskPath(const char* template_id, char** _path) { + // Check that the template identifier is valid + bool valid; + if (IsTemplateIdentifierValid(template_id, &valid) != SUCCESS || !valid) + return FAILURE; + + // Get the path to the template pool + char* pool_path; + if (GetTemplatePoolPath(&pool_path) != SUCCESS) + return FAILURE; + + // Format the template disk path + Result result = Format(_path, "%s/%s", pool_path, template_id); + + free(pool_path); + return result; +} + +Result DoesTemplateExist(const char* template_id, bool* _exists) { + // Get the path to the template disk + char* disk_path; + if (GetTemplateDiskPath(template_id, &disk_path) != SUCCESS) + return FAILURE; + + // Check if the template disk exists + struct stat st; + *_exists = stat(disk_path, &st) == 0 && S_ISDIR(st.st_mode); + + free(disk_path); + return SUCCESS; +} + +Result AddTemplate(const char* template_id, const char* image_id) { + // Check that the template does not already exist + bool exists; + if (DoesTemplateExist(template_id, &exists) != SUCCESS) + return FAILURE; + + if (exists) { + Log(LOG_LEVEL_ERROR, "Template '%s' already exists.", template_id); + return FAILURE; + } + + // Check that the image exists + bool image_exists; + if (DoesImageDiskExist(image_id, &image_exists) != SUCCESS) + return FAILURE; + + if (!image_exists) { + Log(LOG_LEVEL_ERROR, "Image '%s' does not exist.", image_id); + return FAILURE; + } + + // Check that the image has a disk + bool image_has_disk; + if (DoesImageDiskExist(image_id, &image_has_disk) != SUCCESS) + return FAILURE; + + if (!image_has_disk) { + Log(LOG_LEVEL_ERROR, "Image '%s' does not have a disk.", image_id); + return FAILURE; + } + + // Get the path of the image disk + char* image_disk_path; + if (GetImageDiskPath(image_id, &image_disk_path) != SUCCESS) + return FAILURE; + + // Get information about the image disk + DiskInfo image_disk_info; + if (GetDiskInfo(image_disk_path, &image_disk_info) != SUCCESS) { + free(image_disk_path); + return FAILURE; + } + + // If the image disk has a backing, check that the backing is an existing template + if (image_disk_info.backing_identifier != NULL) { + bool backing_exists; + if (DoesTemplateExist(image_disk_info.backing_identifier, &backing_exists) != SUCCESS) { + free(image_disk_path); + FreeDiskInfo(&image_disk_info); + return FAILURE; + } + + if (!backing_exists) { + Log(LOG_LEVEL_ERROR, "Backing template '%s' of image '%s' does not exist.", image_disk_info.backing_identifier, image_id); + free(image_disk_path); + FreeDiskInfo(&image_disk_info); + return FAILURE; + } + } + + // Get the path to the template disk + char* template_disk_path; + if (GetTemplateDiskPath(template_id, &template_disk_path) != SUCCESS) { + free(image_disk_path); + FreeDiskInfo(&image_disk_info); + return FAILURE; + } + + // Copy the image disk to the template disk + Result result = CopyFile(image_disk_path, template_disk_path); + + free(image_disk_path); + + if (result != SUCCESS) { + free(template_disk_path); + FreeDiskInfo(&image_disk_info); + return FAILURE; + } + + // If the image disk has a backing, reback the template disk to be relative + if (image_disk_info.backing_identifier != NULL) + if (RebackDisk(template_disk_path, image_disk_info.backing_identifier) != SUCCESS) { + unlink(template_disk_path); + free(template_disk_path); + FreeDiskInfo(&image_disk_info); + return FAILURE; + } + + // Free the template disk path and image disk info + free(template_disk_path); + FreeDiskInfo(&image_disk_info); + + // Return success + return SUCCESS; +} + +Result RemoveTemplate(const char* template_id) { + // Check that the template exists + bool exists; + if (DoesTemplateExist(template_id, &exists) != SUCCESS) + return FAILURE; + + if (!exists) { + Log(LOG_LEVEL_ERROR, "Template '%s' does not exist.", template_id); + return FAILURE; + } + + // Get the path to the template disk + char* disk_path; + if (GetTemplateDiskPath(template_id, &disk_path) != SUCCESS) + return FAILURE; + + // Remove the template disk + int result = rmdir(disk_path); + + // TODO: Remove any template that is based on this template + + free(disk_path); + return result == 0 ? SUCCESS : FAILURE; +} + +Result ListTemplates(char*** _template_ids) { + // Get the path to the template pool + char* pool_path; + if (GetTemplatePoolPath(&pool_path) != SUCCESS) + return FAILURE; + + // Open the template pool directory + DIR* pool_dir = opendir(pool_path); + if (pool_dir == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to open template pool directory '%s' (%s).", pool_path, strerror(errno)); + free(pool_path); + return FAILURE; + } + free(pool_path); + + // Allocate an initial array, one element is reserved for the NULL terminator + *_template_ids = malloc(sizeof(char*)); + if (*_template_ids == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to allocate memory for template identifiers."); + closedir(pool_dir); + return FAILURE; + } + (*_template_ids)[0] = NULL; + + // Iterate over the template pool directory + int count = 0; + struct dirent* entry; + while ((entry = readdir(pool_dir)) != NULL) { + // Skip the current and parent directory + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Check that the entry actually exists + bool exists; + if (DoesTemplateExist(entry->d_name, &exists) != SUCCESS || !exists) + continue; + + // Allocate a new element in the array + char** new_template_ids = realloc(*_template_ids, (count + 2) * sizeof(char*)); // One element is reserved for the NULL terminator + if (new_template_ids == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to allocate memory for template identifiers."); + closedir(pool_dir); + for (int i = 0; i < count; i++) + free((*_template_ids)[i]); + free(*_template_ids); + return FAILURE; + } + *_template_ids = new_template_ids; + + // Copy the entry name into the array + if (Duplicate(entry->d_name, &(*_template_ids)[count]) != SUCCESS) { + Log(LOG_LEVEL_ERROR, "Failed to allocate memory for template identifier."); + closedir(pool_dir); + for (int i = 0; i < count; i++) + free((*_template_ids)[i]); + free(*_template_ids); + return FAILURE; + } + + // Null-terminate the array + (*_template_ids)[count + 1] = NULL; + + // Increment the count + count++; + } + + // Close the template pool directory + closedir(pool_dir); + + return SUCCESS; +} + +Result GetHighestOrderTemplate(char** _template_id) { + *_template_id = NULL; + + // Get the list of template identifiers + char** template_ids; + if (ListTemplates(&template_ids) != SUCCESS) + return FAILURE; + + // Find the highest order template identifier + uint64_t highest_order = -1; + char* highest_template_id = NULL; + + for (int i = 0; template_ids[i] != NULL; i++) { + uint64_t order; + if (GetTemplateIdentifierOrder(template_ids[i], &order) != SUCCESS) + continue; + + if (order > highest_order) { + highest_order = order; + highest_template_id = template_ids[i]; + } + } + + // Duplicate the highest order template identifier + if (Duplicate(highest_template_id, _template_id) != SUCCESS) + return FAILURE; + + for (int i = 0; template_ids[i] != NULL; i++) + free(template_ids[i]); + free(template_ids); + return SUCCESS; +} diff --git a/src/template.h b/src/template.h new file mode 100644 index 0000000..7e96d85 --- /dev/null +++ b/src/template.h @@ -0,0 +1,19 @@ +#pragma once + +#include "utils.h" + +#define MAX_TEMPLATE_ID_LENGTH 64 + +Result IsTemplateIdentifierValid(const char* template_id, bool* _valid); +Result GetTemplateIdentifierOrder(const char* template_id, uint64_t* _order); +Result GetTemplateIdentifierDescription(const char* template_id, char** _description); +Result CreateTemplateIdentifier(uint64_t order, const char* template_description, char** _template_id); + +Result GetTemplatePoolPath(char** _path); +Result GetTemplateDiskPath(const char* template_id, char** _path); +Result DoesTemplateExist(const char* template_id, bool* _exists); + +Result AddTemplate(const char* template_id, const char* image_id); +Result RemoveTemplate(const char* template_id); +Result ListTemplates(char*** _template_ids); +Result GetHighestOrderTemplate(char** _template_id); \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index cb0041d..9d72bf8 100644 --- a/src/utils.c +++ b/src/utils.c @@ -61,7 +61,7 @@ void Log(LogLevel level, const char* format, ...) { va_end(args); } -Status Format(char** _string, const char* fmt, ...) { +Result Format(char** _string, const char* fmt, ...) { *_string = NULL; va_list args; @@ -92,7 +92,41 @@ Status Format(char** _string, const char* fmt, ...) { return SUCCESS; } -Status FormatSize(uint64_t size, char** _size_str) { +Result Duplicate(const char* string, char** _duplicate) { + *_duplicate = NULL; + + if (string == NULL) + return SUCCESS; + + *_duplicate = strdup(string); + if (*_duplicate == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to duplicate the string (%s).", strerror(errno)); + return FAILURE; + } + + return SUCCESS; +} + +Result Substring(const char* string, size_t start, size_t end, char** _substring) { + if (start > end) { + Log(LOG_LEVEL_ERROR, "Invalid substring range."); + return FAILURE; + } + + size_t length = end - start; + *_substring = malloc(length + 1); + if (*_substring == NULL) { + Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the substring (%s).", strerror(errno)); + return FAILURE; + } + + memcpy(*_substring, string + start, length); + (*_substring)[length] = '\0'; + + return SUCCESS; +} + +Result FormatSize(uint64_t size, char** _size_str) { *_size_str = NULL; // Determine the unit @@ -119,7 +153,7 @@ Status FormatSize(uint64_t size, char** _size_str) { return Format(_size_str, "%.2f %s", value, unit); } -Status ParseSize(const char* size_str, uint64_t* _size) { +Result ParseSize(const char* size_str, uint64_t* _size) { *_size = 0; // Parse the size @@ -153,7 +187,7 @@ Status ParseSize(const char* size_str, uint64_t* _size) { return SUCCESS; } -Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) { +Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) { if (_exit_code != NULL) *_exit_code = -1; if (_stdout != NULL) @@ -301,7 +335,7 @@ Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char return SUCCESS; } -Status ReadFileDescriptor(int fd, char** _content) { +Result ReadFileDescriptor(int fd, char** _content) { *_content = NULL; // Allocate a buffer for the content @@ -333,7 +367,7 @@ Status ReadFileDescriptor(int fd, char** _content) { return SUCCESS; } -Status WriteFileDescriptor(int fd, const char* content) { +Result WriteFileDescriptor(int fd, const char* content) { size_t length = strlen(content); ssize_t bytes_written = 0; @@ -350,7 +384,7 @@ Status WriteFileDescriptor(int fd, const char* content) { return SUCCESS; } -Status ReadFile(const char* path, char** _content) { +Result ReadFile(const char* path, char** _content) { *_content = NULL; // Open the file @@ -361,7 +395,7 @@ Status ReadFile(const char* path, char** _content) { } // Read the file - Status status = ReadFileDescriptor(file, _content); + Result status = ReadFileDescriptor(file, _content); // Close the file close(file); @@ -369,7 +403,7 @@ Status ReadFile(const char* path, char** _content) { return status; } -Status WriteFile(const char* path, const char* content) { +Result WriteFile(const char* path, const char* content) { // Open the file int file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (file == -1) { @@ -378,7 +412,7 @@ Status WriteFile(const char* path, const char* content) { } // Write the content - Status status = WriteFileDescriptor(file, content); + Result status = WriteFileDescriptor(file, content); // Close the file close(file); @@ -386,7 +420,7 @@ Status WriteFile(const char* path, const char* content) { return status; } -Status CopyFile(const char* source_path, const char* destination_path) { +Result CopyFile(const char* source_path, const char* destination_path) { // Open the source file int source_file = open(source_path, O_RDONLY); if (source_file == -1) { diff --git a/src/utils.h b/src/utils.h index 6a9c89b..70e1cf9 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2,11 +2,12 @@ #include #include +#include typedef enum { SUCCESS, FAILURE -} Status; +} Result; typedef enum { LOG_LEVEL_DEBUG, @@ -20,16 +21,18 @@ extern LogLevel LOG_LEVEL; void SetLogLevel(LogLevel level); void Log(LogLevel level, const char* format, ...); -Status Format(char** _string, const char* fmt, ...); +Result Format(char** _string, const char* fmt, ...); +Result Duplicate(const char* string, char** _duplicate); +Result Substring(const char* string, size_t start, size_t end, char** _substring); -Status FormatSize(uint64_t size, char** _size_str); -Status ParseSize(const char* size_str, uint64_t* _size); +Result FormatSize(uint64_t size, char** _size_str); +Result ParseSize(const char* size_str, uint64_t* _size); -Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...); +Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...); -Status ReadFileDescriptor(int fd, char** _content); -Status WriteFileDescriptor(int fd, const char* content); -Status ReadFile(const char* path, char** _content); -Status WriteFile(const char* path, const char* content); +Result ReadFileDescriptor(int fd, char** _content); +Result WriteFileDescriptor(int fd, const char* content); +Result ReadFile(const char* path, char** _content); +Result WriteFile(const char* path, const char* content); -Status CopyFile(const char* source_path, const char* destination_path); +Result CopyFile(const char* source_path, const char* destination_path);