diff --git a/src/backing.h b/src/backing.h deleted file mode 100644 index 2a5afaa..0000000 --- a/src/backing.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "utils.h" -#include "config.h" - -/// @brief Checks whether the specified backing identifier is valid. This call returns a failure if the identifier is invalid. -/// @param backing The backing identifier to check. -/// @return The result of the operation. -result_t check_backing_identifier(const char* backing); - -/// @brief Returns the path of the backing pool. -/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path. -/// @param config The program configuration to use. -/// @return The result of the operation. -result_t get_backing_pool_path(char** _path, const config_t* config); - -/// @brief Returns the path of the specified backing. -/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path. -/// @param config The program configuration to use. -/// @param backing The identifier of the backing to get the path of. -/// @return The result of the operation. -result_t get_backing_path(char** _path, const config_t* config, const char* backing); - -/// @brief Returns the path of the default disk file. -/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path. -/// @param config The program configuration to use. -/// @return The result of the operation. -result_t get_backing_default_path(char** _path, const config_t* config); - -/// @brief Returns the path of the temporary disk file. -/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the path. -/// @param config The program configuration to use. -/// @return The result of the operation. -result_t get_backing_temporary_path(char** _path, const config_t* config); - -/// @brief Checks whether the specified backing exists. This call returns a failure if the backing does not exist. -/// @param config The program configuration to use. -/// @param backing The identifier of the backing to check. -/// @return Whether the backing exists. -result_t check_backing_exists(const config_t* config, const char* backing); - -/// @brief Returns the default backing identifier. -/// @param _backing The pointer to where the backing should be stored. The caller is responsible for freeing the backing. -/// @param config The program configuration to use. -/// @return The result of the operation. -result_t get_default_backing(char** _backing, const config_t* config); - -/// @brief Sets the default backing identifier. -/// @param config The program configuration to use. -/// @param backing The identifier of the backing to set as default. -/// @return The result of the operation. -result_t set_default_backing(const config_t* config, const char* backing); - -/// @brief Adds a new backing to the backing pool from the specified container. -/// @param _backing The pointer to where the backing identifier should be stored. The caller is responsible for freeing the backing. -/// @param config The program configuration to use. -/// @param container The identifier of the container to add the backing from. -/// @return The result of the operation. -result_t add_backing(char** _backing, const config_t* config, const char* container); - -/// @brief Removes the specified backing from the backing pool. -/// @param config The program configuration to use. -/// @param backing The identifier of the backing to remove. -/// @return The result of the operation. -result_t remove_backing(const config_t* config, const char* backing); - -/// @brief Lists the backings in the backing pool, and returns the result in a null-terminated array of strings. -/// @param _backings The pointer to where the backings array should be stored. The caller is responsible for freeing the backings array as well as the strings in the array. -/// @param config The program configuration to use. -/// @return The result of the operation. -result_t list_backings(char*** _backings, const config_t* config); - -/// @brief Executes the syncronization script. -/// @param config The program configuration to use. -/// @return The result of the operation. -result_t sync_backing(const config_t* config); \ No newline at end of file diff --git a/src/config.c b/src/config.c index 072d272..11eddfc 100644 --- a/src/config.c +++ b/src/config.c @@ -67,10 +67,10 @@ result_t init_config(config_t** _config) { free(config); } - // Get the backing pool path. - json_object* backing_pool; - if (!json_object_object_get_ex(root, "backing-pool", &backing_pool)) { - result = failure("Failed to get the backing pool path from the configuration JSON."); + // Get the image pool path. + json_object* image_pool; + if (!json_object_object_get_ex(root, "image-pool", &image_pool)) { + result = failure("Failed to get the image pool path from the configuration JSON."); verbose("Exception -> %s", last_error()); json_object_put(root); @@ -79,10 +79,10 @@ result_t init_config(config_t** _config) { return result; } - // Get the backing pool path string. - const char* backing_pool_string = json_object_get_string(backing_pool); - if (backing_pool_string == NULL) { - result = failure("Failed to parse the backing pool path from the configuration JSON."); + // Get the image pool path string. + const char* image_pool_string = json_object_get_string(image_pool); + if (image_pool_string == NULL) { + result = failure("Failed to parse the image pool path from the configuration JSON."); verbose("Exception -> %s", last_error()); json_object_put(root); @@ -102,10 +102,10 @@ result_t init_config(config_t** _config) { } verbose("Configuration -> Container pool: %s", config->container_pool); - // Set the backing pool path in the configuration. - config->backing_pool = strdup(backing_pool_string); - if (config->backing_pool == NULL) { - result = failure("Failed to allocate memory for the backing pool path."); + // Set the image pool path in the configuration. + config->image_pool = strdup(image_pool_string); + if (config->image_pool == NULL) { + result = failure("Failed to allocate memory for the image pool path."); verbose("Exception -> %s", last_error()); json_object_put(root); @@ -114,7 +114,7 @@ result_t init_config(config_t** _config) { return result; } - verbose("Configuration -> Backing pool: %s", config->backing_pool); + verbose("Configuration -> image pool: %s", config->image_pool); // Set the configuration if requested. if (_config != NULL) @@ -130,7 +130,7 @@ result_t init_config(config_t** _config) { void free_config(config_t* config) { free(config->container_pool); - free(config->backing_pool); + free(config->image_pool); free(config); } diff --git a/src/config.h b/src/config.h index 13d56c5..4f366c4 100644 --- a/src/config.h +++ b/src/config.h @@ -8,8 +8,8 @@ typedef struct { /// @brief The path of the directory where the containers are stored. char* container_pool; - /// @brief The path of the directory where the backings are stored. - char* backing_pool; + /// @brief The path of the directory where the images are stored. + char* image_pool; } config_t; /// @brief Initializes the program configuration. diff --git a/src/container.c b/src/container.c index 50d8fc6..db28417 100644 --- a/src/container.c +++ b/src/container.c @@ -1,5 +1,5 @@ #include "container.h" - +#include "image.h" #include "disk.h" #include @@ -71,11 +71,12 @@ result_t check_container_exists(bool* _exists, const config_t* config, const cha struct stat st; if (stat(path, &st) == -1) { if (errno == ENOENT) { - verbose("The container '%s' does not exist.", identifier); + free(path); + // The container does not exist. *_exists = false; - free(path); + verbose("The container '%s' does not exist.", identifier); return success(); } @@ -223,6 +224,71 @@ result_t add_root_container(const config_t* config, const char* identifier, size return success(); } +result_t add_backed_container(const config_t* config, const char* identifier, const char* image) { + result_t result; + + // Check that the container does not already exist. + bool exists; + result = check_container_exists(&exists, config, identifier); + if (result != success()) { + result = failure("Failed to check whether the container exists."); + verbose("Exception -> %s", last_error()); + + return result; + } + + if (exists) { + result = failure("The specified container already exists."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Get the container path. + char* path; + result = get_container_path(&path, config, identifier); + if (result != success()) { + result = failure("Failed to get the container path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Get the image path. + char* image_path; + result = get_image_path(&image_path, config, image); + if (result != success()) { + result = failure("Failed to get the image path."); + verbose("Exception -> %s", last_error()); + + free(path); + + return result; + } + + // Create the container disk. + result = create_backed_disk(path, image_path); + if (result != success()) { + result = failure("Failed to create the container disk."); + verbose("Exception -> %s", last_error()); + + free(image_path); + free(path); + + return result; + } + + // Free the image path. + free(image_path); + + // Free the container path. + free(path); + + verbose("Successfully added backed container '%s' to the container pool, backed by the image '%s'.", identifier, image); + + return success(); +} + result_t remove_container(const config_t* config, const char* identifier) { result_t result; diff --git a/src/container.h b/src/container.h index 1502c5c..62bc7ec 100644 --- a/src/container.h +++ b/src/container.h @@ -38,12 +38,12 @@ result_t get_container_path(char** _path, const config_t* config, const char* id /// @return The result of the operation. result_t add_root_container(const config_t* config, const char* identifier, size_t size); -/// @brief Adds a new backed container to the pool, with the specified identifier and backing. +/// @brief Adds a new backed container to the pool, with the specified identifier and image. /// @param config The program configuration to use. /// @param identifier The identifier of the container to add. -/// @param backing The identifier of the backing to use. +/// @param image The identifier of the image to use. /// @return The result of the operation. -result_t add_backed_container(const config_t* config, const char* identifier, const char* backing); +result_t add_backed_container(const config_t* config, const char* identifier, const char* image); /// @brief Removes the specified container from the pool. /// @param config The program configuration to use. diff --git a/src/disk.c b/src/disk.c index 43cf9f6..f0db756 100644 --- a/src/disk.c +++ b/src/disk.c @@ -246,7 +246,7 @@ result_t create_backed_disk(const char* disk, const char* backing) { // char* stdout_buffer; char* stderr_buffer; - result = execute_file(&exit_code, NULL, NULL, &stderr_buffer, NULL, NULL, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-o", "backing_file", backing, disk, NULL); + result = execute_file(&exit_code, NULL, NULL, &stderr_buffer, NULL, NULL, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing, disk, NULL); if (result != success()) { result = failure("Failed to execute qemu-img create on the backed disk '%s'.", disk); diff --git a/src/image.c b/src/image.c new file mode 100644 index 0000000..7f1cd76 --- /dev/null +++ b/src/image.c @@ -0,0 +1,554 @@ +#include "image.h" +#include "container.h" +#include "disk.h" + +#include +#include +#include +#include +#include +#include + +result_t check_image_identifier(const char* identifier) { + result_t result; + + // Check that the identifier is not null. + if (identifier == NULL) { + result = failure("The specified image identifier is null."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Check that the identifier is the length of a md5 hash. + size_t length = strlen(identifier); + if (length != 32) { + result = failure("The specified image identifier is not the length of a md5 hash."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Check that the identifier contains only hexadecimal characters. + for (size_t i = 0; i < length; i++) { + if (isxdigit(identifier[i])) + continue; + + result = failure("The specified image identifier contains an invalid character '%c' at index %zu.", identifier[i], i); + verbose("Exception -> %s", last_error()); + + return result; + } + + return success(); +} + +result_t check_image_exists(bool* _exists, const config_t* config, const char* identifier) { + result_t result; + + // Get the image path. + char* path; + result = get_image_path(&path, config, identifier); + if (result != success()) { + result = failure("Failed to get the image path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Check that the image exists. + struct stat st; + if (stat(path, &st) == -1) { + if (errno == ENOENT) { + free(path); + + // The image does not exist. + *_exists = false; + + verbose("The image '%s' does not exist.", identifier); + + return success(); + } + + result = failure("Failed to check whether the image exists."); + verbose("Exception -> %s", last_error()); + + free(path); + + return result; + } + + free(path); + + // Check that the image is a regular file. + if (!S_ISREG(st.st_mode)) { + result = failure("The specified image path is not a regular file."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // The image exists and is a regular file. + *_exists = true; + + verbose("The image '%s' exists.", identifier); + + return success(); +} + +result_t get_image_pool_path(char** _path, const config_t* config) { + result_t result; + + // Check that the configuration is not null. + if (config == NULL) { + result = failure("The specified program configuration is null."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Create a copy of the image pool path. + char* path = strdup(config->image_pool); + if (path == NULL) { + result = failure("Failed to copy the image pool path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Set the image pool path. + *_path = path; + + return success(); +} + +result_t get_image_path(char** _path, const config_t* config, const char* identifier) { + result_t result; + + // Check that the identifier is valid. + result = check_image_identifier(identifier); + if (result != success()) { + result = failure("The specified image identifier is invalid."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Get the image pool path. + char* image_pool; + result = get_image_pool_path(&image_pool, config); + if (result != success()) { + result = failure("Failed to get the image pool path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Append the identifier to the image pool path. + char* path; + if (asprintf(&path, "%s/%s", image_pool, identifier) == -1) { + result = failure("Failed to allocate memory for the image path."); + verbose("Exception -> %s", last_error()); + + free(image_pool); + + return result; + } + + // Free the image pool path. + free(image_pool); + + // Set the image path. + *_path = path; + + return success(); +} + +result_t get_default_image_path(char** _path, const config_t* config) { + result_t result; + + // Get the image pool path. + char* image_pool; + result = get_image_pool_path(&image_pool, config); + if (result != success()) { + result = failure("Failed to get the image pool path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Append the default image identifier to the image pool path. + char* path; + if (asprintf(&path, "%s/default", image_pool) == -1) { + result = failure("Failed to allocate memory for the default image path."); + verbose("Exception -> %s", last_error()); + + free(image_pool); + + return result; + } + + // Free the image pool path. + free(image_pool); + + // Set the default image path. + *_path = path; + + return success(); +} + +result_t get_temporary_image_path(char** _path, const config_t* config) { + result_t result; + + // Get the image pool path. + char* image_pool; + result = get_image_pool_path(&image_pool, config); + if (result != success()) { + result = failure("Failed to get the image pool path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Append the temporary image identifier to the image pool path. + char* path; + if (asprintf(&path, "%s/temporary", image_pool) == -1) { + result = failure("Failed to allocate memory for the temporary image path."); + verbose("Exception -> %s", last_error()); + + free(image_pool); + + return result; + } + + // Free the image pool path. + free(image_pool); + + // Set the temporary image path. + *_path = path; + + return success(); +} + +result_t get_default_image(char** _identifier, const config_t* config) { + result_t result; + + // Get the default image path. + char* path; + result = get_default_image_path(&path, config); + if (result != success()) { + result = failure("Failed to get the default image path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Open the default image file. + char* identifier = NULL; + result = read_file(&identifier, NULL, path); + if (result != success()) { + result = failure("Failed to read the default image file."); + verbose("Exception -> %s", last_error()); + + free(path); + + return result; + } + + // Free the default image path. + free(path); + + // Check that the identifier is valid. + result = check_image_identifier(identifier); + if (result != success()) { + result = failure("The default image identifier is invalid. The default image file may be corrupted."); + verbose("Exception -> %s", last_error()); + + free(identifier); + + return result; + } + + // Set the default image identifier. + *_identifier = identifier; + + return result; +} + +result_t set_default_image(const config_t* config, const char* identifier) { + result_t result; + + // Check that the identifier is valid. + result = check_image_identifier(identifier); + if (result != success()) { + result = failure("The specified image identifier is invalid."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Get the default image path. + char* path; + result = get_default_image_path(&path, config); + if (result != success()) { + result = failure("Failed to get the default image path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Write the identifier to the default image file. + result = write_file(path, identifier, strlen(identifier), 0644); + if (result != success()) { + result = failure("Failed to write the default image file."); + verbose("Exception -> %s", last_error()); + + free(path); + + return result; + } + + // Free the default image path. + free(path); + + return success(); +} + +result_t add_image(char** _identifier, const config_t* config, const char* container) { + result_t result; + + // Check that the container exists. + bool exists; + result = check_container_exists(&exists, config, container); + if (result != success()) { + result = failure("Failed to check whether the container exists."); + verbose("Exception -> %s", last_error()); + + return result; + } + + if (!exists) { + result = failure("The specified container does not exist."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Get the path of the container. + char* container_path; + result = get_container_path(&container_path, config, container); + if (result != success()) { + result = failure("Failed to get the path of the container."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Get the temporary image path. + char* temporary_path; + result = get_temporary_image_path(&temporary_path, config); + if (result != success()) { + result = failure("Failed to get the temporary image path."); + verbose("Exception -> %s", last_error()); + + free(container_path); + + return result; + } + + // Copy the container to the temporary image path. + if (copy_file(container_path, temporary_path, 0644) != 0) { + result = failure("Failed to copy the container to the temporary image path."); + verbose("Exception -> %s", last_error()); + + free(container_path); + free(temporary_path); + + return result; + } + + // Free the container path as it is no longer needed. + free(container_path); + + // Get disk information about the temporary image. + disk_info_t* info; + result = read_disk_info(&info, temporary_path); + if (result != success()) { + result = failure("Failed to read the disk information about the temporary image."); + verbose("Exception -> %s", last_error()); + + free(temporary_path); + + return result; + } + + // If the temporary image is a backed disk, then we need to reback it to a relative path. + if (info->backing_name != NULL) { + result = reback_disk(temporary_path, info->backing_name); + if (result != success()) { + result = failure("Failed to reback the temporary image."); + verbose("Exception -> %s", last_error()); + + remove(temporary_path); + free(temporary_path); + free_disk_info(info); + + return result; + } + } + + // Free the disk information as it is no longer needed. + free_disk_info(info); + + // Calculate the md5 hash of the temporary image, and use it as the identifier. + char* identifier; + result = md5_file(&identifier, temporary_path); + if (result != success()) { + result = failure("Failed to calculate the md5 hash of the temporary image."); + verbose("Exception -> %s", last_error()); + + remove(temporary_path); + free(temporary_path); + + return result; + } + + // Check if the image already exists. + bool image_exists; + result = check_image_exists(&image_exists, config, identifier); + if (result != success()) { + result = failure("Failed to check whether the image exists."); + verbose("Exception -> %s", last_error()); + + free(identifier); + remove(temporary_path); + free(temporary_path); + + return result; + } + + if (image_exists) { + result = failure("The image already exists."); + verbose("Exception -> %s", last_error()); + + free(identifier); + remove(temporary_path); + free(temporary_path); + + return result; + } + + // Get the image path. + char* image_path; + result = get_image_path(&image_path, config, identifier); + if (result != success()) { + result = failure("Failed to get the image path."); + verbose("Exception -> %s", last_error()); + + free(identifier); + remove(temporary_path); + free(temporary_path); + + return result; + } + + // Move the temporary image to the image pool. + if (rename(temporary_path, image_path) == -1) { + result = failure("Failed to move the temporary image to the image pool."); + verbose("Exception -> %s", last_error()); + + free(identifier); + free(image_path); + remove(temporary_path); + free(temporary_path); + + return result; + } + + // Free the temporary image path. + free(temporary_path); + + // Set the identifier of the image. + *_identifier = identifier; + + verbose("Successfully added the image '%s' from the container '%s'.", identifier, container); + + return success(); +} + +result_t remove_image(const config_t* config, const char* identifier) { + result_t result; + + // Get the image path. + char* path; + result = get_image_path(&path, config, identifier); + if (result != success()) { + result = failure("Failed to get the image path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + // Remove the image. + if (remove(path) == -1) { + result = failure("Failed to remove the image."); + verbose("Exception -> %s", last_error()); + + free(path); + + return result; + } + + // Free the image path. + free(path); + + verbose("Successfully removed the image '%s'.", identifier); + + return success(); +} + +result_t list_images(char*** _identifiers, size_t* _count, const config_t* config) { + result_t result; + + // Get the image pool path. + char* image_pool; + result = get_image_pool_path(&image_pool, config); + if (result != success()) { + result = failure("Failed to get the image pool path."); + verbose("Exception -> %s", last_error()); + + return result; + } + + char** identifiers; + size_t count; + result = list_files(&identifiers, &count, image_pool, check_image_identifier); + if (result != success()) { + result = failure("Failed to list the images in the pool."); + verbose("Exception -> %s", last_error()); + + free(image_pool); + + return result; + } + + // Free the image pool path. + free(image_pool); + + // Set the list of identifiers. + if (_identifiers != NULL) + *_identifiers = identifiers; + else { + for (size_t i = 0; i < count; i++) + free(identifiers[i]); + free(identifiers); + } + + // Set the count of identifiers. + if (_count != NULL) + *_count = count; + + verbose("The image pool contains %zu images.", count); + + return success(); +} diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..e40971f --- /dev/null +++ b/src/image.h @@ -0,0 +1,73 @@ +#pragma once + +#include "utils.h" +#include "config.h" + +/// @brief Checks whether the specified image identifier is valid. This call will return an error with a message describing the problem if the identifier is invalid. +/// @param identifier The identifier to check. +/// @return The result of the operation. +result_t check_image_identifier(const char* identifier); + +/// @brief Checks whether the specified image exists. +/// @param _exists The pointer to where the result should be stored. +/// @param config The program configuration to use. +/// @param identifier The identifier of the image to check. +/// @return The result of the operation. +result_t check_image_exists(bool* _exists, const config_t* config, const char* identifier); + +/// @brief Returns the path of the image pool directory. +/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the memory. +/// @param config The program configuration to use. +/// @return The result of the operation. +result_t get_image_pool_path(char** _path, const config_t* config); + +/// @brief Returns the path of the specified image. +/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the memory. +/// @param config The program configuration to use. +/// @param identifier The identifier of the image. +/// @return The result of the operation. +result_t get_image_path(char** _path, const config_t* config, const char* identifier); + +/// @brief Returns the path of the file containing the default image identifier. +/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the memory. +/// @param config The program configuration to use. +/// @return The result of the operation. +result_t get_default_image_path(char** _path, const config_t* config); + +/// @brief Returns the path of the temporary file used to store the disk before it is moved to the image pool. +/// @param _path The pointer to where the path should be stored. The caller is responsible for freeing the memory. +/// @param config The program configuration to use. +/// @return The result of the operation. +result_t get_temporary_image_path(char** _path, const config_t* config); + +/// @brief Returns the identifier of the default image. +/// @param _identifier The pointer to where the identifier should be stored. The caller is responsible for freeing the memory. +/// @param config The program configuration to use. +/// @return The result of the operation. +result_t get_default_image(char** _identifier, const config_t* config); + +/// @brief Sets the identifier of the default image. +/// @param config The program configuration to use. +/// @param identifier The identifier of the image to set as default. +/// @return The result of the operation. +result_t set_default_image(const config_t* config, const char* identifier); + +/// @brief Adds a new image to the pool, using the specified container as the source. +/// @param _identifier The pointer to where the identifier should be stored. The caller is responsible for freeing the memory. +/// @param config The program configuration to use. +/// @param container The identifier of the container to use as the source. +/// @return The result of the operation. +result_t add_image(char** _identifier, const config_t* config, const char* container); + +/// @brief Removes the specified image from the pool. +/// @param config The program configuration to use. +/// @param identifier The identifier of the image to remove. +/// @return The result of the operation. +result_t remove_image(const config_t* config, const char* identifier); + +/// @brief Lists the images in the pool. +/// @param _identifiers The pointer to where the list of identifiers should be stored. The caller is responsible for freeing the strings and the array. +/// @param _count The pointer to where the count of identifiers should be stored. +/// @param config The program configuration to use. +/// @return The result of the operation. +result_t list_images(char*** _identifiers, size_t* _count, const config_t* config); \ No newline at end of file diff --git a/src/sandbox.c b/src/sandbox.c index 181c56c..1e1de23 100644 --- a/src/sandbox.c +++ b/src/sandbox.c @@ -2,6 +2,7 @@ #include "disk.h" #include "config.h" #include "container.h" +#include "image.h" #include #include @@ -12,5 +13,16 @@ int main(int argc, char* argv[]) { if (result != success()) return -1; + add_root_container(config, "test", 1024ULL); + + char* image; + add_image(&image, config, "test"); + + printf("Image: %s\n", image); + + add_backed_container(config, "test2", image); + + add_image(&image, config, "test2"); + free_config(config); } \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 2763a80..75cc7b6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -291,7 +291,7 @@ result_t read_fd(char** _buffer, size_t* _size, int fd) { // If an error occurred, return it. if (bytes_read == -1) { - result = failure("Failed to read the file descriptor (%s).", strerror(errno)); + result = failure("Failed to read the file descriptor %d (%s).", fd, strerror(errno)); verbose("Exception -> %s", last_error()); free(buffer); @@ -320,7 +320,7 @@ result_t write_fd(int fd, const char* buffer, size_t size) { // Write the buffer to the file descriptor. ssize_t bytes_written = write(fd, buffer, size); if (bytes_written == -1) { - result = failure("Failed to write to the file descriptor (%s).", strerror(errno)); + result = failure("Failed to write to the file descriptor %d (%s).", fd, strerror(errno)); verbose("Exception -> %s", last_error()); return result; @@ -405,7 +405,7 @@ result_t copy_fd(int source_fd, int destination_fd) { while (bytes_written < bytes_read) { ssize_t written = write(destination_fd, buffer + bytes_written, bytes_read - bytes_written); if (written == -1) { - result = failure("Failed to write to the file descriptor (%s).", strerror(errno)); + result = failure("Failed to write to the file descriptor %d (%s).", destination_fd, strerror(errno)); verbose("Exception -> %s", last_error()); return result; @@ -417,7 +417,7 @@ result_t copy_fd(int source_fd, int destination_fd) { // If an error occurred, return it. if (bytes_read == -1) { - result = failure("Failed to read the file descriptor (%s).", strerror(errno)); + result = failure("Failed to read the file descriptor %d (%s).", source_fd, strerror(errno)); verbose("Exception -> %s", last_error()); return result; @@ -765,7 +765,7 @@ result_t md5_fd(char** _hash, int fd) { // Set the hash. *_hash = md5; - verbose("Calculated MD5 hash '%s' from file descriptor %d (%zd bytes).", md5, fd, total_bytes_read); + verbose("Successfully calculated MD5 hash '%s' from file descriptor %d (%zd bytes).", md5, fd, total_bytes_read); return success(); }