From 3e08d6a438ce587a41c391bd23f17837ca4a22be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexe=C3=AF=20KADIR?= Date: Sun, 18 Feb 2024 14:05:04 +0100 Subject: [PATCH] Added commands for managing disks --- src/backing.c | 117 ++++++++++++++++++++++++++++++++- src/backing.h | 2 +- src/disk.c | 4 +- src/entry.c | 56 ++++++++++++---- src/sandbox.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++---- src/sandbox.h | 7 +- src/utils.c | 67 ++++++++++++++++++- src/utils.h | 7 +- 8 files changed, 403 insertions(+), 35 deletions(-) diff --git a/src/backing.c b/src/backing.c index 8efc96f..b051bc8 100644 --- a/src/backing.c +++ b/src/backing.c @@ -49,7 +49,7 @@ Status GetBackingPoolPath(char** _backing_pool_path) { return Format(_backing_pool_path, "/var/lib/sandbox/backings"); } -Status GetBackingDiskPath(const char* backing_identifier, char** _backing_path) { +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); @@ -72,7 +72,7 @@ Status GetBackingDiskPath(const char* backing_identifier, char** _backing_path) Status DoesBackingExist(const char* backing_identifier, bool* _result) { // Get the backing path char* backing_path = NULL; - Status status = GetBackingDiskPath(backing_identifier, &backing_path); + Status status = GetBackingPath(backing_identifier, &backing_path); if (status != SUCCESS) return status; @@ -83,3 +83,116 @@ Status DoesBackingExist(const char* backing_identifier, bool* _result) { 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; +} diff --git a/src/backing.h b/src/backing.h index 3127780..a5508bb 100644 --- a/src/backing.h +++ b/src/backing.h @@ -6,7 +6,7 @@ bool IsBackingIdentifierValid(const char* backing_identifier); int GetBackingIndex(const char* backing_identifier); Status GetBackingPoolPath(char** _backing_pool_path); -Status GetBackingDiskPath(const char* backing_identifier, char** _backing_path); +Status GetBackingPath(const char* backing_identifier, char** _backing_path); Status DoesBackingExist(const char* backing_identifier, bool* _result); diff --git a/src/disk.c b/src/disk.c index 3464640..bd64229 100644 --- a/src/disk.c +++ b/src/disk.c @@ -103,11 +103,11 @@ Status TrimDisk(const char* path) { return status; } - status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_file_opt, tmp_path, NULL); + 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", "create", "-f", "qcow2", "-O", "qcow2", tmp_path, NULL); + status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, tmp_path, NULL); FreeDiskInfo(&info); diff --git a/src/entry.c b/src/entry.c index 3b62eae..701dc48 100644 --- a/src/entry.c +++ b/src/entry.c @@ -193,23 +193,23 @@ Status ListEntries(char*** _entries) { } (*_entries)[0] = NULL; - // Read the entries from the directory + // Read the files in the entry pool directory size_t count = 0; - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 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(entry->d_name, &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 entries list (%s).", strerror(errno)); + 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++) @@ -221,7 +221,7 @@ Status ListEntries(char*** _entries) { *_entries = new_entries; // Duplicate the entry name and add it to the list - (*_entries)[count] = strdup(entry->d_name); + (*_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); @@ -266,9 +266,19 @@ Status ClearEntries() { } Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size) { - // Check if the disk already exists + // Check that the entry exists bool exists; - Status status = DoesEntryDiskExist(entry_identifier, &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; @@ -291,9 +301,29 @@ Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size) { } Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_identifier) { - // Check if the disk already exists + // Check that the entry exists bool exists; - Status status = DoesEntryDiskExist(entry_identifier, &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; @@ -304,7 +334,7 @@ Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_iden // Get the backing disk path char* backing_disk_path = NULL; - status = GetBackingDiskPath(backing_identifier, &backing_disk_path); + status = GetBackingPath(backing_identifier, &backing_disk_path); if (status != SUCCESS) return status; @@ -368,7 +398,7 @@ Status ResetEntryDisk(const char* entry_identifier, const char* backing_identifi // Get the backing disk path char* backing_disk_path = NULL; if (backing_identifier != NULL) { - status = GetBackingDiskPath(backing_identifier, &backing_disk_path); + status = GetBackingPath(backing_identifier, &backing_disk_path); if (status != SUCCESS) return status; } diff --git a/src/sandbox.c b/src/sandbox.c index 68439d8..5b0f8ac 100644 --- a/src/sandbox.c +++ b/src/sandbox.c @@ -24,7 +24,15 @@ Command COMMANDS[] = { "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] [--backing|-b ]", "Resets the disk of an entry.", + "TODO: Add details."}, + {CommandTrimDisk, "trim-disk", "", "Trims the disk of an entry.", + "TODO: Add details."}}; int main(int argc, char* argv[]) { struct passwd* pw = getpwnam(SANDBOX_USER); @@ -149,11 +157,13 @@ int CommandAddEntry(int argc, char* argv[]) { return EXIT_FAILURE; } - Status status = AddEntry(argv[0]); - if (status != SUCCESS) - return status; + char* entry_identifier = argv[0]; - Log(LOG_LEVEL_INFO, "Added entry '%s'.", argv[0]); + Status status = AddEntry(entry_identifier); + if (status != SUCCESS) + return EXIT_FAILURE; + + Log(LOG_LEVEL_INFO, "Added entry '%s'.", entry_identifier); return EXIT_SUCCESS; } @@ -166,11 +176,13 @@ int CommandRemoveEntry(int argc, char* argv[]) { return EXIT_FAILURE; } - Status status = RemoveEntry(argv[0]); - if (status != SUCCESS) - return status; + char* entry_identifier = argv[0]; - Log(LOG_LEVEL_INFO, "Removed entry '%s'.", argv[0]); + Status status = RemoveEntry(entry_identifier); + if (status != SUCCESS) + return EXIT_FAILURE; + + Log(LOG_LEVEL_INFO, "Removed entry '%s'.", entry_identifier); return EXIT_SUCCESS; } @@ -183,7 +195,7 @@ int CommandListEntries(int argc, char* argv[]) { char** entries = NULL; Status status = ListEntries(&entries); if (status != SUCCESS) - return status; + return EXIT_FAILURE; for (size_t i = 0; entries[i] != NULL; i++) fprintf(stdout, "%s\n", entries[i]); @@ -203,8 +215,152 @@ int CommandClearEntries(int argc, char* argv[]) { Status status = ClearEntries(); if (status != SUCCESS) - return status; + return EXIT_FAILURE; Log(LOG_LEVEL_INFO, "Cleared all entries."); return EXIT_SUCCESS; } + +int CommandAddDisk(int argc, char* argv[]) { + if (argc < 1) { + Log(LOG_LEVEL_ERROR, "Too few arguments supplied to 'add-disk'."); + return EXIT_FAILURE; + } + + char* entry_identifier = argv[0]; + + bool root = false; + uint64_t size = 0; + + bool backed = false; + const char* backing_identifier = NULL; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--root") == 0 || strcmp(argv[i], "-r") == 0) { + if (root) { + Log(LOG_LEVEL_ERROR, "Duplicate '--root' argument supplied to 'add-disk'."); + return EXIT_FAILURE; + } + + if (i + 1 >= argc) { + Log(LOG_LEVEL_ERROR, "Too few arguments supplied to 'add-disk'. Missing size for '--root '."); + return EXIT_FAILURE; + } + + root = true; + Status status = ParseSize(argv[i + 1], &size); + if (status != SUCCESS) + return EXIT_FAILURE; + + if (size == 0) { + Log(LOG_LEVEL_ERROR, "Invalid size '%s' supplied to 'add-disk'.", argv[i + 1]); + return EXIT_FAILURE; + } + + i++; // Consumes the next argument (the size) + } else if (strcmp(argv[i], "--backed") == 0 || strcmp(argv[i], "-b") == 0) { + if (backed) { + Log(LOG_LEVEL_ERROR, "Duplicate '--backed' argument supplied to 'add-disk'."); + return EXIT_FAILURE; + } + + if (i + 1 >= argc) { + Log(LOG_LEVEL_ERROR, "Too few arguments supplied to 'add-disk'. Missing backing identifier for '--backed '."); + return EXIT_FAILURE; + } + + backed = true; + backing_identifier = argv[i + 1]; + + if (!IsBackingIdentifierValid(backing_identifier)) { + Log(LOG_LEVEL_ERROR, "Invalid backing identifier '%s' supplied to 'add-disk'.", backing_identifier); + return EXIT_FAILURE; + } + + i++; // Consumes the next argument (the backing identifier) + } else { + Log(LOG_LEVEL_ERROR, "Unknown argument '%s' supplied to 'add-disk'.", argv[i]); + return EXIT_FAILURE; + } + } + + if (root && backed) { + Log(LOG_LEVEL_ERROR, "The options '--root' and '--backed' cannot be used together in 'add-disk'."); + return EXIT_FAILURE; + } + + if (root) { + Status status = AddRootEntryDisk(entry_identifier, size); + if (status != SUCCESS) + return EXIT_FAILURE; + + char* size_str = NULL; + status = FormatSize(size, &size_str); + Log(LOG_LEVEL_INFO, "Added root disk to entry '%s' with size %s.", entry_identifier, size_str); + free(size_str); + } else if (backed) { + Status status = AddBackedEntryDisk(entry_identifier, backing_identifier); + if (status != SUCCESS) + return EXIT_FAILURE; + + Log(LOG_LEVEL_INFO, "Added backed disk to entry '%s' with backing '%s'.", entry_identifier, backing_identifier); + } else { + char* backing_identifier = NULL; + Status status = GetLatestBacking(&backing_identifier); + if (status != SUCCESS) + return EXIT_FAILURE; + + status = AddBackedEntryDisk(entry_identifier, backing_identifier); + if (status != SUCCESS) { + free(backing_identifier); + return EXIT_FAILURE; + } + + Log(LOG_LEVEL_INFO, "Added backed disk to entry '%s' with backing '%s'.", entry_identifier, backing_identifier); + free(backing_identifier); + } + + return EXIT_SUCCESS; +} + +int CommandRemoveDisk(int argc, char* argv[]) { + if (argc < 1) { + Log(LOG_LEVEL_ERROR, "Too few arguments supplied to 'remove-disk'."); + return EXIT_FAILURE; + } else if (argc > 1) { + Log(LOG_LEVEL_ERROR, "Too many arguments supplied to 'remove-disk'."); + return EXIT_FAILURE; + } + + char* entry_identifier = argv[0]; + + Status status = RemoveEntryDisk(entry_identifier); + if (status != SUCCESS) + return EXIT_FAILURE; + + Log(LOG_LEVEL_INFO, "Removed disk from entry '%s'.", entry_identifier); + return EXIT_SUCCESS; +} + +int CommandResetDisk(int argc, char* argv[]) { + return 0; +} + +int CommandTrimDisk(int argc, char* argv[]) { + if (argc < 1) { + Log(LOG_LEVEL_ERROR, "Too few arguments supplied to 'trim-disk'."); + return EXIT_FAILURE; + } else if (argc > 1) { + Log(LOG_LEVEL_ERROR, "Too many arguments supplied to 'trim-disk'."); + return EXIT_FAILURE; + } + + char* entry_identifier = argv[0]; + + Status status = TrimEntryDisk(entry_identifier); + if (status != SUCCESS) + return EXIT_FAILURE; + + Log(LOG_LEVEL_INFO, "Trimmed disk of entry '%s'.", entry_identifier); + return EXIT_SUCCESS; +} diff --git a/src/sandbox.h b/src/sandbox.h index 954250a..366e577 100644 --- a/src/sandbox.h +++ b/src/sandbox.h @@ -20,4 +20,9 @@ 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[]); \ No newline at end of file +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[]); \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 5ed4982..cb0041d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -11,14 +11,14 @@ #include #include -LogLevel log_level = LOG_LEVEL_INFO; +LogLevel LOG_LEVEL = LOG_LEVEL_INFO; void SetLogLevel(LogLevel level) { - log_level = level; + LOG_LEVEL = level; } void Log(LogLevel level, const char* format, ...) { - if (level < log_level) + if (level < LOG_LEVEL) return; va_list args; @@ -92,6 +92,67 @@ Status Format(char** _string, const char* fmt, ...) { return SUCCESS; } +Status FormatSize(uint64_t size, char** _size_str) { + *_size_str = NULL; + + // Determine the unit + const char* unit; + double value = size; + + if (value >= 1024ULL * 1024ULL * 1024ULL * 1024ULL) { + unit = "TiB"; + value /= 1024.0 * 1024.0 * 1024.0 * 1024.0; + } else if (value >= 1024ULL * 1024ULL * 1024ULL) { + unit = "GiB"; + value /= 1024.0 * 1024.0 * 1024.0; + } else if (value >= 1024ULL * 1024ULL) { + unit = "MiB"; + value /= 1024.0 * 1024.0; + } else if (value >= 1024ULL) { + unit = "KiB"; + value /= 1024.0; + } else { + unit = "B"; + } + + // Format the size + return Format(_size_str, "%.2f %s", value, unit); +} + +Status ParseSize(const char* size_str, uint64_t* _size) { + *_size = 0; + + // Parse the size + char* endptr; + uint64_t size = strtoull(size_str, &endptr, 10); + if (endptr == size_str) { + Log(LOG_LEVEL_ERROR, "Failed to parse the size string."); + return FAILURE; + } + + if (*endptr == '\0') { + *_size = size; + return SUCCESS; + } + + // Determine the unit + if (strcmp(endptr, "TiB") == 0 || strcmp(endptr, "T") == 0 || strcmp(endptr, "TB") == 0) + size *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; + else if (strcmp(endptr, "GiB") == 0 || strcmp(endptr, "G") == 0 || strcmp(endptr, "GB") == 0) + size *= 1024ULL * 1024ULL * 1024ULL; + else if (strcmp(endptr, "MiB") == 0 || strcmp(endptr, "M") == 0 || strcmp(endptr, "MB") == 0) + size *= 1024ULL * 1024ULL; + else if (strcmp(endptr, "KiB") == 0 || strcmp(endptr, "K") == 0 || strcmp(endptr, "KB") == 0) + size *= 1024ULL; + else if (strcmp(endptr, "B") != 0) { + Log(LOG_LEVEL_ERROR, "Invalid size unit '%s'.", endptr); + return FAILURE; + } + + *_size = size; + return SUCCESS; +} + Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) { if (_exit_code != NULL) *_exit_code = -1; diff --git a/src/utils.h b/src/utils.h index c9d5ea6..6a9c89b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -15,13 +15,16 @@ typedef enum { LOG_LEVEL_ERROR, } LogLevel; -extern LogLevel log_level; +extern LogLevel LOG_LEVEL; void SetLogLevel(LogLevel level); void Log(LogLevel level, const char* format, ...); Status Format(char** _string, const char* fmt, ...); +Status FormatSize(uint64_t size, char** _size_str); +Status ParseSize(const char* size_str, uint64_t* _size); + Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...); Status ReadFileDescriptor(int fd, char** _content); @@ -29,4 +32,4 @@ Status WriteFileDescriptor(int fd, const char* content); Status ReadFile(const char* path, char** _content); Status WriteFile(const char* path, const char* content); -Status CopyFile(const char* source_path, const char* destination_path); \ No newline at end of file +Status CopyFile(const char* source_path, const char* destination_path);