Added commands for managing disks

This commit is contained in:
Alexei KADIR 2024-02-18 14:05:04 +01:00
parent a149a838d1
commit 3e08d6a438
8 changed files with 403 additions and 35 deletions

View File

@ -49,7 +49,7 @@ Status GetBackingPoolPath(char** _backing_pool_path) {
return Format(_backing_pool_path, "/var/lib/sandbox/backings"); 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 // Check that the identifier is valid, as it will be used in a path
if (!IsBackingIdentifierValid(backing_identifier)) { if (!IsBackingIdentifierValid(backing_identifier)) {
Log(LOG_LEVEL_ERROR, "Invalid backing identifier '%s'.", 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) { Status DoesBackingExist(const char* backing_identifier, bool* _result) {
// Get the backing path // Get the backing path
char* backing_path = NULL; char* backing_path = NULL;
Status status = GetBackingDiskPath(backing_identifier, &backing_path); Status status = GetBackingPath(backing_identifier, &backing_path);
if (status != SUCCESS) if (status != SUCCESS)
return status; return status;
@ -83,3 +83,116 @@ Status DoesBackingExist(const char* backing_identifier, bool* _result) {
return SUCCESS; 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;
}

View File

@ -6,7 +6,7 @@ bool IsBackingIdentifierValid(const char* backing_identifier);
int GetBackingIndex(const char* backing_identifier); int GetBackingIndex(const char* backing_identifier);
Status GetBackingPoolPath(char** _backing_pool_path); 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); Status DoesBackingExist(const char* backing_identifier, bool* _result);

View File

@ -103,11 +103,11 @@ Status TrimDisk(const char* path) {
return status; 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); free(backing_file_opt);
} else } 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); FreeDiskInfo(&info);

View File

@ -193,23 +193,23 @@ Status ListEntries(char*** _entries) {
} }
(*_entries)[0] = NULL; (*_entries)[0] = NULL;
// Read the entries from the directory // Read the files in the entry pool directory
size_t count = 0; size_t count = 0;
struct dirent* entry; struct dirent* file;
while ((entry = readdir(dir)) != NULL) { while ((file = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) if (strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)
continue; continue;
// Check if the entry exists (this checks for validity) // Check if the entry exists (this checks for validity)
bool exists; bool exists;
Status status = DoesEntryExist(entry->d_name, &exists); Status status = DoesEntryExist(file->d_name, &exists);
if (status != SUCCESS || !exists) if (status != SUCCESS || !exists)
continue; continue;
// Allocate memory for the new entry list // 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 char** new_entries = realloc(*_entries, (count + 2) * sizeof(char*)); // +2 for the new entry and the NULL terminator
if (new_entries == NULL) { 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); closedir(dir);
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
@ -221,7 +221,7 @@ Status ListEntries(char*** _entries) {
*_entries = new_entries; *_entries = new_entries;
// Duplicate the entry name and add it to the list // 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) { if ((*_entries)[count] == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to duplicate the entry name (%s).", strerror(errno)); Log(LOG_LEVEL_ERROR, "Failed to duplicate the entry name (%s).", strerror(errno));
closedir(dir); closedir(dir);
@ -266,9 +266,19 @@ Status ClearEntries() {
} }
Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size) { Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size) {
// Check if the disk already exists // Check that the entry exists
bool 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) if (status != SUCCESS)
return status; 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) { Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_identifier) {
// Check if the disk already exists // Check that the entry exists
bool 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) if (status != SUCCESS)
return status; return status;
@ -304,7 +334,7 @@ Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_iden
// Get the backing disk path // Get the backing disk path
char* backing_disk_path = NULL; char* backing_disk_path = NULL;
status = GetBackingDiskPath(backing_identifier, &backing_disk_path); status = GetBackingPath(backing_identifier, &backing_disk_path);
if (status != SUCCESS) if (status != SUCCESS)
return status; return status;
@ -368,7 +398,7 @@ Status ResetEntryDisk(const char* entry_identifier, const char* backing_identifi
// Get the backing disk path // Get the backing disk path
char* backing_disk_path = NULL; char* backing_disk_path = NULL;
if (backing_identifier != NULL) { if (backing_identifier != NULL) {
status = GetBackingDiskPath(backing_identifier, &backing_disk_path); status = GetBackingPath(backing_identifier, &backing_disk_path);
if (status != SUCCESS) if (status != SUCCESS)
return status; return status;
} }

View File

@ -24,7 +24,15 @@ Command COMMANDS[] = {
"TODO: Add details."}, "TODO: Add details."},
{CommandClearEntries, "clear-entries", NULL, "Clears all the entries from the sandbox.", {CommandClearEntries, "clear-entries", NULL, "Clears all the entries from the sandbox.",
"TODO: Add details."}, "TODO: Add details."},
}; {},
{CommandAddDisk, "add-disk", "<entry id> [--root|-r <size>] [--backed|-b <backing id>]", "Adds a new disk to an entry.",
"TODO: Add details."},
{CommandRemoveDisk, "remove-disk", "<entry id>", "Removes the disk from an entry.",
"TODO: Add details."},
{CommandResetDisk, "reset-disk", "<entry id> [--update|-u] [--backing|-b <backing id>]", "Resets the disk of an entry.",
"TODO: Add details."},
{CommandTrimDisk, "trim-disk", "<entry id>", "Trims the disk of an entry.",
"TODO: Add details."}};
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
struct passwd* pw = getpwnam(SANDBOX_USER); struct passwd* pw = getpwnam(SANDBOX_USER);
@ -149,11 +157,13 @@ int CommandAddEntry(int argc, char* argv[]) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
Status status = AddEntry(argv[0]); char* entry_identifier = argv[0];
if (status != SUCCESS)
return status;
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; return EXIT_SUCCESS;
} }
@ -166,11 +176,13 @@ int CommandRemoveEntry(int argc, char* argv[]) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
Status status = RemoveEntry(argv[0]); char* entry_identifier = argv[0];
if (status != SUCCESS)
return status;
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; return EXIT_SUCCESS;
} }
@ -183,7 +195,7 @@ int CommandListEntries(int argc, char* argv[]) {
char** entries = NULL; char** entries = NULL;
Status status = ListEntries(&entries); Status status = ListEntries(&entries);
if (status != SUCCESS) if (status != SUCCESS)
return status; return EXIT_FAILURE;
for (size_t i = 0; entries[i] != NULL; i++) for (size_t i = 0; entries[i] != NULL; i++)
fprintf(stdout, "%s\n", entries[i]); fprintf(stdout, "%s\n", entries[i]);
@ -203,8 +215,152 @@ int CommandClearEntries(int argc, char* argv[]) {
Status status = ClearEntries(); Status status = ClearEntries();
if (status != SUCCESS) if (status != SUCCESS)
return status; return EXIT_FAILURE;
Log(LOG_LEVEL_INFO, "Cleared all entries."); Log(LOG_LEVEL_INFO, "Cleared all entries.");
return EXIT_SUCCESS; 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 <size>'.");
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 <backing id>'.");
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;
}

View File

@ -21,3 +21,8 @@ int CommandAddEntry(int argc, char* argv[]);
int CommandRemoveEntry(int argc, char* argv[]); int CommandRemoveEntry(int argc, char* argv[]);
int CommandListEntries(int argc, char* argv[]); int CommandListEntries(int argc, char* argv[]);
int CommandClearEntries(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[]);

View File

@ -11,14 +11,14 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/types.h> #include <sys/types.h>
LogLevel log_level = LOG_LEVEL_INFO; LogLevel LOG_LEVEL = LOG_LEVEL_INFO;
void SetLogLevel(LogLevel level) { void SetLogLevel(LogLevel level) {
log_level = level; LOG_LEVEL = level;
} }
void Log(LogLevel level, const char* format, ...) { void Log(LogLevel level, const char* format, ...) {
if (level < log_level) if (level < LOG_LEVEL)
return; return;
va_list args; va_list args;
@ -92,6 +92,67 @@ Status Format(char** _string, const char* fmt, ...) {
return SUCCESS; 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, ...) { Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) {
if (_exit_code != NULL) if (_exit_code != NULL)
*_exit_code = -1; *_exit_code = -1;

View File

@ -15,13 +15,16 @@ typedef enum {
LOG_LEVEL_ERROR, LOG_LEVEL_ERROR,
} LogLevel; } LogLevel;
extern LogLevel log_level; extern LogLevel LOG_LEVEL;
void SetLogLevel(LogLevel level); void SetLogLevel(LogLevel level);
void Log(LogLevel level, const char* format, ...); void Log(LogLevel level, const char* format, ...);
Status Format(char** _string, const char* fmt, ...); 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 RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...);
Status ReadFileDescriptor(int fd, char** _content); Status ReadFileDescriptor(int fd, char** _content);