Added the image system

This commit is contained in:
Alexei KADIR 2024-02-29 10:58:47 +01:00
parent 0373d159a2
commit b90502c4f2
10 changed files with 733 additions and 104 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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.

View File

@ -1,5 +1,5 @@
#include "container.h"
#include "image.h"
#include "disk.h"
#include <stdio.h>
@ -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;

View File

@ -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.

View File

@ -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);

554
src/image.c Normal file

File diff suppressed because it is too large Load Diff

73
src/image.h Normal file
View File

@ -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);

View File

@ -2,6 +2,7 @@
#include "disk.h"
#include "config.h"
#include "container.h"
#include "image.h"
#include <stdio.h>
#include <dirent.h>
@ -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);
}

View File

@ -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();
}