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