555 lines
13 KiB
C
555 lines
13 KiB
C
|
#include "image.h"
|
||
|
#include "container.h"
|
||
|
#include "disk.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <errno.h>
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
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();
|
||
|
}
|