linuxinstall/src/image.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();
}