Made small modifications

This commit is contained in:
Alexei KADIR 2024-02-18 18:09:53 +01:00
parent c9f76746b4
commit 0da7474158
14 changed files with 1150 additions and 1713 deletions

View File

@ -1,332 +0,0 @@
#include "backing.h"
#include "disk.h"
#include "entry.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/stat.h>
bool IsBackingIdentifierValid(const char* backing_identifier) {
if (backing_identifier == NULL)
return false;
// Check that the identifier is not empty or too long
size_t length = strlen(backing_identifier);
if (length == 0 || length > 64)
return false;
// Check that the identifier does not contain any slashes
for (size_t i = 0; i < length; i++)
if (backing_identifier[i] == '/')
return false;
// Check that the identifier is not "." or ".."
if (strcmp(backing_identifier, ".") == 0 || strcmp(backing_identifier, "..") == 0)
return false;
// Check that the identifier starts with a number
if (backing_identifier[0] < '0' || backing_identifier[0] > '9')
return false;
return true;
}
int GetBackingIndex(const char* backing_identifier) {
// Check that the identifier is valid
if (!IsBackingIdentifierValid(backing_identifier))
return -1;
// Get the index
return atoi(backing_identifier);
}
Status GetBackingPoolPath(char** _backing_pool_path) {
return Format(_backing_pool_path, "/var/lib/sandbox/backings");
}
Status GetBackingPath(const char* backing_identifier, char** _backing_path) {
// Check that the identifier is valid, as it will be used in a path
if (!IsBackingIdentifierValid(backing_identifier)) {
Log(LOG_LEVEL_ERROR, "Invalid backing identifier '%s'.", backing_identifier);
return FAILURE;
}
// Get the backing pool path
char* backing_pool_path = NULL;
Status status = GetBackingPoolPath(&backing_pool_path);
if (status != SUCCESS)
return status;
// Format the backing path
status = Format(_backing_path, "%s/%s", backing_pool_path, backing_identifier);
free(backing_pool_path);
return status;
}
Status DoesBackingExist(const char* backing_identifier, bool* _result) {
// Get the backing path
char* backing_path = NULL;
Status status = GetBackingPath(backing_identifier, &backing_path);
if (status != SUCCESS)
return status;
// Check if the backing exists and is a regular file
struct stat st;
*_result = stat(backing_path, &st) == 0 && S_ISREG(st.st_mode);
free(backing_path);
return SUCCESS;
}
Status AddBacking(const char* backing_identifier, const char* entry_identifier) {
// Check that the backing does not already exist
bool exists;
Status status = DoesBackingExist(backing_identifier, &exists);
if (status != SUCCESS)
return status;
if (exists) {
Log(LOG_LEVEL_ERROR, "The backing '%s' already exists.", backing_identifier);
return FAILURE;
}
// Check that the entry disk exists
status = DoesEntryDiskExist(entry_identifier, &exists);
if (status != SUCCESS)
return status;
if (!exists) {
Log(LOG_LEVEL_ERROR, "The entry disk '%s' does not exist. Cannot create backing from it.", entry_identifier);
return FAILURE;
}
// Get information about the entry disk
DiskInfo entry_info;
status = GetEntryDiskInfo(entry_identifier, &entry_info);
if (status != SUCCESS)
return status;
// Check that the backing used by the entry actually exists (broken backing chain)
if (entry_info.backing_identifier != NULL) {
status = DoesBackingExist(entry_info.backing_identifier, &exists);
if (status != SUCCESS) {
FreeDiskInfo(&entry_info);
return status;
}
if (!exists) {
Log(LOG_LEVEL_ERROR, "The backing '%s' of the entry disk '%s' does not exist.", entry_info.backing_identifier, entry_identifier);
FreeDiskInfo(&entry_info);
return FAILURE;
}
}
// Copy the entry disk to the backing disk
char* entry_disk_path = NULL;
status = GetEntryDiskPath(entry_identifier, &entry_disk_path);
if (status != SUCCESS) {
FreeDiskInfo(&entry_info);
return status;
}
char* backing_path = NULL;
status = GetBackingPath(backing_identifier, &backing_path);
if (status != SUCCESS) {
free(entry_disk_path);
FreeDiskInfo(&entry_info);
return status;
}
status = CopyFile(entry_disk_path, backing_path);
if (status != SUCCESS) {
free(entry_disk_path);
free(backing_path);
FreeDiskInfo(&entry_info);
return status;
}
free(entry_disk_path);
// If the entry disk has a backing, rebase the backing to the new backing
if (entry_info.backing_identifier != NULL) {
status = RebaseDisk(backing_path, entry_info.backing_identifier);
if (status != SUCCESS) {
RemoveBacking(backing_identifier);
free(backing_path);
FreeDiskInfo(&entry_info);
return status;
}
}
free(backing_path);
FreeDiskInfo(&entry_info);
return SUCCESS;
}
Status RemoveBacking(const char* backing_identifier) {
// Check that the backing exists
bool exists;
Status status = DoesBackingExist(backing_identifier, &exists);
if (status != SUCCESS)
return status;
if (!exists) {
Log(LOG_LEVEL_ERROR, "The backing '%s' does not exist.", backing_identifier);
return FAILURE;
}
// Get the backing path
char* backing_path = NULL;
status = GetBackingPath(backing_identifier, &backing_path);
if (status != SUCCESS)
return status;
// Remove the backing
if (unlink(backing_path) != 0) {
Log(LOG_LEVEL_ERROR, "Failed to remove the backing '%s' (%s).", backing_identifier, strerror(errno));
free(backing_path);
return FAILURE;
}
free(backing_path);
// TODO: Remove all the backings that use this backing as a base
return SUCCESS;
}
Status ListBackings(char*** _backings) {
*_backings = NULL;
// Open the backing pool directory
char* backing_pool_path = NULL;
Status status = GetBackingPoolPath(&backing_pool_path);
if (status != SUCCESS)
return status;
DIR* dir = opendir(backing_pool_path);
if (dir == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to open the backing pool directory %s (%s).", backing_pool_path, strerror(errno));
free(backing_pool_path);
return FAILURE;
}
free(backing_pool_path);
// Allocate memory for at least one backing (the NULL terminator)
*_backings = malloc(sizeof(char*));
if (*_backings == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the backings list (%s).", strerror(errno));
closedir(dir);
return FAILURE;
}
(*_backings)[0] = NULL;
// Read the files in the backing pool directory
size_t count = 0;
struct dirent* file;
while ((file = readdir(dir)) != NULL) {
if (strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)
continue;
// Check if the backing exists (this checks for validity)
bool exists;
Status status = DoesBackingExist(file->d_name, &exists);
if (status != SUCCESS || !exists)
continue;
// Allocate memory for the new backing list
char** new_backings = realloc(*_backings, (count + 2) * sizeof(char*)); // +2 for the new entry and the NULL terminator
if (new_backings == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to reallocate memory for the backing list (%s).", strerror(errno));
closedir(dir);
for (size_t i = 0; i < count; i++)
free((*_backings)[i]);
free(*_backings);
return FAILURE;
}
*_backings = new_backings;
// Duplicate the file name and add it to the list
(*_backings)[count] = strdup(file->d_name);
if ((*_backings)[count] == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to duplicate the backing name (%s).", strerror(errno));
closedir(dir);
for (size_t i = 0; i < count; i++)
free((*_backings)[i]);
free(*_backings);
return FAILURE;
}
// Add the NULL terminator
(*_backings)[count + 1] = NULL;
count++;
}
closedir(dir);
return SUCCESS;
}
Status GetLatestBacking(char** _backing_identifier) {
// List the backings
char** backings = NULL;
Status status = ListBackings(&backings);
if (status != SUCCESS)
return status;
// Find the latest backing
int latest_identifier_index = -1;
int latest_index = -1;
for (int i = 0; backings[i] != NULL; i++) {
int index = GetBackingIndex(backings[i]);
if (index > latest_index) {
latest_identifier_index = i;
latest_index = index;
}
}
// Return the latest backing
if (latest_identifier_index == -1) {
*_backing_identifier = NULL;
} else {
*_backing_identifier = strdup(backings[latest_identifier_index]);
if (*_backing_identifier == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to duplicate the backing identifier (%s).", strerror(errno));
status = FAILURE;
}
}
// Free the backings list
for (int i = 0; backings[i] != NULL; i++)
free(backings[i]);
free(backings);
return status;
}
Status GetBackingDiskInfo(const char* backing_identifier, DiskInfo* _info) {
*_info = (DiskInfo){0};
// Get the backing path
char* backing_path = NULL;
Status status = GetBackingPath(backing_identifier, &backing_path);
if (status != SUCCESS)
return status;
// Get the disk info
status = GetDiskInfo(backing_path, _info);
free(backing_path);
return status;
}

View File

@ -1,20 +0,0 @@
#pragma once
#include "utils.h"
#include "disk.h"
bool IsBackingIdentifierValid(const char* backing_identifier);
int GetBackingIndex(const char* backing_identifier);
Status GetBackingPoolPath(char** _backing_pool_path);
Status GetBackingPath(const char* backing_identifier, char** _backing_path);
Status DoesBackingExist(const char* backing_identifier, bool* _result);
Status AddBacking(const char* backing_identifier, const char* entry_identifier);
Status RemoveBacking(const char* backing_identifier);
Status ListBackings(char*** _backings);
Status GetLatestBacking(char** _backing_identifier);
Status GetBackingDiskInfo(const char* backing_identifier, DiskInfo* _info);

View File

@ -1,185 +1,219 @@
#include "disk.h" #include "disk.h"
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <libgen.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <libgen.h>
#include <json-c/json.h> #include <json-c/json.h>
Status CreateRootDisk(const char* path, uint64_t disk_size) { Result CreateRootDisk(const char* disk_path, uint64_t size) {
// Convert the size to a string // Convert the size to a string
char* size_str; char* size_str;
Status status = Format(&size_str, "%lu", disk_size); if (Format(&size_str, "%lu", size) == FAILURE)
if (status != SUCCESS) return FAILURE;
return status;
// Create the disk // Create the root disk
int exit_code; int exit_code;
// char* stdout; // char* stdoutb; No need to capture stdout
char* stderrb; char* stderrb;
status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", path, size_str, NULL); Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "create", "-f", "qcow2", disk_path, size_str, NULL);
// Free the size string
free(size_str); free(size_str);
// Check if the disk was created successfully // If the execution failed, return the error
if (status != SUCCESS) if (result != SUCCESS)
return status; return result;
// If the exit code is non-zero, return the error
if (exit_code != 0) { if (exit_code != 0) {
if (stderrb != NULL) { // If the error message is empty, return a generic error
// Remove newlines from the stderr buffer if (stderrb == NULL)
for (int i = 0; stderrb[i] != '\0'; i++) Log(LOG_LEVEL_ERROR, "Failed to create root disk '%s'.", disk_path);
if (stderrb[i] == '\n') else {
stderrb[i] = ' '; // Remove all newlines from the error message
for (char* c = stderrb; *c != '\0'; c++)
if (*c == '\n')
*c = ' ';
Log(LOG_LEVEL_ERROR, "Failed to create disk at '%s' (%s).", path, stderrb); Log(LOG_LEVEL_ERROR, "Failed to create root disk '%s' (%s).", disk_path, stderrb);
}
// Free the error message
free(stderrb); free(stderrb);
} else
Log(LOG_LEVEL_ERROR, "Failed to create disk");
return FAILURE; return FAILURE;
} }
// As there can still be an error message for a successful exit code, we need to free it
free(stderrb);
return SUCCESS; return SUCCESS;
} }
Status CreateBackedDisk(const char* path, const char* backing_file) { Result CreateBackedDisk(const char* disk_path, const char* backing_disk_path) {
// Create the disk // Create the backed disk
int exit_code; int exit_code;
// char* stdout; // char* stdoutb; No need to capture stdout
char* stderrb; char* stderrb;
Status status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_file, path, NULL); Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_disk_path, disk_path, NULL);
// Check if the disk was created successfully // If the execution failed, return the error
if (status != SUCCESS) if (result != SUCCESS)
return status; return result;
// If the exit code is non-zero, return the error
if (exit_code != 0) { if (exit_code != 0) {
if (stderrb != NULL) { // If the error message is empty, return a generic error
// Remove newlines from the stderr buffer if (stderrb == NULL)
for (int i = 0; stderrb[i] != '\0'; i++) Log(LOG_LEVEL_ERROR, "Failed to create backed disk '%s' with backing disk '%s'.", disk_path, backing_disk_path);
if (stderrb[i] == '\n') else {
stderrb[i] = ' '; // Remove all newlines from the error message
for (char* c = stderrb; *c != '\0'; c++)
if (*c == '\n')
*c = ' ';
Log(LOG_LEVEL_ERROR, "Failed to create disk at '%s' (%s).", path, stderrb); Log(LOG_LEVEL_ERROR, "Failed to create backed disk '%s' with backing disk '%s' (%s).", disk_path, backing_disk_path, stderrb);
}
// Free the error message
free(stderrb); free(stderrb);
} else
Log(LOG_LEVEL_ERROR, "Failed to create disk");
return FAILURE; return FAILURE;
} }
// As there can still be an error message for a successful exit code, we need to free it
free(stderrb);
return SUCCESS; return SUCCESS;
} }
Status TrimDisk(const char* path) { Result TrimDisk(const char* disk_path) {
char* tmp_path = NULL; // Get a temporary file path to store the trimmed disk
Status status = Format(&tmp_path, "%s.tmp", path); char* trimmed_disk_path;
if (status != SUCCESS) if (Format(&trimmed_disk_path, "%s.trimmed", disk_path) == FAILURE)
return status; return FAILURE;
// Get info about the disk
DiskInfo info; DiskInfo info;
status = GetDiskInfo(path, &info); if (GetDiskInfo(disk_path, &info) == FAILURE) {
if (status != SUCCESS) { free(trimmed_disk_path);
free(tmp_path); return FAILURE;
return status;
} }
// Create the temporary disk // Trim the disk
int exit_code; int exit_code;
// char* stdout; // char* stdoutb; No need to capture stdout
char* stderrb; char* stderrb;
if (info.backing_file != NULL) { // If the disk is not backed, we can just use the "convert" command to trim it
char* backing_file_opt = NULL; Result result;
status = Format(&backing_file_opt, "backing_file=%s", info.backing_file);
if (status != SUCCESS) { if (info.backing_file_path == NULL)
free(tmp_path); result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", disk_path, trimmed_disk_path, NULL);
else {
// We need to get the option to specify the backing file
char* backing_option;
if (Format(&backing_option, "backing_file=%s", info.backing_file_path) == FAILURE) {
free(trimmed_disk_path);
FreeDiskInfo(&info); FreeDiskInfo(&info);
return status; return FAILURE;
} }
status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_file_opt, path, tmp_path, NULL); // Trim the disk using the "convert" command
result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_option, disk_path, trimmed_disk_path, NULL);
free(backing_file_opt); // Free the backing option
} else free(backing_option);
status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, tmp_path, NULL);
FreeDiskInfo(&info);
// Check if the disk was created successfully
if (status != SUCCESS) {
free(tmp_path);
return status;
} }
// Free the disk info
FreeDiskInfo(&info);
// If the execution failed, return the error
if (result != SUCCESS) {
free(trimmed_disk_path);
return result;
}
// If the exit code is non-zero, return the error
if (exit_code != 0) { if (exit_code != 0) {
if (stderrb != NULL) { // If the error message is empty, return a generic error
// Remove newlines from the stderr buffer if (stderrb == NULL)
for (int i = 0; stderrb[i] != '\0'; i++) Log(LOG_LEVEL_ERROR, "Failed to trim disk '%s'.", disk_path);
if (stderrb[i] == '\n') else {
stderrb[i] = ' '; // Remove all newlines from the error message
for (char* c = stderrb; *c != '\0'; c++)
if (*c == '\n')
*c = ' ';
Log(LOG_LEVEL_ERROR, "Failed to create temporary disk at '%s' (%s).", tmp_path, stderrb); Log(LOG_LEVEL_ERROR, "Failed to trim disk '%s' (%s).", disk_path, stderrb);
}
// Free the error message
free(stderrb); free(stderrb);
} else free(trimmed_disk_path);
Log(LOG_LEVEL_ERROR, "Failed to create temporary disk");
free(tmp_path);
return FAILURE; return FAILURE;
} }
// Try to move the temporary disk to the original path // As there can still be an error message for a successful exit code, we need to free it
if (rename(tmp_path, path) != 0) { free(stderrb);
Log(LOG_LEVEL_ERROR, "Failed to move the temporary disk to the original path '%s' (%s).", path, strerror(errno));
unlink(tmp_path); // Move the trimmed disk to the original path
if (rename(trimmed_disk_path, disk_path) != 0) {
free(tmp_path); Log(LOG_LEVEL_ERROR, "Failed to move trimmed disk '%s' to original path '%s' (%s).", trimmed_disk_path, disk_path, strerror(errno));
unlink(trimmed_disk_path);
free(trimmed_disk_path);
return FAILURE; return FAILURE;
} }
free(tmp_path); // Free the temporary file path
free(trimmed_disk_path);
return SUCCESS; return SUCCESS;
} }
Status RebaseDisk(const char* path, const char* backing_file) { Result RebackDisk(const char* disk_path, const char* backing_disk_path) {
// Create the disk // Reback the disk
int exit_code; int exit_code;
// char* stdout; // char* stdoutb; No need to capture stdout
char* stderrb; char* stderrb;
Status status = RunExecutable(&exit_code, NULL, &stderrb, "/usr/bin/qemu-img", "rebase", "-f", "qcow2", "-F", "qcow2", "-u", "-b", backing_file, path, NULL); Result result = RunExecutable(&exit_code, NULL, &stderrb, "qemu-img", "rebase", "-f", "qcow2", "-F", "qcow2", "-u", "-b", backing_disk_path, disk_path, NULL);
// Check if the disk was created successfully // If the execution failed, return the error
if (status != SUCCESS) if (result != SUCCESS)
return status; return result;
// If the exit code is non-zero, return the error
if (exit_code != 0) { if (exit_code != 0) {
if (stderrb != NULL) { // If the error message is empty, return a generic error
// Remove newlines from the stderr buffer if (stderrb == NULL)
for (int i = 0; stderrb[i] != '\0'; i++) Log(LOG_LEVEL_ERROR, "Failed to reback disk '%s' with backing disk '%s'.", disk_path, backing_disk_path);
if (stderrb[i] == '\n') else {
stderrb[i] = ' '; // Remove all newlines from the error message
for (char* c = stderrb; *c != '\0'; c++)
if (*c == '\n')
*c = ' ';
Log(LOG_LEVEL_ERROR, "Failed to rebase disk at '%s' (%s).", path, stderrb); Log(LOG_LEVEL_ERROR, "Failed to reback disk '%s' with backing disk '%s' (%s).", disk_path, backing_disk_path, stderrb);
}
// Free the error message
free(stderrb); free(stderrb);
} else
Log(LOG_LEVEL_ERROR, "Failed to rebase disk");
return FAILURE; return FAILURE;
} }
// As there can still be an error message for a successful exit code, we need to free it
free(stderrb);
return SUCCESS; return SUCCESS;
} }
Status GetDiskInfo(const char* path, DiskInfo* _info) { Result GetDiskInfo(const char* disk_path, DiskInfo* _info) {
// Initialize the disk info
*_info = (DiskInfo){0}; *_info = (DiskInfo){0};
// Get the disk info // Get the disk info
@ -187,66 +221,116 @@ Status GetDiskInfo(const char* path, DiskInfo* _info) {
char* stdoutb; char* stdoutb;
char* stderrb; char* stderrb;
Status status = RunExecutable(&exit_code, &stdoutb, &stderrb, "/usr/bin/qemu-img", "info", "--output", "json", path, NULL); Result result = RunExecutable(&exit_code, &stdoutb, &stderrb, "qemu-img", "info", "--output", "json", disk_path, NULL);
if (status != SUCCESS)
return status;
// If the execution failed, return the error
if (result != SUCCESS)
return result;
// If the exit code is non-zero, return the error
if (exit_code != 0) { if (exit_code != 0) {
if (stderrb != NULL) { // If the error message is empty, return a generic error
// Remove newlines from the stderr buffer if (stderrb == NULL)
for (int i = 0; stderrb[i] != '\0'; i++) Log(LOG_LEVEL_ERROR, "Failed to get info for disk '%s'.", disk_path);
if (stderrb[i] == '\n') else {
stderrb[i] = ' '; // Remove all newlines from the error message
for (char* c = stderrb; *c != '\0'; c++)
if (*c == '\n')
*c = ' ';
Log(LOG_LEVEL_ERROR, "Failed to get disk info at '%s' (%s).", path, stderrb); Log(LOG_LEVEL_ERROR, "Failed to get info for disk '%s' (%s).", disk_path, stderrb);
free(stderrb); }
} else
Log(LOG_LEVEL_ERROR, "Failed to get disk info");
// Free the error message
free(stdoutb); free(stdoutb);
free(stderrb);
return FAILURE; return FAILURE;
} }
// No need for the error message anymore
free(stderrb); free(stderrb);
// Parse the JSON output // Parse the JSON output
json_object* root = json_tokener_parse(stdoutb); json_object* root = json_tokener_parse(stdoutb);
free(stdoutb);
if (root == NULL) { if (root == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to parse the JSON output"); Log(LOG_LEVEL_ERROR, "Failed to parse JSON output for disk '%s'.", disk_path);
free(stdoutb);
return FAILURE; return FAILURE;
} }
json_object* virtual_size = NULL; // No need for the JSON string anymore
json_object* actual_size = NULL; free(stdoutb);
json_object* backing_file = NULL;
json_object_object_get_ex(root, "virtual-size", &virtual_size);
json_object_object_get_ex(root, "actual-size", &actual_size);
json_object_object_get_ex(root, "backing-filename", &backing_file);
if (virtual_size != NULL)
_info->size = json_object_get_int64(virtual_size);
if (actual_size != NULL)
_info->allocated = json_object_get_int64(actual_size);
if (backing_file != NULL)
_info->backing_file = strdup(json_object_get_string(backing_file));
// Get the size of the disk
json_object* size_obj;
if (!json_object_object_get_ex(root, "virtual-size", &size_obj)) {
Log(LOG_LEVEL_ERROR, "Failed to get virtual size for disk '%s'.", disk_path);
json_object_put(root); json_object_put(root);
FreeDiskInfo(_info);
return FAILURE;
}
if (_info->backing_file != NULL) { errno = 0;
_info->backing_identifier = strdup(basename(_info->backing_file)); _info->size = json_object_get_int64(size_obj);
if (_info->backing_identifier == NULL) { if (errno != 0) {
Log(LOG_LEVEL_ERROR, "Failed to duplicate the backing identifier (%s).", strerror(errno)); Log(LOG_LEVEL_ERROR, "Failed to parse virtual size for disk '%s'.", disk_path);
json_object_put(root);
FreeDiskInfo(_info);
return FAILURE;
}
// Get the allocated size of the disk
json_object* allocation_obj;
if (!json_object_object_get_ex(root, "actual-size", &allocation_obj)) {
Log(LOG_LEVEL_ERROR, "Failed to get actual size for disk '%s'.", disk_path);
json_object_put(root);
FreeDiskInfo(_info);
return FAILURE;
}
errno = 0;
_info->allocated = json_object_get_int64(allocation_obj);
if (errno != 0) {
Log(LOG_LEVEL_ERROR, "Failed to parse actual size for disk '%s'.", disk_path);
json_object_put(root);
FreeDiskInfo(_info);
return FAILURE;
}
// Get the backing file of the disk
json_object* backing_file_obj;
if (!json_object_object_get_ex(root, "backing-filename", &backing_file_obj))
_info->backing_file_path = NULL;
else {
const char* backing_file_path = json_object_get_string(backing_file_obj);
if (backing_file_path == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to parse backing file for disk '%s'.", disk_path);
json_object_put(root);
FreeDiskInfo(_info);
return FAILURE;
}
if (Duplicate(backing_file_path, &_info->backing_file_path) == FAILURE) {
json_object_put(root);
FreeDiskInfo(_info);
return FAILURE;
}
// Get the backing identifier of the disk (basename)
if (Duplicate(basename(_info->backing_file_path), &_info->backing_identifier) == FAILURE) {
json_object_put(root);
FreeDiskInfo(_info); FreeDiskInfo(_info);
return FAILURE; return FAILURE;
} }
} }
// Free the JSON object
json_object_put(root);
return SUCCESS; return SUCCESS;
} }
void FreeDiskInfo(DiskInfo* info) { Result FreeDiskInfo(DiskInfo* info) {
free(info->backing_file); free(info->backing_file_path);
free(info->backing_identifier); free(info->backing_identifier);
return SUCCESS;
} }

View File

@ -5,15 +5,14 @@
typedef struct { typedef struct {
uint64_t size; uint64_t size;
uint64_t allocated; uint64_t allocated;
char* backing_file; char* backing_file_path;
char* backing_identifier; char* backing_identifier;
} DiskInfo; } DiskInfo;
Status CreateRootDisk(const char* path, uint64_t disk_size); Result CreateRootDisk(const char* disk_path, uint64_t size);
Status CreateBackedDisk(const char* path, const char* backing_file); Result CreateBackedDisk(const char* disk_path, const char* backing_disk_path);
Result TrimDisk(const char* disk_path);
Result RebackDisk(const char* disk_path, const char* backing_disk_path);
Status TrimDisk(const char* path); Result GetDiskInfo(const char* disk_path, DiskInfo* _info);
Status RebaseDisk(const char* path, const char* backing_file); Result FreeDiskInfo(DiskInfo* info);
Status GetDiskInfo(const char* path, DiskInfo* _info);
void FreeDiskInfo(DiskInfo* info);

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
#pragma once
#include "utils.h"
#include "disk.h"
bool IsEntryIdentifierValid(const char* entry_identifier);
Status GetEntryPoolPath(char** _entry_pool_path);
Status GetEntryPath(const char* entry_identifier, char** _entry_path);
Status GetEntryDiskPath(const char* entry_identifier, char** _entry_disk_path);
Status DoesEntryExist(const char* entry_identifier, bool* _result);
Status DoesEntryDiskExist(const char* entry_identifier, bool* _result);
Status AddEntry(const char* entry_identifier);
Status RemoveEntry(const char* entry_identifier);
Status ListEntries(char*** _entries);
Status ClearEntries();
Status AddRootEntryDisk(const char* entry_identifier, uint64_t disk_size);
Status AddBackedEntryDisk(const char* entry_identifier, const char* backing_identifier);
Status RemoveEntryDisk(const char* entry_identifier);
Status ResetEntryDisk(const char* entry_identifier, const char* backing_identifier);
Status TrimEntryDisk(const char* entry_identifier);
Status GetEntryDiskInfo(const char* entry_identifier, DiskInfo* _info);

415
src/image.c Normal file
View File

@ -0,0 +1,415 @@
#include "image.h"
#include "disk.h"
#include "template.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
Result IsImageIdentifierValid(const char* image_id, bool* _valid) {
*_valid = false;
// Check that the image identifier is not NULL
if (image_id == NULL)
return SUCCESS;
// Check that the image identifier is not empty or too long
size_t length = strlen(image_id);
if (length == 0 || length > MAX_IMAGE_ID_LENGTH)
return SUCCESS;
// Check that the image identifier contains characters that are allowed as a user name
for (size_t i = 0; i < length; i++) {
char c = image_id[i];
if (c >= 'a' && c <= 'z')
continue;
if (c >= 'A' && c <= 'Z')
continue;
if (c >= '0' && c <= '9')
continue;
if (c == '_' || c == '-' || c == '.')
continue;
return SUCCESS;
}
// Check that the image identifier is not a reserved name
if (strcmp(image_id, ".") == 0 || strcmp(image_id, "..") == 0)
return SUCCESS;
*_valid = true;
return SUCCESS;
}
Result GetImagePoolPath(char** _path) {
return Format(_path, "/var/lib/sandbox/images");
}
Result GetImageDirectoryPath(const char* image_id, char** _path) {
// Check that the image identifier is valid
if (IsImageIdentifierValid(image_id, NULL) != SUCCESS)
return FAILURE;
// Get the path to the image pool
char* pool_path;
if (GetImagePoolPath(&pool_path) != SUCCESS)
return FAILURE;
// Format the image directory path
Result result = Format(_path, "%s/%s", pool_path, image_id);
free(pool_path);
return result;
}
Result GetImageDiskPath(const char* image_id, char** _path) {
// Get the path to the image directory
char* directory_path;
if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS)
return FAILURE;
// Format the image disk path
Result result = Format(_path, "%s/disk", directory_path);
free(directory_path);
return result;
}
Result DoesImageDirectoryExist(const char* image_id, bool* _exists) {
// Get the path to the image directory
char* directory_path;
if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS)
return FAILURE;
// Check if the image directory exists
struct stat st;
*_exists = stat(directory_path, &st) == 0 && S_ISDIR(st.st_mode);
free(directory_path);
return SUCCESS;
}
Result DoesImageDiskExist(const char* image_id, bool* _exists) {
// Get the path to the image disk
char* disk_path;
if (GetImageDiskPath(image_id, &disk_path) != SUCCESS)
return FAILURE;
// Check if the image disk exists
struct stat st;
*_exists = stat(disk_path, &st) == 0 && S_ISREG(st.st_mode);
free(disk_path);
return SUCCESS;
}
Result AddImage(const char* image_id) {
// Check if the image directory already exists
bool directory_exists;
if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS)
return FAILURE;
if (directory_exists) {
Log(LOG_LEVEL_ERROR, "Image '%s' already exists", image_id);
return FAILURE;
}
// Get the path to the image directory
char* directory_path;
if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS)
return FAILURE;
// Create the image directory
Result result = mkdir(directory_path, 0755) == 0 ? SUCCESS : FAILURE;
if (result == FAILURE)
Log(LOG_LEVEL_ERROR, "Failed to create image '%s' (%s)", directory_path, strerror(errno));
free(directory_path);
return result;
}
Result RemoveImage(const char* image_id) {
// Check if the image directory exists
bool directory_exists;
if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS)
return FAILURE;
if (!directory_exists) {
Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id);
return FAILURE;
}
// Check if there is a disk for the image and remove it if it exists
bool disk_exists;
if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS)
return FAILURE;
if (disk_exists)
RemoveImageDisk(image_id);
// Get the path to the image directory
char* directory_path;
if (GetImageDirectoryPath(image_id, &directory_path) != SUCCESS)
return FAILURE;
// Remove the image directory
Result result = rmdir(directory_path) == 0 ? SUCCESS : FAILURE;
if (result == FAILURE)
Log(LOG_LEVEL_ERROR, "Failed to remove image '%s' (%s)", directory_path, strerror(errno));
free(directory_path);
return result;
}
Result ListImages(char*** _image_ids) {
// Get the path to the image pool
char* pool_path;
if (GetImagePoolPath(&pool_path) != SUCCESS)
return FAILURE;
// Open the image pool directory
DIR* pool_dir = opendir(pool_path);
if (pool_dir == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to open image pool directory '%s' (%s)", pool_path, strerror(errno));
free(pool_path);
return FAILURE;
}
free(pool_path);
// Allocate an initial array, one element is reserved for the NULL terminator
*_image_ids = malloc(sizeof(char*));
if (*_image_ids == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for image identifiers");
closedir(pool_dir);
return FAILURE;
}
(*_image_ids)[0] = NULL;
// Iterate over the image pool directory
int count = 0;
struct dirent* entry;
while ((entry = readdir(pool_dir)) != NULL) {
// Skip the current and parent directory
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
// Check that the entry actually exists
bool exists;
if (DoesImageDirectoryExist(entry->d_name, &exists) != SUCCESS || !exists)
continue;
// Allocate a new element in the array
char** new_image_ids = realloc(*_image_ids, (count + 2) * sizeof(char*)); // One element is reserved for the NULL terminator
if (new_image_ids == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for image identifiers");
closedir(pool_dir);
for (int i = 0; i < count; i++)
free((*_image_ids)[i]);
free(*_image_ids);
return FAILURE;
}
*_image_ids = new_image_ids;
// Copy the entry name into the array
if (Duplicate(entry->d_name, &(*_image_ids)[count]) != SUCCESS) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for image identifier");
closedir(pool_dir);
for (int i = 0; i < count; i++)
free((*_image_ids)[i]);
free(*_image_ids);
return FAILURE;
}
// Null-terminate the array
(*_image_ids)[count + 1] = NULL;
// Increment the count
count++;
}
// Close the image pool directory
closedir(pool_dir);
return SUCCESS;
}
Result ClearImages(void) {
// Get the image identifiers
char** image_ids;
if (ListImages(&image_ids) != SUCCESS)
return FAILURE;
// Remove each image
for (int i = 0; image_ids[i] != NULL; i++)
RemoveImage(image_ids[i]);
// Free the image identifiers
for (int i = 0; image_ids[i] != NULL; i++)
free(image_ids[i]);
free(image_ids);
return SUCCESS;
}
Result AddRootImageDisk(const char* image_id, uint64_t size) {
// Check that the image directory exists
bool directory_exists;
if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS)
return FAILURE;
if (!directory_exists) {
Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id);
return FAILURE;
}
// Check that the image disk does not exist
bool disk_exists;
if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS)
return FAILURE;
if (disk_exists) {
Log(LOG_LEVEL_ERROR, "Disk for image '%s' already exists", image_id);
return FAILURE;
}
// Get the path to the image disk
char* disk_path;
if (GetImageDiskPath(image_id, &disk_path) != SUCCESS)
return FAILURE;
// Create the root disk
Result result = CreateRootDisk(disk_path, size);
free(disk_path);
return result;
}
Result AddTemplatedImageDisk(const char* image_id, const char* template_id) {
// Check that the image directory exists
bool directory_exists;
if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS)
return FAILURE;
if (!directory_exists) {
Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id);
return FAILURE;
}
// Check that the image disk does not exist
bool disk_exists;
if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS)
return FAILURE;
if (disk_exists) {
Log(LOG_LEVEL_ERROR, "Disk for image '%s' already exists", image_id);
return FAILURE;
}
// Check that the template exists
bool template_exists;
if (DoesTemplateExist(template_id, &template_exists) != SUCCESS)
return FAILURE;
if (!template_exists) {
Log(LOG_LEVEL_ERROR, "Template '%s' does not exist", template_id);
return FAILURE;
}
// Get the path to the template disk
char* template_disk_path;
if (GetTemplateDiskPath(template_id, &template_disk_path) != SUCCESS)
return FAILURE;
// Get the path to the image disk
char* disk_path;
if (GetImageDiskPath(image_id, &disk_path) != SUCCESS) {
free(template_disk_path);
return FAILURE;
}
// Create the templated disk
Result result = CreateBackedDisk(disk_path, template_disk_path);
free(template_disk_path);
free(disk_path);
return result;
}
Result RemoveImageDisk(const char* image_id) {
// Check that the image directory exists
bool directory_exists;
if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS)
return FAILURE;
if (!directory_exists) {
Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id);
return FAILURE;
}
// Check that the image disk exists
bool disk_exists;
if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS)
return FAILURE;
if (!disk_exists) {
Log(LOG_LEVEL_ERROR, "Disk for image '%s' does not exist", image_id);
return FAILURE;
}
// Get the path to the image disk
char* disk_path;
if (GetImageDiskPath(image_id, &disk_path) != SUCCESS)
return FAILURE;
// Remove the image disk
int result = unlink(disk_path);
free(disk_path);
if (result != 0) {
Log(LOG_LEVEL_ERROR, "Failed to remove disk for image '%s' (%s)", image_id, strerror(errno));
return FAILURE;
}
return SUCCESS;
}
Result TrimImageDisk(const char* image_id) {
// Check that the image directory exists
bool directory_exists;
if (DoesImageDirectoryExist(image_id, &directory_exists) != SUCCESS)
return FAILURE;
if (!directory_exists) {
Log(LOG_LEVEL_ERROR, "Image '%s' does not exist", image_id);
return FAILURE;
}
// Check that the image disk exists
bool disk_exists;
if (DoesImageDiskExist(image_id, &disk_exists) != SUCCESS)
return FAILURE;
if (!disk_exists) {
Log(LOG_LEVEL_ERROR, "Disk for image '%s' does not exist", image_id);
return FAILURE;
}
// Get the path to the image disk
char* disk_path;
if (GetImageDiskPath(image_id, &disk_path) != SUCCESS)
return FAILURE;
// Trim the image disk
Result result = TrimDisk(disk_path);
free(disk_path);
return result;
}

24
src/image.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "utils.h"
#define MAX_IMAGE_ID_LENGTH 64
Result IsImageIdentifierValid(const char* image_id, bool* _valid);
Result GetImagePoolPath(char** _path);
Result GetImageDirectoryPath(const char* image_id, char** _path);
Result GetImageDiskPath(const char* image_id, char** _path);
Result DoesImageDirectoryExist(const char* image_id, bool* _exists);
Result DoesImageDiskExist(const char* image_id, bool* _exists);
Result AddImage(const char* image_id);
Result RemoveImage(const char* image_id);
Result ListImages(char*** _image_ids);
Result ClearImages(void);
Result AddRootImageDisk(const char* image_id, uint64_t size);
Result AddTemplatedImageDisk(const char* image_id, const char* template_id);
Result RemoveImageDisk(const char* image_id);
Result TrimImageDisk(const char* image_id);

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +1,6 @@
#pragma once #pragma once
#define VERSION "0.0.9" #define SANDBOX_VERSION "0.1.2"
#define SANDBOX_USER "sandbox" #define SANDBOX_USER "sandbox"
typedef struct { int main(int argc, char** argv);
int (*handler)(int argc, char* argv[]);
const char* name;
const char* arguments;
const char* description;
const char* details;
} Command;
int main(int argc, char* argv[]);
int CommandHelp(int argc, char* argv[]);
int CommandVersion(int argc, char* argv[]);
int CommandAddEntry(int argc, char* argv[]);
int CommandRemoveEntry(int argc, char* argv[]);
int CommandListEntries(int argc, char* argv[]);
int CommandClearEntries(int argc, char* argv[]);
int CommandAddDisk(int argc, char* argv[]);
int CommandRemoveDisk(int argc, char* argv[]);
int CommandResetDisk(int argc, char* argv[]);
int CommandTrimDisk(int argc, char* argv[]);
int CommandAddBacking(int argc, char* argv[]);
int CommandRemoveBacking(int argc, char* argv[]);
int CommandListBackings(int argc, char* argv[]);

353
src/template.c Normal file
View File

@ -0,0 +1,353 @@
#include "template.h"
#include "image.h"
#include "disk.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <libgen.h>
#include <sys/stat.h>
Result IsTemplateIdentifierValid(const char* template_id, bool* _valid) {
*_valid = false;
// Check that the template identifier is not NULL
if (template_id == NULL)
return SUCCESS;
// Check that the template identifier is not empty or too long
size_t length = strlen(template_id);
if (length == 0 || length > MAX_TEMPLATE_ID_LENGTH)
return SUCCESS;
// Check that the template identifier only contains allowed characters
for (size_t i = 0; i < length; i++) {
char c = template_id[i];
if (c >= 'a' && c <= 'z')
continue;
if (c >= 'A' && c <= 'Z')
continue;
if (c >= '0' && c <= '9')
continue;
if (c == '_' || c == '-' || c == '.' || c == ' ')
continue;
return SUCCESS;
}
// Check that the template identifier is not a reserved name
if (strcmp(template_id, ".") == 0 || strcmp(template_id, "..") == 0)
return SUCCESS;
// Check that the template identifier starts with a number (the order)
if (template_id[0] < '0' || template_id[0] > '9')
return SUCCESS;
*_valid = true;
return SUCCESS;
}
Result GetTemplateIdentifierOrder(const char* template_id, uint64_t* _order) {
// Check that the template identifier is valid
bool valid;
if (IsTemplateIdentifierValid(template_id, &valid) != SUCCESS || !valid)
return FAILURE;
// Get the order from the template identifier
*_order = strtoull(template_id, NULL, 10);
return SUCCESS;
}
Result GetTemplateIdentifierDescription(const char* template_id, char** _description) {
// Check that the template identifier is valid
bool valid;
if (IsTemplateIdentifierValid(template_id, &valid) != SUCCESS || !valid)
return FAILURE;
// Get the length of the order
size_t length = 0;
while (template_id[length] >= '0' && template_id[length] <= '9')
length++;
// Get the substring after the order
return Substring(template_id, length, strlen(template_id), _description);
}
Result CreateTemplateIdentifier(uint64_t order, const char* template_description, char** _template_id) {
// Check that the description does not start with a number
if (template_description[0] >= '0' && template_description[0] <= '9')
return FAILURE;
// Format the template identifier
if (Format(_template_id, "%llu%s", order, template_description) != SUCCESS)
return FAILURE;
// Check that the template identifier is valid
bool valid;
return IsTemplateIdentifierValid(*_template_id, &valid) == SUCCESS && valid ? SUCCESS : FAILURE;
}
Result GetTemplatePoolPath(char** _path) {
return Format(_path, "/var/lib/sandbox/templates");
}
Result GetTemplateDiskPath(const char* template_id, char** _path) {
// Check that the template identifier is valid
bool valid;
if (IsTemplateIdentifierValid(template_id, &valid) != SUCCESS || !valid)
return FAILURE;
// Get the path to the template pool
char* pool_path;
if (GetTemplatePoolPath(&pool_path) != SUCCESS)
return FAILURE;
// Format the template disk path
Result result = Format(_path, "%s/%s", pool_path, template_id);
free(pool_path);
return result;
}
Result DoesTemplateExist(const char* template_id, bool* _exists) {
// Get the path to the template disk
char* disk_path;
if (GetTemplateDiskPath(template_id, &disk_path) != SUCCESS)
return FAILURE;
// Check if the template disk exists
struct stat st;
*_exists = stat(disk_path, &st) == 0 && S_ISDIR(st.st_mode);
free(disk_path);
return SUCCESS;
}
Result AddTemplate(const char* template_id, const char* image_id) {
// Check that the template does not already exist
bool exists;
if (DoesTemplateExist(template_id, &exists) != SUCCESS)
return FAILURE;
if (exists) {
Log(LOG_LEVEL_ERROR, "Template '%s' already exists.", template_id);
return FAILURE;
}
// Check that the image exists
bool image_exists;
if (DoesImageDiskExist(image_id, &image_exists) != SUCCESS)
return FAILURE;
if (!image_exists) {
Log(LOG_LEVEL_ERROR, "Image '%s' does not exist.", image_id);
return FAILURE;
}
// Check that the image has a disk
bool image_has_disk;
if (DoesImageDiskExist(image_id, &image_has_disk) != SUCCESS)
return FAILURE;
if (!image_has_disk) {
Log(LOG_LEVEL_ERROR, "Image '%s' does not have a disk.", image_id);
return FAILURE;
}
// Get the path of the image disk
char* image_disk_path;
if (GetImageDiskPath(image_id, &image_disk_path) != SUCCESS)
return FAILURE;
// Get information about the image disk
DiskInfo image_disk_info;
if (GetDiskInfo(image_disk_path, &image_disk_info) != SUCCESS) {
free(image_disk_path);
return FAILURE;
}
// If the image disk has a backing, check that the backing is an existing template
if (image_disk_info.backing_identifier != NULL) {
bool backing_exists;
if (DoesTemplateExist(image_disk_info.backing_identifier, &backing_exists) != SUCCESS) {
free(image_disk_path);
FreeDiskInfo(&image_disk_info);
return FAILURE;
}
if (!backing_exists) {
Log(LOG_LEVEL_ERROR, "Backing template '%s' of image '%s' does not exist.", image_disk_info.backing_identifier, image_id);
free(image_disk_path);
FreeDiskInfo(&image_disk_info);
return FAILURE;
}
}
// Get the path to the template disk
char* template_disk_path;
if (GetTemplateDiskPath(template_id, &template_disk_path) != SUCCESS) {
free(image_disk_path);
FreeDiskInfo(&image_disk_info);
return FAILURE;
}
// Copy the image disk to the template disk
Result result = CopyFile(image_disk_path, template_disk_path);
free(image_disk_path);
if (result != SUCCESS) {
free(template_disk_path);
FreeDiskInfo(&image_disk_info);
return FAILURE;
}
// If the image disk has a backing, reback the template disk to be relative
if (image_disk_info.backing_identifier != NULL)
if (RebackDisk(template_disk_path, image_disk_info.backing_identifier) != SUCCESS) {
unlink(template_disk_path);
free(template_disk_path);
FreeDiskInfo(&image_disk_info);
return FAILURE;
}
// Free the template disk path and image disk info
free(template_disk_path);
FreeDiskInfo(&image_disk_info);
// Return success
return SUCCESS;
}
Result RemoveTemplate(const char* template_id) {
// Check that the template exists
bool exists;
if (DoesTemplateExist(template_id, &exists) != SUCCESS)
return FAILURE;
if (!exists) {
Log(LOG_LEVEL_ERROR, "Template '%s' does not exist.", template_id);
return FAILURE;
}
// Get the path to the template disk
char* disk_path;
if (GetTemplateDiskPath(template_id, &disk_path) != SUCCESS)
return FAILURE;
// Remove the template disk
int result = rmdir(disk_path);
// TODO: Remove any template that is based on this template
free(disk_path);
return result == 0 ? SUCCESS : FAILURE;
}
Result ListTemplates(char*** _template_ids) {
// Get the path to the template pool
char* pool_path;
if (GetTemplatePoolPath(&pool_path) != SUCCESS)
return FAILURE;
// Open the template pool directory
DIR* pool_dir = opendir(pool_path);
if (pool_dir == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to open template pool directory '%s' (%s).", pool_path, strerror(errno));
free(pool_path);
return FAILURE;
}
free(pool_path);
// Allocate an initial array, one element is reserved for the NULL terminator
*_template_ids = malloc(sizeof(char*));
if (*_template_ids == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for template identifiers.");
closedir(pool_dir);
return FAILURE;
}
(*_template_ids)[0] = NULL;
// Iterate over the template pool directory
int count = 0;
struct dirent* entry;
while ((entry = readdir(pool_dir)) != NULL) {
// Skip the current and parent directory
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
// Check that the entry actually exists
bool exists;
if (DoesTemplateExist(entry->d_name, &exists) != SUCCESS || !exists)
continue;
// Allocate a new element in the array
char** new_template_ids = realloc(*_template_ids, (count + 2) * sizeof(char*)); // One element is reserved for the NULL terminator
if (new_template_ids == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for template identifiers.");
closedir(pool_dir);
for (int i = 0; i < count; i++)
free((*_template_ids)[i]);
free(*_template_ids);
return FAILURE;
}
*_template_ids = new_template_ids;
// Copy the entry name into the array
if (Duplicate(entry->d_name, &(*_template_ids)[count]) != SUCCESS) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for template identifier.");
closedir(pool_dir);
for (int i = 0; i < count; i++)
free((*_template_ids)[i]);
free(*_template_ids);
return FAILURE;
}
// Null-terminate the array
(*_template_ids)[count + 1] = NULL;
// Increment the count
count++;
}
// Close the template pool directory
closedir(pool_dir);
return SUCCESS;
}
Result GetHighestOrderTemplate(char** _template_id) {
*_template_id = NULL;
// Get the list of template identifiers
char** template_ids;
if (ListTemplates(&template_ids) != SUCCESS)
return FAILURE;
// Find the highest order template identifier
uint64_t highest_order = -1;
char* highest_template_id = NULL;
for (int i = 0; template_ids[i] != NULL; i++) {
uint64_t order;
if (GetTemplateIdentifierOrder(template_ids[i], &order) != SUCCESS)
continue;
if (order > highest_order) {
highest_order = order;
highest_template_id = template_ids[i];
}
}
// Duplicate the highest order template identifier
if (Duplicate(highest_template_id, _template_id) != SUCCESS)
return FAILURE;
for (int i = 0; template_ids[i] != NULL; i++)
free(template_ids[i]);
free(template_ids);
return SUCCESS;
}

19
src/template.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include "utils.h"
#define MAX_TEMPLATE_ID_LENGTH 64
Result IsTemplateIdentifierValid(const char* template_id, bool* _valid);
Result GetTemplateIdentifierOrder(const char* template_id, uint64_t* _order);
Result GetTemplateIdentifierDescription(const char* template_id, char** _description);
Result CreateTemplateIdentifier(uint64_t order, const char* template_description, char** _template_id);
Result GetTemplatePoolPath(char** _path);
Result GetTemplateDiskPath(const char* template_id, char** _path);
Result DoesTemplateExist(const char* template_id, bool* _exists);
Result AddTemplate(const char* template_id, const char* image_id);
Result RemoveTemplate(const char* template_id);
Result ListTemplates(char*** _template_ids);
Result GetHighestOrderTemplate(char** _template_id);

View File

@ -61,7 +61,7 @@ void Log(LogLevel level, const char* format, ...) {
va_end(args); va_end(args);
} }
Status Format(char** _string, const char* fmt, ...) { Result Format(char** _string, const char* fmt, ...) {
*_string = NULL; *_string = NULL;
va_list args; va_list args;
@ -92,7 +92,41 @@ Status Format(char** _string, const char* fmt, ...) {
return SUCCESS; return SUCCESS;
} }
Status FormatSize(uint64_t size, char** _size_str) { Result Duplicate(const char* string, char** _duplicate) {
*_duplicate = NULL;
if (string == NULL)
return SUCCESS;
*_duplicate = strdup(string);
if (*_duplicate == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to duplicate the string (%s).", strerror(errno));
return FAILURE;
}
return SUCCESS;
}
Result Substring(const char* string, size_t start, size_t end, char** _substring) {
if (start > end) {
Log(LOG_LEVEL_ERROR, "Invalid substring range.");
return FAILURE;
}
size_t length = end - start;
*_substring = malloc(length + 1);
if (*_substring == NULL) {
Log(LOG_LEVEL_ERROR, "Failed to allocate memory for the substring (%s).", strerror(errno));
return FAILURE;
}
memcpy(*_substring, string + start, length);
(*_substring)[length] = '\0';
return SUCCESS;
}
Result FormatSize(uint64_t size, char** _size_str) {
*_size_str = NULL; *_size_str = NULL;
// Determine the unit // Determine the unit
@ -119,7 +153,7 @@ Status FormatSize(uint64_t size, char** _size_str) {
return Format(_size_str, "%.2f %s", value, unit); return Format(_size_str, "%.2f %s", value, unit);
} }
Status ParseSize(const char* size_str, uint64_t* _size) { Result ParseSize(const char* size_str, uint64_t* _size) {
*_size = 0; *_size = 0;
// Parse the size // Parse the size
@ -153,7 +187,7 @@ Status ParseSize(const char* size_str, uint64_t* _size) {
return SUCCESS; return SUCCESS;
} }
Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) { Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...) {
if (_exit_code != NULL) if (_exit_code != NULL)
*_exit_code = -1; *_exit_code = -1;
if (_stdout != NULL) if (_stdout != NULL)
@ -301,7 +335,7 @@ Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char
return SUCCESS; return SUCCESS;
} }
Status ReadFileDescriptor(int fd, char** _content) { Result ReadFileDescriptor(int fd, char** _content) {
*_content = NULL; *_content = NULL;
// Allocate a buffer for the content // Allocate a buffer for the content
@ -333,7 +367,7 @@ Status ReadFileDescriptor(int fd, char** _content) {
return SUCCESS; return SUCCESS;
} }
Status WriteFileDescriptor(int fd, const char* content) { Result WriteFileDescriptor(int fd, const char* content) {
size_t length = strlen(content); size_t length = strlen(content);
ssize_t bytes_written = 0; ssize_t bytes_written = 0;
@ -350,7 +384,7 @@ Status WriteFileDescriptor(int fd, const char* content) {
return SUCCESS; return SUCCESS;
} }
Status ReadFile(const char* path, char** _content) { Result ReadFile(const char* path, char** _content) {
*_content = NULL; *_content = NULL;
// Open the file // Open the file
@ -361,7 +395,7 @@ Status ReadFile(const char* path, char** _content) {
} }
// Read the file // Read the file
Status status = ReadFileDescriptor(file, _content); Result status = ReadFileDescriptor(file, _content);
// Close the file // Close the file
close(file); close(file);
@ -369,7 +403,7 @@ Status ReadFile(const char* path, char** _content) {
return status; return status;
} }
Status WriteFile(const char* path, const char* content) { Result WriteFile(const char* path, const char* content) {
// Open the file // Open the file
int file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); int file = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (file == -1) { if (file == -1) {
@ -378,7 +412,7 @@ Status WriteFile(const char* path, const char* content) {
} }
// Write the content // Write the content
Status status = WriteFileDescriptor(file, content); Result status = WriteFileDescriptor(file, content);
// Close the file // Close the file
close(file); close(file);
@ -386,7 +420,7 @@ Status WriteFile(const char* path, const char* content) {
return status; return status;
} }
Status CopyFile(const char* source_path, const char* destination_path) { Result CopyFile(const char* source_path, const char* destination_path) {
// Open the source file // Open the source file
int source_file = open(source_path, O_RDONLY); int source_file = open(source_path, O_RDONLY);
if (source_file == -1) { if (source_file == -1) {

View File

@ -2,11 +2,12 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
typedef enum { typedef enum {
SUCCESS, SUCCESS,
FAILURE FAILURE
} Status; } Result;
typedef enum { typedef enum {
LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG,
@ -20,16 +21,18 @@ extern LogLevel LOG_LEVEL;
void SetLogLevel(LogLevel level); void SetLogLevel(LogLevel level);
void Log(LogLevel level, const char* format, ...); void Log(LogLevel level, const char* format, ...);
Status Format(char** _string, const char* fmt, ...); Result Format(char** _string, const char* fmt, ...);
Result Duplicate(const char* string, char** _duplicate);
Result Substring(const char* string, size_t start, size_t end, char** _substring);
Status FormatSize(uint64_t size, char** _size_str); Result FormatSize(uint64_t size, char** _size_str);
Status ParseSize(const char* size_str, uint64_t* _size); Result ParseSize(const char* size_str, uint64_t* _size);
Status RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...); Result RunExecutable(int* _exit_code, char** _stdout, char** _stderr, const char* executable, ...);
Status ReadFileDescriptor(int fd, char** _content); Result ReadFileDescriptor(int fd, char** _content);
Status WriteFileDescriptor(int fd, const char* content); Result WriteFileDescriptor(int fd, const char* content);
Status ReadFile(const char* path, char** _content); Result ReadFile(const char* path, char** _content);
Status WriteFile(const char* path, const char* content); Result WriteFile(const char* path, const char* content);
Status CopyFile(const char* source_path, const char* destination_path); Result CopyFile(const char* source_path, const char* destination_path);