Made small modifications
This commit is contained in:
parent
c9f76746b4
commit
0da7474158
332
src/backing.c
332
src/backing.c
@ -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;
|
|
||||||
}
|
|
@ -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);
|
|
354
src/disk.c
354
src/disk.c
@ -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;
|
||||||
}
|
}
|
15
src/disk.h
15
src/disk.h
@ -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);
|
|
507
src/entry.c
507
src/entry.c
File diff suppressed because it is too large
Load Diff
27
src/entry.h
27
src/entry.h
@ -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
415
src/image.c
Normal 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
24
src/image.h
Normal 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);
|
608
src/sandbox.c
608
src/sandbox.c
File diff suppressed because it is too large
Load Diff
@ -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
353
src/template.c
Normal 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
19
src/template.h
Normal 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);
|
56
src/utils.c
56
src/utils.c
@ -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) {
|
||||||
|
23
src/utils.h
23
src/utils.h
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user