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);
|
|
432
src/disk.c
432
src/disk.c
File diff suppressed because it is too large
Load Diff
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);
|
610
src/sandbox.c
610
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