2024-02-17 01:34:58 +01:00
|
|
|
#include "disk.h"
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
#include <json-c/json.h>
|
2024-02-18 18:09:53 +01:00
|
|
|
#include <errno.h>
|
2024-02-17 23:59:38 +01:00
|
|
|
#include <stdio.h>
|
2024-02-17 01:34:58 +01:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t create_root_disk(const char* path, uint64_t size) {
|
2024-02-17 01:34:58 +01:00
|
|
|
// Convert the size to a string
|
|
|
|
char* size_str;
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t result = format(&size_str, "%llu", size);
|
|
|
|
if (result != success())
|
|
|
|
return result;
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Create the disk
|
2024-02-17 01:34:58 +01:00
|
|
|
int exit_code;
|
2024-02-19 16:01:53 +01:00
|
|
|
// char* stdoutbuf;
|
|
|
|
char* stderrbuf;
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "create", "-f", "qcow2", path, size_str, NULL);
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Free the size string as it is no longer needed
|
2024-02-17 23:59:38 +01:00
|
|
|
free(size_str);
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check for errors during the execution
|
|
|
|
if (result != success())
|
2024-02-18 18:09:53 +01:00
|
|
|
return result;
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check the exit code
|
2024-02-17 23:59:38 +01:00
|
|
|
if (exit_code != 0) {
|
2024-02-19 16:01:53 +01:00
|
|
|
// Remove all newlines from the stderr buffer
|
|
|
|
for (char* c = stderrbuf; *c != '\0'; c++)
|
|
|
|
if (*c == '\n')
|
|
|
|
*c = ' ';
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result = failure("Failed to create root disk (%s).", stderrbuf);
|
|
|
|
free(stderrbuf);
|
|
|
|
return result;
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Free the stderr buffer
|
|
|
|
free(stderrbuf);
|
|
|
|
|
|
|
|
return success();
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t create_backed_disk(const char* path, const char* backing_path) {
|
|
|
|
// Create the disk
|
2024-02-17 01:34:58 +01:00
|
|
|
int exit_code;
|
2024-02-19 16:01:53 +01:00
|
|
|
// char* stdoutbuf;
|
|
|
|
char* stderrbuf;
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_path, path, NULL);
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check for errors during the execution
|
|
|
|
if (result != success())
|
2024-02-18 18:09:53 +01:00
|
|
|
return result;
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check the exit code
|
2024-02-17 23:59:38 +01:00
|
|
|
if (exit_code != 0) {
|
2024-02-19 16:01:53 +01:00
|
|
|
// Remove all newlines from the stderr buffer
|
|
|
|
for (char* c = stderrbuf; *c != '\0'; c++)
|
|
|
|
if (*c == '\n')
|
|
|
|
*c = ' ';
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result = failure("Failed to create backed disk (%s).", stderrbuf);
|
|
|
|
free(stderrbuf);
|
|
|
|
return result;
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Free the stderr buffer
|
|
|
|
free(stderrbuf);
|
|
|
|
|
|
|
|
return success();
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t trim_disk(const char* path) {
|
|
|
|
// Get the disk info
|
|
|
|
disk_info_t info;
|
|
|
|
result_t result = get_disk_info(&info, path);
|
|
|
|
if (result != success())
|
|
|
|
return result;
|
|
|
|
|
|
|
|
// Generate a new path for the temporary disk
|
|
|
|
char* temp_path;
|
|
|
|
result = format(&temp_path, "%s.tmp", path);
|
|
|
|
if (result != success()) {
|
|
|
|
free_disk_info(&info);
|
|
|
|
return result;
|
2024-02-17 02:39:41 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Use convert to trim the disk
|
2024-02-17 02:39:41 +01:00
|
|
|
int exit_code;
|
2024-02-19 16:01:53 +01:00
|
|
|
// char* stdoutbuf;
|
|
|
|
char* stderrbuf;
|
2024-02-17 02:39:41 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
if (info.backing_path == NULL)
|
|
|
|
result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, temp_path, NULL);
|
|
|
|
else
|
|
|
|
result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-B", info.backing_path, path, temp_path, NULL);
|
2024-02-17 02:39:41 +01:00
|
|
|
|
2024-02-18 18:09:53 +01:00
|
|
|
// Free the disk info
|
2024-02-19 16:01:53 +01:00
|
|
|
free_disk_info(&info);
|
2024-02-17 02:39:41 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check for errors during the execution
|
|
|
|
if (result != success()) {
|
|
|
|
free(temp_path);
|
2024-02-18 18:09:53 +01:00
|
|
|
return result;
|
2024-02-17 02:39:41 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check the exit code
|
2024-02-17 23:59:38 +01:00
|
|
|
if (exit_code != 0) {
|
2024-02-19 16:01:53 +01:00
|
|
|
// Remove all newlines from the stderr buffer
|
|
|
|
for (char* c = stderrbuf; *c != '\0'; c++)
|
|
|
|
if (*c == '\n')
|
|
|
|
*c = ' ';
|
|
|
|
|
|
|
|
result = failure("Failed to trim disk (%s).", stderrbuf);
|
|
|
|
free(temp_path);
|
|
|
|
free(stderrbuf);
|
|
|
|
return result;
|
2024-02-17 02:39:41 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Free the stderr buffer as it is no longer needed
|
|
|
|
free(stderrbuf);
|
|
|
|
|
|
|
|
// Replace the original disk with the temporary disk
|
|
|
|
errno = 0;
|
|
|
|
rename(temp_path, path);
|
|
|
|
|
|
|
|
// Free the temporary path
|
|
|
|
free(temp_path);
|
2024-02-17 23:59:38 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check for errors during the renaming
|
|
|
|
if (errno != 0) {
|
|
|
|
// Try to remove the temporary disk if the renaming failed
|
|
|
|
remove(temp_path);
|
|
|
|
return failure("Failed to replace the original disk with the temporary disk (%s).", strerror(errno));
|
2024-02-17 02:39:41 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
return success();
|
2024-02-17 02:39:41 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t reset_disk(const char* path) {
|
|
|
|
// Get the disk info
|
|
|
|
disk_info_t info;
|
|
|
|
result_t result = get_disk_info(&info, path);
|
|
|
|
if (result != success())
|
|
|
|
return result;
|
|
|
|
|
|
|
|
if (info.backing_path == NULL)
|
|
|
|
// If the disk is not backed, simply create a new disk with the same size on top of it
|
|
|
|
result = create_root_disk(path, info.size);
|
|
|
|
else
|
|
|
|
// If the disk is backed, create a new disk with the same backing path
|
|
|
|
result = create_backed_disk(path, info.backing_path);
|
|
|
|
|
|
|
|
// Free the disk info
|
|
|
|
free_disk_info(&info);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result_t reback_disk(const char* path, const char* backing_path) {
|
|
|
|
// Create the disk
|
2024-02-17 01:34:58 +01:00
|
|
|
int exit_code;
|
2024-02-19 16:01:53 +01:00
|
|
|
// char* stdoutbuf;
|
|
|
|
char* stderrbuf;
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t result = execute(&exit_code, NULL, &stderrbuf, "qemu-img", "rebase", "-u", "-f", "qcow2", "-F", "qcow2", "-b", backing_path, path, NULL);
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check for errors during the execution
|
|
|
|
if (result != success())
|
2024-02-18 18:09:53 +01:00
|
|
|
return result;
|
2024-02-17 23:59:38 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check the exit code
|
2024-02-17 23:59:38 +01:00
|
|
|
if (exit_code != 0) {
|
2024-02-19 16:01:53 +01:00
|
|
|
// Remove all newlines from the stderr buffer
|
|
|
|
for (char* c = stderrbuf; *c != '\0'; c++)
|
|
|
|
if (*c == '\n')
|
|
|
|
*c = ' ';
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result = failure("Failed to reback disk (%s).", stderrbuf);
|
|
|
|
free(stderrbuf);
|
|
|
|
return result;
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Free the stderr buffer
|
|
|
|
free(stderrbuf);
|
|
|
|
|
|
|
|
return success();
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t get_disk_info(disk_info_t* _info, const char* path) {
|
|
|
|
// Initialize the output parameters
|
|
|
|
_info->size = 0;
|
|
|
|
_info->allocated = 0;
|
|
|
|
_info->backing_path = NULL;
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-17 23:59:38 +01:00
|
|
|
// Get the disk info
|
2024-02-17 01:34:58 +01:00
|
|
|
int exit_code;
|
2024-02-19 16:01:53 +01:00
|
|
|
char* stdoutbuf;
|
|
|
|
char* stderrbuf;
|
2024-02-17 23:59:38 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
result_t result = execute(&exit_code, &stdoutbuf, &stderrbuf, "qemu-img", "info", "--output", "json", path, NULL);
|
2024-02-17 23:59:38 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check for errors during the execution
|
|
|
|
if (result != success())
|
2024-02-18 18:09:53 +01:00
|
|
|
return result;
|
2024-02-17 23:59:38 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check the exit code
|
2024-02-18 18:09:53 +01:00
|
|
|
if (exit_code != 0) {
|
2024-02-19 16:01:53 +01:00
|
|
|
// Remove all newlines from the stderr buffer
|
|
|
|
for (char* c = stderrbuf; *c != '\0'; c++)
|
|
|
|
if (*c == '\n')
|
|
|
|
*c = ' ';
|
|
|
|
|
|
|
|
result = failure("Failed to get disk info (%s).", stderrbuf);
|
|
|
|
free(stdoutbuf);
|
|
|
|
free(stderrbuf);
|
|
|
|
return result;
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
2024-02-18 18:09:53 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Free the stderr buffer as it is no longer needed
|
|
|
|
free(stderrbuf);
|
2024-02-17 02:46:21 +01:00
|
|
|
|
2024-02-17 01:34:58 +01:00
|
|
|
// Parse the JSON output
|
2024-02-19 16:01:53 +01:00
|
|
|
json_object* root = json_tokener_parse(stdoutbuf);
|
|
|
|
|
|
|
|
// Free the stdout buffer as it is no longer needed
|
|
|
|
free(stdoutbuf);
|
2024-02-18 18:09:53 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Check for errors during the parsing
|
|
|
|
if (root == NULL)
|
|
|
|
return failure("Failed to parse the JSON output.");
|
2024-02-17 23:59:38 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Get the size
|
2024-02-18 18:09:53 +01:00
|
|
|
json_object* size_obj;
|
|
|
|
if (!json_object_object_get_ex(root, "virtual-size", &size_obj)) {
|
|
|
|
json_object_put(root);
|
2024-02-19 16:01:53 +01:00
|
|
|
return failure("Missing 'virtual-size' field in the JSON output. Cannot get the disk size.");
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-18 18:09:53 +01:00
|
|
|
errno = 0;
|
2024-02-19 16:01:53 +01:00
|
|
|
uint64_t size = json_object_get_int64(size_obj);
|
2024-02-18 18:09:53 +01:00
|
|
|
if (errno != 0) {
|
|
|
|
json_object_put(root);
|
2024-02-19 16:01:53 +01:00
|
|
|
return failure("Failed to parse the 'virtual-size' field in the JSON output (%s).", strerror(errno));
|
2024-02-18 18:09:53 +01:00
|
|
|
}
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Get the allocated size
|
|
|
|
json_object* allocated_obj;
|
|
|
|
if (!json_object_object_get_ex(root, "actual-size", &allocated_obj)) {
|
2024-02-18 18:09:53 +01:00
|
|
|
json_object_put(root);
|
2024-02-19 16:01:53 +01:00
|
|
|
return failure("Missing 'actual-size' field in the JSON output. Cannot get the allocated size.");
|
2024-02-18 18:09:53 +01:00
|
|
|
}
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-18 18:09:53 +01:00
|
|
|
errno = 0;
|
2024-02-19 16:01:53 +01:00
|
|
|
uint64_t allocated = json_object_get_int64(allocated_obj);
|
2024-02-18 18:09:53 +01:00
|
|
|
if (errno != 0) {
|
|
|
|
json_object_put(root);
|
2024-02-19 16:01:53 +01:00
|
|
|
return failure("Failed to parse the 'actual-size' field in the JSON output (%s).", strerror(errno));
|
2024-02-18 18:09:53 +01:00
|
|
|
}
|
2024-02-17 01:34:58 +01:00
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Get the backing file
|
|
|
|
json_object* backing_obj;
|
|
|
|
char* backing_path = NULL;
|
|
|
|
if (json_object_object_get_ex(root, "backing-filename", &backing_obj)) {
|
|
|
|
const char* backing_str = json_object_get_string(backing_obj);
|
|
|
|
if (backing_str == NULL) {
|
2024-02-18 18:09:53 +01:00
|
|
|
json_object_put(root);
|
2024-02-19 16:01:53 +01:00
|
|
|
return failure("Failed to parse the 'backing-filename' field in the JSON output.");
|
2024-02-18 18:09:53 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Duplicate the backing path string
|
|
|
|
backing_path = strdup(backing_str);
|
|
|
|
if (backing_path == NULL) {
|
2024-02-18 18:09:53 +01:00
|
|
|
json_object_put(root);
|
2024-02-19 16:01:53 +01:00
|
|
|
return failure("Failed to allocate memory for the backing path.");
|
2024-02-18 14:56:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
// Free the root object
|
2024-02-18 18:09:53 +01:00
|
|
|
json_object_put(root);
|
2024-02-19 16:01:53 +01:00
|
|
|
|
|
|
|
// Write the disk info to the output
|
|
|
|
_info->size = size;
|
|
|
|
_info->allocated = allocated;
|
|
|
|
_info->backing_path = backing_path;
|
|
|
|
return success();
|
2024-02-17 01:34:58 +01:00
|
|
|
}
|
|
|
|
|
2024-02-19 16:01:53 +01:00
|
|
|
void free_disk_info(disk_info_t* _info) {
|
|
|
|
// Free the backing path string
|
|
|
|
free(_info->backing_path);
|
|
|
|
|
|
|
|
// Clear the fields
|
|
|
|
_info->size = 0;
|
|
|
|
_info->allocated = 0;
|
|
|
|
_info->backing_path = NULL;
|
2024-02-18 18:09:53 +01:00
|
|
|
}
|