diff --git a/.clang-format b/.clang-format new file mode 100755 index 0000000..3123b51 --- /dev/null +++ b/.clang-format @@ -0,0 +1,14 @@ +--- +BasedOnStyle: LLVM +AlignOperands: 'true' +IndentWidth: '4' +IndentWrappedFunctionNames: 'true' +KeepEmptyLinesAtTheStartOfBlocks: 'false' +TabWidth: '4' +UseTab: Always +ColumnLimit: '0' +PointerAlignment: Left +BreakConstructorInitializers: BeforeComma +AccessModifierOffset: -4 +SortIncludes: 'false' +SortUsingDeclarations: 'false' diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/Makefile b/Makefile old mode 100644 new mode 100755 index 9756b41..64c1e09 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # ---- ---- # -CC = clang -CF = -Wall -lkrb5 -lvirt -ljson-c -g +CC = gcc +CF = -Wall -lkrb5 -lvirt -ljson-c -lcrypto -g # ---- ---- # diff --git a/src/backing.c b/src/backing.c new file mode 100755 index 0000000..22272bf --- /dev/null +++ b/src/backing.c @@ -0,0 +1,221 @@ +#include "backing.h" + +#include "container.h" +#include "disk.h" + +#include +#include +#include +#include +#include +#include +#include + +result_t check_backing_identifier(const char* backing) { + // Check that the identifier is not null + if (backing == NULL) + return failure("Backing identifier cannot be null."); + + // Check that the identifier is the length of a md5 hash + size_t length = strlen(backing); + if (length != MD5_DIGEST_STRING_LENGTH - 1) // -1 because the string is null-terminated + return failure("Backing identifier must be %d characters long.", MD5_DIGEST_STRING_LENGTH - 1); + + // Check that the identifier only contains allowed characters + for (size_t i = 0; i < length; i++) { + char c = backing[i]; + if (c >= 'a' && c <= 'f') + continue; + if (c >= '0' && c <= '9') + continue; + + return failure("Backing identifier cannot contain the character '%c' at index %d.", c, i); + } + + return success(); +} + +result_t check_backing_exists(const char* backing) { + // Get the backing path + char* path; + result_t result = get_backing_path(&path, backing); + if (result != success()) + return result; + + // Check that the backing exists + struct stat st; + errno = 0; + if (stat(path, &st) != 0) { + if (errno == ENOENT) + result = failure("Backing '%s' does not exist.", backing); + else + result = failure("Failed to check if backing '%s' exists (%s).", backing, strerror(errno)); + + free(path); + return result; + } + + // Check that the backing is a file + if (!S_ISREG(st.st_mode)) { + result = failure("Backing '%s' is not a regular file.", backing); + free(path); + return result; + } + + // Free the backing path + free(path); + + return success(); +} + +result_t get_backing_pool_path(char** _path) { + return format(_path, "/var/lib/sandbox/backings"); +} + +result_t get_backing_path(char** _path, const char* backing) { + // Check the backing identifier + result_t result = check_backing_identifier(backing); + if (result != success()) + return result; + + // Get the backing pool path + char* pool_path; + result = get_backing_pool_path(&pool_path); + if (result != success()) + return result; + + // Format the backing path + result = format(_path, "%s/%s", pool_path, backing); + + free(pool_path); + return result; +} + +result_t get_backing_default_path(char** _path) { + // Get the backing pool path + char* pool_path; + result_t result = get_backing_pool_path(&pool_path); + if (result != success()) + return result; + + // Format the backing path + result = format(_path, "%s/default", pool_path); + + free(pool_path); + return result; +} + +result_t remove_backing(const char* backing) { + // Check that the backing exists + result_t result = check_backing_exists(backing); + if (result != success()) + return result; + + // Get the backing path + char* path; + result = get_backing_path(&path, backing); + if (result != success()) + return result; + + // Remove the backing + errno = 0; + remove(path); + + // Check for errors during the removal + if (errno != 0) { + free(path); + return failure("Failed to remove backing '%s' (%s).", backing, strerror(errno)); + } + + free(path); + return success(); +} + +bool backing_filter(const char* file) { + // Check that a backing with the same name exists + return check_backing_exists(file) == success(); +} + +result_t list_backings(char*** _backings) { + // Get the backing pool path + char* pool_path; + result_t result = get_backing_pool_path(&pool_path); + if (result != success()) + return result; + + // List the backings + result = list_files(_backings, pool_path, &backing_filter); + + free(pool_path); + return result; +} + +result_t get_default_backing(char** _backing) { + // Get the backing default path + char* path; + result_t result = get_backing_default_path(&path); + if (result != success()) + return result; + + char* backing; + + // Read the default backing + result = read_file(&backing, path); + + free(path); + + if (result != success()) + return failure("No default backing configured."); + + // Check that the backing is valid + result = check_backing_identifier(backing); + if (result != success()) { + free(backing); + return failure("Default backing '%s' is not a valid backing identifier.", backing); + } + + // Check that the backing exists + result = check_backing_exists(backing); + if (result != success()) { + free(backing); + return failure("Default backing '%s' does not exist.", backing); + } + + *_backing = backing; + return result; +} + +result_t set_default_backing(const char* backing) { + // Get the backing default path + char* path; + result_t result = get_backing_default_path(&path); + if (result != success()) + return result; + + // If the backing is null, remove the default backing file + if (backing == NULL) { + errno = 0; + remove(path); + free(path); + + if (errno == ENOENT) + return success(); + else if (errno != 0) + return failure("Failed to remove the default backing file (%s).", strerror(errno)); + + return success(); + } else { + // Check that the backing exists + result = check_backing_exists(backing); + if (result != success()) { + free(path); + return result; + } + + // Write the default backing + result = write_file(path, backing); + + free(path); + return result; + } +} diff --git a/src/backing.h b/src/backing.h new file mode 100755 index 0000000..61dfb2d --- /dev/null +++ b/src/backing.h @@ -0,0 +1,19 @@ +#pragma once + +#include "utils.h" + +result_t check_backing_identifier(const char* backing); +result_t check_backing_exists(const char* backing); + +result_t get_backing_pool_path(char** _path); +result_t get_backing_path(char** _path, const char* backing); +result_t get_backing_default_path(char** _path); + +result_t add_backing(const char* backing, const char* container); +result_t remove_backing(const char* backing); + +bool backing_filter(const char* file); +result_t list_backings(char*** _backings); + +result_t get_default_backing(char** _backing); +result_t set_default_backing(const char* backing); \ No newline at end of file diff --git a/src/container.c b/src/container.c new file mode 100755 index 0000000..7f3bc1c --- /dev/null +++ b/src/container.c @@ -0,0 +1,222 @@ +#include "container.h" + +#include "disk.h" +#include "backing.h" + +#include +#include +#include +#include +#include + +result_t check_container_identifier(const char* container) { + // Check that the container identifier is not null + if (container == NULL) + return failure("Container identifier cannot be null."); + + // Check that the container identifier is not empty, nor too long + size_t length = strlen(container); + if (length == 0) + return failure("Container identifier cannot be empty."); + if (length > CONTAINER_IDENTIFIER_MAX_LENGTH) + return failure("Container identifier cannot be longer than %d characters.", CONTAINER_IDENTIFIER_MAX_LENGTH); + + // Check that the container identifier only contains allowed characters + for (size_t i = 0; i < length; i++) { + char c = container[i]; + if (c >= 'a' && c <= 'z') + continue; + if (c >= 'A' && c <= 'Z') + continue; + if (c >= '0' && c <= '9') + continue; + if ((c == '-' || c == '_' || c == '.') && i > 0) // Not at the beginning + continue; + + return failure("Container identifier cannot contain the character '%c' at index %d.", c, i); + } + + return success(); +} + +result_t check_container_exists(const char* container) { + // Get the container path + char* path; + result_t result = get_container_path(&path, container); + if (result != success()) + return result; + + // Check that the container exists + struct stat st; + errno = 0; + if (stat(path, &st) != 0) { + if (errno == ENOENT) + result = failure("Container '%s' does not exist.", container); + else + result = failure("Failed to check if container '%s' exists (%s).", container, strerror(errno)); + + free(path); + return result; + } + + // Check that the container is a file + if (!S_ISREG(st.st_mode)) { + result = failure("Container '%s' is not a file.", container); + free(path); + return result; + } + + free(path); + return success(); +} + +result_t get_container_pool_path(char** _path) { + return format(_path, "/var/lib/sandbox/containers"); +} + +result_t get_container_path(char** _path, const char* container) { + // Check the container identifier + result_t result = check_container_identifier(container); + if (result != success()) + return result; + + // Get the container pool path + char* pool_path; + result = get_container_pool_path(&pool_path); + if (result != success()) + return result; + + // Format the container path + result = format(_path, "%s/%s", pool_path, container); + + free(pool_path); + return result; +} + +result_t add_root_container(const char* container, uint64_t size) { + // Check that the container does not already exist + result_t result = check_container_exists(container); + if (result == success()) + return failure("Container '%s' already exists.", container); + + // Get the container path + char* path; + result = get_container_path(&path, container); + if (result != success()) + return result; + + // Create the root container + result = create_root_disk(path, size); + + free(path); + return result; +} + +result_t add_backed_container(const char* container, const char* backing) { + // Check that the container does not already exist + result_t result = check_container_exists(container); + if (result == success()) + return failure("Container '%s' already exists.", container); + + // Get the container path + char* path; + result = get_container_path(&path, container); + if (result != success()) + return result; + + // Get the backing path + char* backing_path; + result = get_backing_path(&backing_path, backing); + if (result != success()) { + free(path); + return result; + } + + // Create the backed container + result = create_backed_disk(path, backing_path); + + free(backing_path); + free(path); + return result; +} + +result_t remove_container(const char* container) { + // Check that the container exists + result_t result = check_container_exists(container); + if (result != success()) + return result; + + // Get the container path + char* path; + result = get_container_path(&path, container); + if (result != success()) + return result; + + // Delete the container + errno = 0; + remove(path); + + free(path); + + if (errno != 0) + return failure("Failed to remove container '%s' (%s).", container, strerror(errno)); + + return success(); +} + +result_t trim_container(const char* container) { + // Check that the container exists + result_t result = check_container_exists(container); + if (result != success()) + return result; + + // Get the container path + char* path; + result = get_container_path(&path, container); + if (result != success()) + return result; + + // Trim the container + result = trim_disk(path); + + free(path); + return result; +} + +result_t reset_container(const char* container) { + // Check that the container exists + result_t result = check_container_exists(container); + if (result != success()) + return result; + + // Get the container path + char* path; + result = get_container_path(&path, container); + if (result != success()) + return result; + + // Reset the container + result = reset_disk(path); + + free(path); + return result; +} + +bool container_filter(const char* file) { + // Check that a container with the same name exists + return check_container_exists(file) == success(); +} + +result_t list_containers(char*** _containers) { + // Get the container pool path + char* pool_path; + result_t result = get_container_pool_path(&pool_path); + if (result != success()) + return result; + + // List the files in the container pool + result = list_files(_containers, pool_path, container_filter); + + free(pool_path); + return result; +} \ No newline at end of file diff --git a/src/container.h b/src/container.h new file mode 100755 index 0000000..8271714 --- /dev/null +++ b/src/container.h @@ -0,0 +1,21 @@ +#pragma once + +#include "utils.h" + +#define CONTAINER_IDENTIFIER_MAX_LENGTH 64 + +result_t check_container_identifier(const char* container); +result_t check_container_exists(const char* container); + +result_t get_container_pool_path(char** _path); +result_t get_container_path(char** _path, const char* container); + +result_t add_root_container(const char* container, uint64_t size); +result_t add_backed_container(const char* container, const char* backing); +result_t remove_container(const char* container); + +result_t trim_container(const char* container); +result_t reset_container(const char* container); + +bool container_filter(const char* file); +result_t list_containers(char*** _containers); \ No newline at end of file diff --git a/src/disk.c b/src/disk.c old mode 100644 new mode 100755 index ca042a1..0bb0d11 --- a/src/disk.c +++ b/src/disk.c @@ -1,336 +1,301 @@ #include "disk.h" +#include #include #include -#include #include #include -#include -#include -#include -#include -#include -#include -Result CreateRootDisk(const char* disk_path, uint64_t size) { +result_t create_root_disk(const char* path, uint64_t size) { // Convert the size to a string char* size_str; - if (Format(&size_str, "%lu", size) == FAILURE) - return FAILURE; + result_t result = format(&size_str, "%llu", size); + if (result != success()) + return result; - // Create the root disk + // Create the disk int exit_code; - // char* stdoutb; No need to capture stdout - char* stderrb; + // char* stdoutbuf; + char* stderrbuf; - Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "create", "-f", "qcow2", disk_path, size_str, NULL); + result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "create", "-f", "qcow2", path, size_str, NULL); - // Free the size string + // Free the size string as it is no longer needed free(size_str); - // If the execution failed, return the error - if (result != SUCCESS) + // Check for errors during the execution + if (result != success()) return result; - // If the exit code is non-zero, return the error + // Check the exit code if (exit_code != 0) { - // 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 = ' '; + // Remove all newlines from the stderr buffer + for (char* c = stderrbuf; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; - Log(LOG_LEVEL_ERROR, "Failed to create root disk '%s' (%s).", disk_path, stderrb); - } - - // Free the error message - free(stderrb); - return FAILURE; + result = failure("Failed to create root disk (%s).", stderrbuf); + free(stderrbuf); + return result; } - // As there can still be an error message for a successful exit code, we need to free it - free(stderrb); - return SUCCESS; + // Free the stderr buffer + free(stderrbuf); + + return success(); } -Result CreateBackedDisk(const char* disk_path, const char* backing_disk_path) { - // Create the backed disk +result_t create_backed_disk(const char* path, const char* backing_path) { + // Create the disk int exit_code; - // char* stdoutb; No need to capture stdout - char* stderrb; + // char* stdoutbuf; + char* stderrbuf; - Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_disk_path, disk_path, NULL); + result_t result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_path, path, NULL); - // If the execution failed, return the error - if (result != SUCCESS) + // Check for errors during the execution + if (result != success()) return result; - // If the exit code is non-zero, return the error + // Check the exit code if (exit_code != 0) { - // 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 = ' '; + // Remove all newlines from the stderr buffer + for (char* c = stderrbuf; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; - 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; + result = failure("Failed to create backed disk (%s).", stderrbuf); + free(stderrbuf); + return result; } - // As there can still be an error message for a successful exit code, we need to free it - free(stderrb); - return SUCCESS; + // Free the stderr buffer + free(stderrbuf); + + return success(); } -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; +result_t trim_disk(const char* path) { + // Get the disk info + disk_info_t info; + result_t result = get_disk_info(&info, path); + if (result != success()) + return result; - // Get info about the disk - DiskInfo info; - if (GetDiskInfo(disk_path, &info) == FAILURE) { - free(trimmed_disk_path); - return FAILURE; + // Generate a new path for the temporary disk + char* temp_path; + result = format(&temp_path, "%s.tmp", path); + if (result != success()) { + free_disk_info(&info); + return result; } - // Trim the disk + // Use convert to trim the disk int exit_code; - // char* stdoutb; No need to capture stdout - char* stderrb; + // char* stdoutbuf; + char* stderrbuf; - // 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); - } + if (info.backing_path == NULL) + result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, temp_path, NULL); + else + result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-B", info.backing_path, path, temp_path, NULL); // Free the disk info - FreeDiskInfo(&info); + free_disk_info(&info); - // If the execution failed, return the error - if (result != SUCCESS) { - free(trimmed_disk_path); + // Check for errors during the execution + if (result != success()) { + free(temp_path); return result; } - // If the exit code is non-zero, return the error + // Check the exit code 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 = ' '; + // Remove all newlines from the stderr buffer + for (char* c = stderrbuf; *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; + result = failure("Failed to trim disk (%s).", stderrbuf); + free(temp_path); + free(stderrbuf); + return result; } - // As there can still be an error message for a successful exit code, we need to free it - free(stderrb); + // Free the stderr buffer as it is no longer needed + free(stderrbuf); - // 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; + // Replace the original disk with the temporary disk + errno = 0; + rename(temp_path, path); + + // Free the temporary path + free(temp_path); + + // Check for errors during the renaming + if (errno != 0) { + // Try to remove the temporary disk if the renaming failed + remove(temp_path); + return failure("Failed to replace the original disk with the temporary disk (%s).", strerror(errno)); } - // Free the temporary file path - free(trimmed_disk_path); - return SUCCESS; + return success(); } -Result RebackDisk(const char* disk_path, const char* backing_disk_path) { - // Reback the disk +result_t reset_disk(const char* path) { + // Get the disk info + disk_info_t info; + result_t result = get_disk_info(&info, path); + if (result != success()) + return result; + + if (info.backing_path == NULL) + // If the disk is not backed, simply create a new disk with the same size on top of it + result = create_root_disk(path, info.size); + else + // If the disk is backed, create a new disk with the same backing path + result = create_backed_disk(path, info.backing_path); + + // Free the disk info + free_disk_info(&info); + return result; +} + +result_t reback_disk(const char* path, const char* backing_path) { + // Create the disk int exit_code; - // char* stdoutb; No need to capture stdout - char* stderrb; + // char* stdoutbuf; + char* stderrbuf; - Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "rebase", "-f", "qcow2", "-F", "qcow2", "-u", "-b", backing_disk_path, disk_path, NULL); + result_t result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "rebase", "-u", "-f", "qcow2", "-F", "qcow2", "-b", backing_path, path, NULL); - // If the execution failed, return the error - if (result != SUCCESS) + // Check for errors during the execution + if (result != success()) return result; - // If the exit code is non-zero, return the error + // Check the exit code 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 = ' '; + // Remove all newlines from the stderr buffer + for (char* c = stderrbuf; *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; + result = failure("Failed to reback disk (%s).", stderrbuf); + free(stderrbuf); + return result; } - // As there can still be an error message for a successful exit code, we need to free it - free(stderrb); - return SUCCESS; + // Free the stderr buffer + free(stderrbuf); + + return success(); } -Result GetDiskInfo(const char* disk_path, DiskInfo* _info) { - // Initialize the disk info - *_info = (DiskInfo){0}; +result_t get_disk_info(disk_info_t* _info, const char* path) { + // Initialize the output parameters + _info->size = 0; + _info->allocated = 0; + _info->backing_path = NULL; // Get the disk info int exit_code; - char* stdoutb; - char* stderrb; + char* stdoutbuf; + char* stderrbuf; - Result result = RunExecutable(&exit_code, &stdoutb, &stderrb, "qemu-img", "info", "--output", "json", disk_path, NULL); + result_t result = execute(&exit_code, &stdoutbuf, &stderrbuf, "qemu-img", "info", "--output", "json", path, NULL); - // If the execution failed, return the error - if (result != SUCCESS) + // Check for errors during the execution + if (result != success()) return result; - // If the exit code is non-zero, return the error + // Check the exit code if (exit_code != 0) { - // 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 = ' '; + // Remove all newlines from the stderr buffer + for (char* c = stderrbuf; *c != '\0'; c++) + if (*c == '\n') + *c = ' '; - 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; + result = failure("Failed to get disk info (%s).", stderrbuf); + free(stdoutbuf); + free(stderrbuf); + return result; } - // No need for the error message anymore - free(stderrb); + // Free the stderr buffer as it is no longer needed + free(stderrbuf); // Parse the JSON output - json_object* root = json_tokener_parse(stdoutb); - if (root == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to parse JSON output for disk '%s'.", disk_path); - free(stdoutb); - return FAILURE; - } + json_object* root = json_tokener_parse(stdoutbuf); - // No need for the JSON string anymore - free(stdoutb); + // Free the stdout buffer as it is no longer needed + free(stdoutbuf); - // Get the size of the disk + // Check for errors during the parsing + if (root == NULL) + return failure("Failed to parse the JSON output."); + + // Get the size 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; + return failure("Missing 'virtual-size' field in the JSON output. Cannot get the disk size."); } errno = 0; - _info->size = json_object_get_int64(size_obj); + uint64_t 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; + return failure("Failed to parse the 'virtual-size' field in the JSON output (%s).", strerror(errno)); } - // 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); + // Get the allocated size + json_object* allocated_obj; + if (!json_object_object_get_ex(root, "actual-size", &allocated_obj)) { json_object_put(root); - FreeDiskInfo(_info); - return FAILURE; + return failure("Missing 'actual-size' field in the JSON output. Cannot get the allocated size."); } errno = 0; - _info->allocated = json_object_get_int64(allocation_obj); + uint64_t allocated = json_object_get_int64(allocated_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; + return failure("Failed to parse the 'actual-size' field in the JSON output (%s).", strerror(errno)); } - // 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); + // Get the backing file + json_object* backing_obj; + char* backing_path = NULL; + if (json_object_object_get_ex(root, "backing-filename", &backing_obj)) { + const char* backing_str = json_object_get_string(backing_obj); + if (backing_str == NULL) { json_object_put(root); - FreeDiskInfo(_info); - return FAILURE; + return failure("Failed to parse the 'backing-filename' field in the JSON output."); } - if (Duplicate(backing_file_path, &_info->backing_file_path) == FAILURE) { + // Duplicate the backing path string + backing_path = strdup(backing_str); + if (backing_path == NULL) { 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; + return failure("Failed to allocate memory for the backing path."); } } - // Free the JSON object + // Free the root object json_object_put(root); - return SUCCESS; + + // Write the disk info to the output + _info->size = size; + _info->allocated = allocated; + _info->backing_path = backing_path; + return success(); } -Result FreeDiskInfo(DiskInfo* info) { - free(info->backing_file_path); - free(info->backing_identifier); - return SUCCESS; +void free_disk_info(disk_info_t* _info) { + // Free the backing path string + free(_info->backing_path); + + // Clear the fields + _info->size = 0; + _info->allocated = 0; + _info->backing_path = NULL; } diff --git a/src/disk.h b/src/disk.h old mode 100644 new mode 100755 index 3bb71e8..f0ddc1f --- a/src/disk.h +++ b/src/disk.h @@ -5,14 +5,15 @@ typedef struct { uint64_t size; uint64_t allocated; - char* backing_file_path; - char* backing_identifier; -} DiskInfo; + char* backing_path; +} disk_info_t; -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); +result_t create_root_disk(const char* path, uint64_t size); +result_t create_backed_disk(const char* path, const char* backing_path); -Result GetDiskInfo(const char* disk_path, DiskInfo* _info); -Result FreeDiskInfo(DiskInfo* info); \ No newline at end of file +result_t trim_disk(const char* path); +result_t reset_disk(const char* path); +result_t reback_disk(const char* path, const char* backing_path); + +result_t get_disk_info(disk_info_t* _info, const char* path); +void free_disk_info(disk_info_t* _info); diff --git a/src/image.c b/src/image.c deleted file mode 100644 index 108dd36..0000000 --- a/src/image.c +++ /dev/null @@ -1,415 +0,0 @@ -#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 deleted file mode 100644 index fd992c9..0000000 --- a/src/image.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 old mode 100644 new mode 100755 index c76a531..c52a8d0 --- a/src/sandbox.c +++ b/src/sandbox.c @@ -2,268 +2,37 @@ #include "utils.h" +#include #include #include #include -Command COMMANDS[] = { - { - command_help, - {{"help", NULL}, NULL}, - NULL, - {{"command", "TODO: Add description.", false}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_version, - {{"version", NULL}, NULL}, - NULL, - NULL, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_config, - {{"config", NULL}, NULL}, - NULL, - NULL, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_add, - {{"container", "add", NULL}, NULL}, - { - { - {"-r", "--root", NULL}, - {{"size", "TODO: Add description.", true}, NULL}, - "TODO: Add description." - }, - { - {"-i", "--image", NULL}, - {{"image", "TODO: Add description.", true}, NULL}, - "TODO: Add description." - }, - NULL - }, - {{"container", "TODO: Add description.", true}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_remove, - {{"container", "rm", NULL}, {"container", "remove", NULL}, {"container", "del", NULL}, {"container", "delete", NULL}, NULL}, - NULL, - {{"container", "TODO: Add description.", true}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_reset, - {{"container", "reset", NULL}, NULL}, - NULL, - {{"container", "TODO: Add description.", true}, NULL}, - "Resets the container.", - "TODO: Add details." - }, - { - command_container_trim, - {{"container", "trim", NULL}, NULL}, - NULL, - {{"container", "TODO: Add description.", true}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_info, - {{"container", "info", NULL}, NULL}, - NULL, - {{"container", "TODO: Add description.", true}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_list, - {{"container", "ls", NULL}, {"container", "list", NULL}, NULL}, - NULL, - NULL, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_wipe, - {{"container", "wipe", NULL}, NULL}, - NULL, - NULL, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_start, - {{"container", "start", NULL}, NULL}, - { - { - {"-p", "--no-pci", NULL}, - NULL, - "TODO: Add description." - }, - { - {"-v", "--vnc", NULL}, - { - {"port", "TODO: Add description.", true}, - {"password", "TODO: Add description.", true}, - NULL - }, - "TODO: Add description." - }, - { - {"-i", "--iso", NULL}, - { - {"iso", "TODO: Add description.", true}, - NULL - }, - "TODO: Add description." - }, - NULL - }, - {{"container", "TODO: Add description.", true}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_stop, - {{"container", "stop", NULL}, NULL}, - { - { - {"-f", "--force", NULL}, - NULL, - "TODO: Add description." - }, - { - {"-t", "--timeout", NULL}, - { - {"timeout", "TODO: Add description.", true}, - NULL - }, - "TODO: Add description." - }, - NULL - }, - {{"container", "TODO: Add description.", true}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_container_ps, - {{"container", "ps", NULL}, NULL}, - NULL, - NULL, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_image_sync, - {{"image", "sync", NULL}, NULL}, - NULL, - NULL, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_image_add, - {{"image", "add", NULL}, NULL}, - NULL, - { - {"container", "The name of the container to add.", true}, - NULL - }, - "TODO: Add details.", - "TODO: Add details." - }, - { - command_image_import, - {{"image", "import", NULL}, NULL}, - NULL, - { - {"file", "TODO: Add description.", true}, - NULL - }, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_image_remove, - {{"image", "rm", NULL}, {"image", "remove", NULL}, NULL}, - NULL, - {{"image", "TODO: Add description.", true}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_image_default, - {{"image", "default", NULL}, NULL}, - NULL, - {{"image", "TODO: Add description.", false}, NULL}, - "TODO: Add description.", - "TODO: Add details." - }, - { - command_image_list, - {{"image", "ls", NULL}, {"image", "list", NULL}, NULL}, - NULL, - NULL, - "TODO: Add description.", - "TODO: Add details." - } -}; - int main(int argc, char** argv) { // Ensure the sandbox user exists struct passwd* pw = getpwnam(SANDBOX_USER); if (pw == NULL) { - Log(LOG_LEVEL_ERROR, "User '%s' does not exist. Please check that the program is installed correctly.", SANDBOX_USER); + fprintf(stderr, "User '%s' does not exist. Please check that the program is installed correctly.\n", SANDBOX_USER); return EXIT_FAILURE; } // 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); + fprintf(stderr, "This program must be run as root or as the user '%s'.\n", SANDBOX_USER); return EXIT_FAILURE; } // If the program is run as root, switch to the sandbox user if (geteuid() == 0) { if (setregid(pw->pw_gid, pw->pw_gid) != 0) { - Log(LOG_LEVEL_ERROR, "Failed to switch to the group '%s'.", pw->pw_name); + fprintf(stderr, "Failed to switch to the group '%s'.\n", pw->pw_name); return EXIT_FAILURE; } if (setreuid(pw->pw_uid, pw->pw_uid) != 0) { - Log(LOG_LEVEL_ERROR, "Failed to switch to the user '%s'.", pw->pw_name); + fprintf(stderr, "Failed to switch to the user '%s'.\n", pw->pw_name); return EXIT_FAILURE; } } // TODO: Parse commands from the command line -} - -int command_help(int argc, char** argv) { return EXIT_SUCCESS; } -int command_version(int argc, char** argv) { return EXIT_SUCCESS; } -int command_config(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_add(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_remove(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_reset(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_trim(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_info(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_list(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_wipe(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_start(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_stop(int argc, char** argv) { return EXIT_SUCCESS; } -int command_container_ps(int argc, char** argv) { return EXIT_SUCCESS; } -int command_image_sync(int argc, char** argv) { return EXIT_SUCCESS; } -int command_image_add(int argc, char** argv) { return EXIT_SUCCESS; } -int command_image_import(int argc, char** argv) { return EXIT_SUCCESS; } -int command_image_remove(int argc, char** argv) { return EXIT_SUCCESS; } -int command_image_default(int argc, char** argv) { return EXIT_SUCCESS; } -int command_image_list(int argc, char** argv) { return EXIT_SUCCESS; } +} \ No newline at end of file diff --git a/src/sandbox.h b/src/sandbox.h old mode 100644 new mode 100755 index ef6b820..422c737 --- a/src/sandbox.h +++ b/src/sandbox.h @@ -2,28 +2,7 @@ #include -#define SANDBOX_VERSION "0.1.2" +#define SANDBOX_VERSION "0.1.4" #define SANDBOX_USER "sandbox" -typedef struct { - const char* name; - const char* description; - const bool required; -} Argument; - -typedef struct { - const char** aliases; - const Argument* arguments; - const char* description; -} Option; - -typedef struct { - int (*handler)(int argc, char* argv[]); - const char*** aliases; - const Option* options; - const Argument* arguments; - const char* description; - const char* details; -} Command; - int main(int argc, char** argv); diff --git a/src/template.c b/src/template.c deleted file mode 100644 index 7ecc565..0000000 --- a/src/template.c +++ /dev/null @@ -1,353 +0,0 @@ -#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 deleted file mode 100644 index 7e96d85..0000000 --- a/src/template.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 old mode 100644 new mode 100755 index 9d72bf8..19a8a85 --- a/src/utils.c +++ b/src/utils.c @@ -3,200 +3,178 @@ #include #include #include -#include #include -#include +#include #include -#include #include -#include +#include +#include +#include +#include -LogLevel LOG_LEVEL = LOG_LEVEL_INFO; +char _error_buffer[ERROR_BUFFER_SIZE]; -void SetLogLevel(LogLevel level) { - LOG_LEVEL = level; +result_t success(void) { + return EXIT_SUCCESS; } -void Log(LogLevel level, const char* format, ...) { - if (level < LOG_LEVEL) - return; - +result_t failure(const char* format, ...) { va_list args; va_start(args, format); - const char* color; - const char* level_str; - // Set the color and level_str based on the log level - switch (level) { - case LOG_LEVEL_DEBUG: - color = "\033[0;90m"; - level_str = "DEBUG"; - break; - case LOG_LEVEL_INFO: - color = "\033[0;36m"; - level_str = "INFO"; - break; - case LOG_LEVEL_WARNING: - color = "\033[0;33m"; - level_str = "WARNING"; - break; - case LOG_LEVEL_ERROR: - color = "\033[0;31m"; - level_str = "ERROR"; - break; - } - - // Get the current time - time_t t = time(NULL); - struct tm* tm_info = localtime(&t); - - // Print the label, message and newline - fprintf(stderr, "%s[%02d/%02d/%02d %02d:%02d:%02d - %s]\033[m ", color, tm_info->tm_mday, tm_info->tm_mon + 1, (tm_info->tm_year + 1900) % 100, tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec, level_str); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - - // Flush the output - fflush(stderr); + // Print the error message into the error buffer + vsnprintf(_error_buffer, ERROR_BUFFER_SIZE, format, args); va_end(args); + + return EXIT_FAILURE; } -Result Format(char** _string, const char* fmt, ...) { - *_string = NULL; +const char* error(void) { + return _error_buffer; +} - va_list args; - va_start(args, fmt); +result_t format(char** _str, const char* format, ...) { + // Initialize the output parameters + *_str = NULL; // Calculate the length of the formatted string - size_t length = vsnprintf(NULL, 0, fmt, args) + 1; - va_end(args); - if (length <= 0) { - Log(LOG_LEVEL_ERROR, "Failed to calculate the length of the formatted string."); - return FAILURE; - } + va_list args; + va_start(args, format); - // Allocate a buffer for the formatted string - char* buffer = calloc(length, sizeof(char)); - if (buffer == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the formatted string (%s).", strerror(errno)); - return FAILURE; - } - - va_start(args, fmt); - - vsnprintf(buffer, length, fmt, args); + int length = vsnprintf(NULL, 0, format, args); va_end(args); - *_string = buffer; - return SUCCESS; + if (length < 0) + return failure("Failed to calculate the length of the formatted string."); + + // Allocate memory for the formatted string + errno = 0; + char* str = malloc(length + 1); + if (str == NULL) + return failure("Failed to allocate memory for the formatted string (%s).", strerror(errno)); + + // Format the string + va_start(args, format); + + if (vsnprintf(str, length + 1, format, args) < 0) + return failure("Failed to format the string."); + + va_end(args); + + // Return the formatted string + *_str = str; + return success(); } -Result Duplicate(const char* string, char** _duplicate) { - *_duplicate = NULL; +result_t substring(char** _str, const char* str, size_t start, size_t length) { + // Initialize the output parameters + *_str = NULL; - if (string == NULL) - return SUCCESS; + // Check the start index + size_t str_length = strlen(str); - *_duplicate = strdup(string); - if (*_duplicate == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to duplicate the string (%s).", strerror(errno)); - return FAILURE; - } + if (start > str_length) + return failure("The start index is out of range."); + if (start + length > str_length) + length = str_length - start; - return SUCCESS; + // Allocate memory for the substring + errno = 0; + char* substr = malloc(length + 1); // +1 for the null terminator + if (substr == NULL) + return failure("Failed to allocate memory for the substring (%s).", strerror(errno)); + + // Copy the substring + memcpy(substr, str + start, length); + substr[length] = '\0'; + + // Return the substring + *_str = substr; + 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 - 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"; - } +result_t format_size(char** _str, uint64_t size) { + // Initialize the output parameters + *_str = NULL; // Format the size - return Format(_size_str, "%.2f %s", value, unit); + if (size < 1024ULL) + return format(_str, "%lluB", size); + if (size < 1024ULL * 1024) + return format(_str, "%.2fKiB", size / (1024.0)); + if (size < 1024ULL * 1024 * 1024) + return format(_str, "%.2fMiB", size / (1024.0 * 1024.0)); + if (size < 1024ULL * 1024 * 1024 * 1024) + return format(_str, "%.2fGiB", size / (1024.0 * 1024.0 * 1024.0)); + if (size < 1024ULL * 1024 * 1024 * 1024 * 1024) + return format(_str, "%.2fTiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0)); + if (size < 1024ULL * 1024 * 1024 * 1024 * 1024 * 1024) + return format(_str, "%.2fPiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)); + + return format(_str, "%.2fEiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)); } -Result ParseSize(const char* size_str, uint64_t* _size) { +result_t parse_size(uint64_t* _size, const char* str) { + // Initialize the output parameters *_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; - } + uint64_t size = strtoull(str, &endptr, 10); + if (endptr == str) + return failure("Failed to parse the size."); + // Check the suffix if (*endptr == '\0') { + // No suffix *_size = size; - return SUCCESS; + 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; - } + // Parse the suffix + if (endptr[1] != '\0') + return failure("Invalid size suffix."); - *_size = size; - return SUCCESS; + switch (endptr[0]) { + case 'B': + *_size = size; + return success(); + case 'K': + *_size = size * 1024; + return success(); + case 'M': + *_size = size * 1024 * 1024; + return success(); + case 'G': + *_size = size * 1024 * 1024 * 1024; + return success(); + case 'T': + *_size = size * 1024 * 1024 * 1024 * 1024; + return success(); + case 'P': + *_size = size * 1024 * 1024 * 1024 * 1024 * 1024; + return success(); + case 'E': + *_size = size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024; + return success(); + default: + return failure("Invalid size suffix."); + } } -Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) { +result_t execute(int* _exit_code, char** _stdoutbuf, char** _stderrbuf, const char* executable, ...) { + // Initialize the output parameters if (_exit_code != NULL) - *_exit_code = -1; - if (_stdout != NULL) - *_stdout = NULL; - if (_stderr != NULL) - *_stderr = NULL; + *_exit_code = 0; + if (_stdoutbuf != NULL) + *_stdoutbuf = NULL; + if (_stderrbuf != NULL) + *_stderrbuf = NULL; - // Count the number of arguments - int argc = 1; // The first argument is the executable itself + // Count the number of arguments to pass to the executable + int argc = 1; // +1 for the executable itself va_list args; va_start(args, executable); @@ -206,34 +184,31 @@ Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char va_end(args); - // Allocate an array for the arguments - char** argv = calloc(argc + 1, sizeof(char*)); // +1 for the NULL terminator - if (argv == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the arguments array (%s).", strerror(errno)); - return FAILURE; - } + // Allocate memory for the arguments array + errno = 0; + char** argv = malloc((argc + 1) * sizeof(char*)); // +1 for the NULL terminator + if (argv == NULL) + return failure("Failed to allocate memory for the arguments array (%s).", strerror(errno)); // Fill the arguments array + + errno = 0; argv[0] = strdup(executable); if (argv[0] == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to duplicate the executable path (%s).", strerror(errno)); free(argv); - return FAILURE; + return failure("Failed to duplicate the executable string (%s).", strerror(errno)); } va_start(args, executable); - // Duplicate the arguments and store them in the array for (int i = 1; i < argc; i++) { argv[i] = strdup(va_arg(args, const char*)); if (argv[i] == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to duplicate the argument n°%d (%s).", i, strerror(errno)); - for (int j = 0; j < i; j++) free(argv[j]); free(argv); - - return FAILURE; + va_end(args); + return failure("Failed to duplicate the argument string (%s).", strerror(errno)); } } @@ -241,229 +216,280 @@ Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char argv[argc] = NULL; - // Create pipes for the standard output and error + // Create pipes for the standard output and standard error of the child process int stdout_pipe[2]; - if (pipe(stdout_pipe) == -1) { - Log(LOG_LEVEL_ERROR, "Failed to create the standard output pipe (%s).", strerror(errno)); - + errno = 0; + if (pipe(stdout_pipe) < 0) { for (int i = 0; i < argc; i++) free(argv[i]); free(argv); - - return FAILURE; + return failure("Failed to create a pipe for the standard output of the child process (%s).", strerror(errno)); } int stderr_pipe[2]; - if (pipe(stderr_pipe) == -1) { - Log(LOG_LEVEL_ERROR, "Failed to create the standard error pipe (%s).", strerror(errno)); - + errno = 0; + if (pipe(stderr_pipe) < 0) { for (int i = 0; i < argc; i++) free(argv[i]); free(argv); - close(stdout_pipe[0]); close(stdout_pipe[1]); - - return FAILURE; + return failure("Failed to create a pipe for the standard error of the child process (%s).", strerror(errno)); } - // Fork the process + // Fork the child process + errno = 0; pid_t pid = fork(); - if (pid == -1) { - Log(LOG_LEVEL_ERROR, "Failed to fork the process (%s).", strerror(errno)); - + if (pid < 0) { for (int i = 0; i < argc; i++) free(argv[i]); free(argv); - close(stdout_pipe[0]); close(stdout_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); - - return FAILURE; + return failure("Failed to fork the child process (%s).", strerror(errno)); } if (pid == 0) { - // Redirect the standard output and error to the pipes - dup2(stdout_pipe[1], STDOUT_FILENO); - dup2(stderr_pipe[1], STDERR_FILENO); - - // Close the unused ends of the pipes + // Redirect the standard output and standard error of the child process close(stdout_pipe[0]); - close(stdout_pipe[1]); close(stderr_pipe[0]); + + if (dup2(stdout_pipe[1], STDOUT_FILENO) < 0) + exit(EXIT_FAILURE); + + if (dup2(stderr_pipe[1], STDERR_FILENO) < 0) + exit(EXIT_FAILURE); + + close(stdout_pipe[1]); close(stderr_pipe[1]); - // Execute the command - execv(executable, argv); + // Execute the child process + errno = 0; + execvp(executable, argv); - // If execv returns, it failed - fprintf(stderr, "Failed to execute the command (%s).\n", strerror(errno)); + // If the child process reaches this point, it failed to execute + fprintf(stderr, "Failed to execute the child process (%s).\n", strerror(errno)); exit(EXIT_FAILURE); } - // Close the unused ends of the pipes - close(stdout_pipe[1]); - close(stderr_pipe[1]); - - // Free the arguments + // Free the arguments array for (int i = 0; i < argc; i++) free(argv[i]); free(argv); + // Close the write ends of the pipes + close(stdout_pipe[1]); + close(stderr_pipe[1]); + // Wait for the child process to terminate int status; - waitpid(pid, &status, 0); + errno = 0; + if (waitpid(pid, &status, 0) < 0) { + close(stdout_pipe[0]); + close(stderr_pipe[0]); + return failure("Failed to wait for the child process to terminate (%s).", strerror(errno)); + } - // Read the standard output and error - if (_stdout != NULL) - ReadFileDescriptor(stdout_pipe[0], _stdout); + // Read the standard output and standard error of the child process + if (_exit_code != NULL) + *_exit_code = WEXITSTATUS(status); + if (_stdoutbuf != NULL) + read_fd(_stdoutbuf, stdout_pipe[0]); + if (_stderrbuf != NULL) + read_fd(_stderrbuf, stderr_pipe[0]); - if (_stderr != NULL) - ReadFileDescriptor(stderr_pipe[0], _stderr); - - // Close the pipes + // Close the read ends of the pipes close(stdout_pipe[0]); close(stderr_pipe[0]); - // Set the exit code - if (_exit_code != NULL) - *_exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1; - - return SUCCESS; + return success(); } -Result ReadFileDescriptor(int fd, char** _content) { - *_content = NULL; +result_t read_fd(char** _str, int fd) { + // Initialize the output parameters + *_str = NULL; - // Allocate a buffer for the content - char buffer[4096]; + // Read the file descriptor size_t length = 0; + size_t capacity = 4096; - ssize_t bytes_read; - while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { - char* new_content = realloc(*_content, length + bytes_read + 1); // +1 for the NULL terminator - if (new_content == NULL) { - Log(LOG_LEVEL_ERROR, "Failed to reallocate memory for the file content (%s).", strerror(errno)); - free(*_content); - return FAILURE; - } - *_content = new_content; + // Allocate memory for the output string + errno = 0; + char* str = malloc(capacity + 1); // +1 for the null terminator + if (str == NULL) + return failure("Failed to allocate memory for the output string (%s).", strerror(errno)); - memcpy(*_content + length, buffer, bytes_read); - length += bytes_read; - - (*_content)[length] = '\0'; - } - - if (bytes_read == -1) { - Log(LOG_LEVEL_ERROR, "Failed to read from the file descriptor (%s).", strerror(errno)); - free(*_content); - return FAILURE; - } - - return SUCCESS; -} - -Result WriteFileDescriptor(int fd, const char* content) { - size_t length = strlen(content); - ssize_t bytes_written = 0; - - while (bytes_written < length) { - ssize_t result = write(fd, content + bytes_written, length - bytes_written); - if (result == -1) { - Log(LOG_LEVEL_ERROR, "Failed to write to the file descriptor (%s).", strerror(errno)); - return FAILURE; + ssize_t count; + while (1) { + // Read the file descriptor into the output string + errno = 0; + count = read(fd, str + length, capacity - length); + if (count < 0) { + free(str); + return failure("Failed to read the file descriptor %d (%s).", fd, strerror(errno)); } - bytes_written += result; + // Update the length of the output string + length += count; + if (count == 0) + break; + + // Reallocate memory for the output string if necessary + if (length >= capacity) { + capacity *= 2; + + errno = 0; + char* new_str = realloc(str, capacity + 1); // +1 for the null terminator + if (new_str == NULL) { + free(str); + return failure("Failed to reallocate memory for the output string (%s).", strerror(errno)); + } + str = new_str; + } } - return SUCCESS; + // Null-terminate the string + str[length] = '\0'; + + // Return the output string + *_str = str; + return success(); } -Result ReadFile(const char* path, char** _content) { - *_content = NULL; +result_t write_fd(int fd, const char* str) { + // Write the string to the file descriptor + size_t length = strlen(str); + size_t written = 0; + + while (written < length) { + // Try to write the entire string at once + errno = 0; + ssize_t count = write(fd, str + written, length - written); + if (count < 0) + return failure("Failed to write to the file descriptor %d (%s).", fd, strerror(errno)); + + written += count; + } + + return success(); +} + +result_t read_file(char** _str, const char* path) { + // Initialize the output parameters + *_str = NULL; // Open the file - int file = open(path, O_RDONLY); - if (file == -1) { - Log(LOG_LEVEL_ERROR, "Failed to open the file %s (%s).", path, strerror(errno)); - return FAILURE; - } + errno = 0; + int fd = open(path, O_RDONLY); + if (fd < 0) + return failure("Failed to open the file '%s' (%s).", path, strerror(errno)); // Read the file - Result status = ReadFileDescriptor(file, _content); + result_t result = read_fd(_str, fd); // Close the file - close(file); + close(fd); - return status; + return result; } -Result WriteFile(const char* path, const char* content) { +result_t write_file(const char* path, const char* str) { // Open the file - int file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (file == -1) { - Log(LOG_LEVEL_ERROR, "Failed to open the file %s (%s).", path, strerror(errno)); - return FAILURE; - } + errno = 0; + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + return failure("Failed to open the file '%s' (%s).", path, strerror(errno)); - // Write the content - Result status = WriteFileDescriptor(file, content); + // Write the string to the file + result_t result = write_fd(fd, str); // Close the file - close(file); + close(fd); - return status; + return result; } -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) { - Log(LOG_LEVEL_ERROR, "Failed to open the source file %s (%s).", source_path, strerror(errno)); - return FAILURE; +result_t list_files(char*** _files, const char* path, bool (*filter)(const char*)) { + // Initialize the output parameters + *_files = NULL; + + // Allocate memory for the files array + size_t length = 0; + size_t capacity = 64; + + errno = 0; + char** files = malloc(capacity * sizeof(char*)); + if (files == NULL) + return failure("Failed to allocate memory for the files array (%s).", strerror(errno)); + + // Open the directory + errno = 0; + DIR* dir = opendir(path); + if (dir == NULL) { + free(files); + return failure("Failed to open the directory '%s' (%s).", path, strerror(errno)); } - // Open the destination file - int destination_file = open(destination_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (destination_file == -1) { - Log(LOG_LEVEL_ERROR, "Failed to open the destination file %s (%s).", destination_path, strerror(errno)); - close(source_file); - return FAILURE; - } - - // Copy the file - char buffer[4096]; - ssize_t bytes_read; - while ((bytes_read = read(source_file, buffer, sizeof(buffer))) > 0) { - ssize_t bytes_written = 0; - while (bytes_written < bytes_read) { - ssize_t result = write(destination_file, buffer + bytes_written, bytes_read - bytes_written); - if (result == -1) { - Log(LOG_LEVEL_ERROR, "Failed to write to the destination file %s (%s).", destination_path, strerror(errno)); - close(source_file); - close(destination_file); - return FAILURE; + // Read the directory + struct dirent* entry; + while (1) { + // Read the next entry + errno = 0; + entry = readdir(dir); + if (entry == NULL) { + if (errno != 0) { + for (size_t i = 0; i < length; i++) + free(files[i]); + free(files); + closedir(dir); + return failure("Failed to read the directory '%s' (%s).", path, strerror(errno)); } + break; + } - bytes_written += result; + // Filter the entry + if (filter != NULL && !filter(entry->d_name)) + continue; + + // Add the entry to the files array + files[length] = strdup(entry->d_name); + if (files[length] == NULL) { + for (size_t i = 0; i < length; i++) + free(files[i]); + free(files); + closedir(dir); + return failure("Failed to allocate memory for the file name."); + } + + length++; + + // Reallocate memory for the files array if necessary + if (length > capacity) { + capacity *= 2; + + errno = 0; + char** new_files = realloc(files, capacity * sizeof(char*)); + if (new_files == NULL) { + for (size_t i = 0; i < length; i++) + free(files[i]); + free(files); + closedir(dir); + return failure("Failed to reallocate memory for the files array (%s).", strerror(errno)); + } + files = new_files; } } - if (bytes_read == -1) { - Log(LOG_LEVEL_ERROR, "Failed to read from the source file %s (%s).", source_path, strerror(errno)); - close(source_file); - close(destination_file); - return FAILURE; - } + // Close the directory + closedir(dir); - // Close the files - close(source_file); - close(destination_file); + // Null-terminate the files array + files[length] = NULL; - return SUCCESS; + // Return the files array + *_files = files; + return success(); } diff --git a/src/utils.h b/src/utils.h old mode 100644 new mode 100755 index 70e1cf9..1bbd2b3 --- a/src/utils.h +++ b/src/utils.h @@ -1,38 +1,30 @@ #pragma once +#include #include #include -#include -typedef enum { - SUCCESS, - FAILURE -} Result; +#define ERROR_BUFFER_SIZE 4096 -typedef enum { - LOG_LEVEL_DEBUG, - LOG_LEVEL_INFO, - LOG_LEVEL_WARNING, - LOG_LEVEL_ERROR, -} LogLevel; +extern char _error_buffer[]; -extern LogLevel LOG_LEVEL; +typedef int result_t; -void SetLogLevel(LogLevel level); -void Log(LogLevel level, const char* format, ...); +result_t success(void); +result_t failure(const char* format, ...); +const char* error(void); -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); +result_t format(char** _str, const char* format, ...); +result_t substring(char** _str, const char* str, size_t start, size_t length); -Result FormatSize(uint64_t size, char** _size_str); -Result ParseSize(const char* size_str, uint64_t* _size); +result_t format_size(char** _str, uint64_t size); +result_t parse_size(uint64_t* _size, const char* str); -Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...); +result_t execute(int* _exit_code, char** _stdoutbuf, char** _stderrbuf, const char* executable, ...); -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); +result_t read_fd(char** _str, int fd); +result_t write_fd(int fd, const char* str); +result_t read_file(char** _str, const char* path); +result_t write_file(const char* path, const char* str); -Result CopyFile(const char* source_path, const char* destination_path); +result_t list_files(char*** _files, const char* path, bool (*filter)(const char*));