497 lines
11 KiB
C
Executable File
497 lines
11 KiB
C
Executable File
#include "backing.h"
|
|
|
|
#include "sandbox.h"
|
|
#include "container.h"
|
|
#include "disk.h"
|
|
|
|
#include <md5.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <libgen.h>
|
|
#include <sys/stat.h>
|
|
|
|
result_t check_backing_identifier(const char* backing) {
|
|
// Check that the identifier is not null
|
|
if (backing == NULL)
|
|
return failure("Backing identifier cannot be null.");
|
|
|
|
// Check that the identifier is the length of a md5 hash
|
|
size_t length = strlen(backing);
|
|
if (length != MD5_DIGEST_STRING_LENGTH - 1) // -1 because the string is null-terminated
|
|
return failure("Backing identifier must be %d characters long.", MD5_DIGEST_STRING_LENGTH - 1);
|
|
|
|
// Check that the identifier only contains allowed characters
|
|
for (size_t i = 0; i < length; i++) {
|
|
char c = backing[i];
|
|
if (c >= 'a' && c <= 'f')
|
|
continue;
|
|
if (c >= '0' && c <= '9')
|
|
continue;
|
|
|
|
return failure("Backing identifier cannot contain the character '%c' at index %d.", c, i);
|
|
}
|
|
|
|
return success();
|
|
}
|
|
|
|
result_t check_backing_exists(const char* backing) {
|
|
// Get the backing path
|
|
char* path;
|
|
result_t result = get_backing_path(&path, backing);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Check that the backing exists
|
|
struct stat st;
|
|
errno = 0;
|
|
if (stat(path, &st) != 0) {
|
|
if (errno == ENOENT)
|
|
result = failure("Backing '%s' does not exist.", backing);
|
|
else
|
|
result = failure("Failed to check if backing '%s' exists (%s).", backing, strerror(errno));
|
|
|
|
free(path);
|
|
return result;
|
|
}
|
|
|
|
// Check that the backing is a file
|
|
if (!S_ISREG(st.st_mode)) {
|
|
result = failure("Backing '%s' is not a regular file.", backing);
|
|
free(path);
|
|
return result;
|
|
}
|
|
|
|
// Free the backing path
|
|
free(path);
|
|
|
|
return success();
|
|
}
|
|
|
|
result_t get_backing_pool_path(char** _path) {
|
|
// Initialize the output parameters
|
|
*_path = NULL;
|
|
|
|
return format(_path, "/var/lib/sandbox/backings");
|
|
}
|
|
|
|
result_t get_backing_path(char** _path, const char* backing) {
|
|
// Initialize the output parameters
|
|
*_path = NULL;
|
|
|
|
// Check the backing identifier
|
|
result_t result = check_backing_identifier(backing);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Get the backing pool path
|
|
char* pool_path;
|
|
result = get_backing_pool_path(&pool_path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Format the backing path
|
|
result = format(_path, "%s/%s", pool_path, backing);
|
|
|
|
free(pool_path);
|
|
return result;
|
|
}
|
|
|
|
result_t get_backing_default_path(char** _path) {
|
|
// Initialize the output parameters
|
|
*_path = NULL;
|
|
|
|
// Get the backing pool path
|
|
char* pool_path;
|
|
result_t result = get_backing_pool_path(&pool_path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Format the backing path
|
|
result = format(_path, "%s/default", pool_path);
|
|
|
|
free(pool_path);
|
|
return result;
|
|
}
|
|
|
|
result_t get_backing_temp_path(char** _path) {
|
|
// Initialize the output parameters
|
|
*_path = NULL;
|
|
|
|
// Get the backing pool path
|
|
char* pool_path;
|
|
result_t result = get_backing_pool_path(&pool_path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Format the backing path
|
|
result = format(_path, "%s/temp", pool_path);
|
|
|
|
free(pool_path);
|
|
return result;
|
|
}
|
|
|
|
result_t add_backing(char** _backing, const char* container) {
|
|
// Initialize the output parameters
|
|
*_backing = NULL;
|
|
|
|
// Get the container path
|
|
char* path;
|
|
result_t result = get_container_path(&path, container);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Import the container as a backing
|
|
result = import_backing(_backing, path);
|
|
|
|
free(path);
|
|
return result;
|
|
}
|
|
|
|
result_t import_backing(char** _backing, const char* disk) {
|
|
// Initialize the output parameters
|
|
*_backing = NULL;
|
|
|
|
// Get the temporary backing path
|
|
char* temp_path;
|
|
result_t result = get_backing_temp_path(&temp_path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Copy the disk to the temporary backing path
|
|
result = copy_file(disk, temp_path);
|
|
if (result != success()) {
|
|
free(temp_path);
|
|
return result;
|
|
}
|
|
|
|
// Get the disk info to know if the disk is backed
|
|
disk_info_t info;
|
|
result = get_disk_info(&info, temp_path);
|
|
if (result != success()) {
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
return result;
|
|
}
|
|
|
|
if (info.backing_path != NULL) {
|
|
// Get the backing identifier of the parent
|
|
errno = 0;
|
|
char* backing = strdup(basename(info.backing_path));
|
|
if (backing == NULL) {
|
|
free_disk_info(&info);
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
return failure("Failed to get the backing identifier of the parent of '%s' (%s).", disk, strerror(errno));
|
|
}
|
|
|
|
// Check that the backing exists
|
|
result = check_backing_exists(backing);
|
|
if (result != success()) {
|
|
free_disk_info(&info);
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
free(backing);
|
|
return result;
|
|
}
|
|
|
|
// Reback the disk to the parent
|
|
result = reback_disk(temp_path, backing);
|
|
if (result != success()) {
|
|
free_disk_info(&info);
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
free(backing);
|
|
return result;
|
|
}
|
|
|
|
free(backing);
|
|
}
|
|
|
|
free_disk_info(&info);
|
|
|
|
// Get the md5 hash of the disk as the backing identifier
|
|
char* backing;
|
|
result = md5sum(&backing, temp_path);
|
|
if (result != success()) {
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
return result;
|
|
}
|
|
|
|
// Check if the backing already exists
|
|
result = check_backing_exists(backing);
|
|
if (result == success()) {
|
|
result = failure("Backing '%s' already exists.", backing);
|
|
free(backing);
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
return result;
|
|
}
|
|
|
|
// Get the path of the final backing
|
|
char* backing_path;
|
|
result = get_backing_path(&backing_path, backing);
|
|
if (result != success()) {
|
|
free(backing);
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
return result;
|
|
}
|
|
|
|
// Move the temporary backing to the final backing path
|
|
errno = 0;
|
|
if (rename(temp_path, backing_path) != 0) {
|
|
result = failure("Failed to move the temporary backing to the final backing path (%s).", strerror(errno));
|
|
free(backing);
|
|
free(backing_path);
|
|
remove(temp_path);
|
|
free(temp_path);
|
|
return result;
|
|
}
|
|
|
|
free(backing_path);
|
|
free(temp_path);
|
|
|
|
*_backing = backing;
|
|
return result;
|
|
}
|
|
|
|
result_t remove_backing(const char* backing) {
|
|
// Check that the backing exists
|
|
result_t result = check_backing_exists(backing);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Get the backing path
|
|
char* path;
|
|
result = get_backing_path(&path, backing);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Remove the backing
|
|
errno = 0;
|
|
remove(path);
|
|
|
|
// Check for errors during the removal
|
|
if (errno != 0) {
|
|
free(path);
|
|
return failure("Failed to remove backing '%s' (%s).", backing, strerror(errno));
|
|
}
|
|
|
|
free(path);
|
|
return success();
|
|
}
|
|
|
|
bool backing_filter(const char* file) {
|
|
// Check that a backing with the same name exists
|
|
return check_backing_exists(file) == success();
|
|
}
|
|
|
|
result_t list_backings(char*** _backings) {
|
|
// Initialize the output parameters
|
|
*_backings = NULL;
|
|
|
|
// Get the backing pool path
|
|
char* pool_path;
|
|
result_t result = get_backing_pool_path(&pool_path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// List the backings
|
|
result = list_files(_backings, pool_path, &backing_filter);
|
|
|
|
free(pool_path);
|
|
return result;
|
|
}
|
|
|
|
result_t get_default_backing(char** _backing) {
|
|
// Initialize the output parameters
|
|
*_backing = NULL;
|
|
|
|
// Get the backing default path
|
|
char* path;
|
|
result_t result = get_backing_default_path(&path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
char* backing;
|
|
|
|
// Read the default backing
|
|
result = read_file(&backing, path);
|
|
|
|
free(path);
|
|
|
|
if (result != success())
|
|
return failure("No default backing configured.");
|
|
|
|
// Check that the backing is valid
|
|
result = check_backing_identifier(backing);
|
|
if (result != success()) {
|
|
free(backing);
|
|
return failure("Default backing '%s' is not a valid backing identifier.", backing);
|
|
}
|
|
|
|
// Check that the backing exists
|
|
result = check_backing_exists(backing);
|
|
if (result != success()) {
|
|
free(backing);
|
|
return failure("Default backing '%s' does not exist.", backing);
|
|
}
|
|
|
|
*_backing = backing;
|
|
return result;
|
|
}
|
|
|
|
result_t set_default_backing(const char* backing) {
|
|
// Get the backing default path
|
|
char* path;
|
|
result_t result = get_backing_default_path(&path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// If the backing is null, remove the default backing file
|
|
if (backing == NULL) {
|
|
errno = 0;
|
|
remove(path);
|
|
free(path);
|
|
|
|
if (errno == ENOENT)
|
|
return success();
|
|
else if (errno != 0)
|
|
return failure("Failed to remove the default backing file (%s).", strerror(errno));
|
|
|
|
return success();
|
|
} else {
|
|
// Check that the backing exists
|
|
result = check_backing_exists(backing);
|
|
if (result != success()) {
|
|
free(path);
|
|
return result;
|
|
}
|
|
|
|
// Write the default backing
|
|
result = write_file(path, backing);
|
|
|
|
free(path);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
result_t get_backing_parent(char** _parent, const char* backing) {
|
|
// Initialize the output parameters
|
|
*_parent = NULL;
|
|
|
|
// Get the backing path
|
|
char* path;
|
|
result_t result = get_backing_path(&path, backing);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Get the disk info
|
|
disk_info_t info;
|
|
result = get_disk_info(&info, path);
|
|
if (result != success()) {
|
|
free(path);
|
|
return result;
|
|
}
|
|
|
|
// Free the backing path as it is not needed anymore
|
|
free(path);
|
|
|
|
// Check if the disk is backed
|
|
if (info.backing_path == NULL) {
|
|
free_disk_info(&info);
|
|
return success();
|
|
}
|
|
|
|
// Get the backing identifier of the parent
|
|
errno = 0;
|
|
char* parent = strdup(basename(info.backing_path));
|
|
|
|
// Free the disk info as it is not needed anymore
|
|
free_disk_info(&info);
|
|
|
|
if (parent == NULL)
|
|
return failure("Failed to get the backing identifier of the parent of '%s' (%s).", backing, strerror(errno));
|
|
|
|
*_parent = parent;
|
|
return success();
|
|
}
|
|
|
|
result_t get_backing_info(disk_info_t* _info, const char* backing) {
|
|
// Initialize the output parameters
|
|
_info->size = 0;
|
|
_info->allocated = 0;
|
|
_info->backing_path = NULL;
|
|
|
|
// Check that the backing exists
|
|
result_t result = check_backing_exists(backing);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Get the backing path
|
|
char* path;
|
|
result = get_backing_path(&path, backing);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Get the disk information
|
|
result = get_disk_info(_info, path);
|
|
|
|
free(path);
|
|
|
|
return result;
|
|
}
|
|
|
|
result_t sync_backing_pool(void) {
|
|
// Check that the file /etc/sandbox.d/sync exists
|
|
struct stat st;
|
|
errno = 0;
|
|
if (stat("/etc/sandbox.d/sync", &st) != 0) {
|
|
if (errno == ENOENT)
|
|
return success();
|
|
else
|
|
return failure("Failed to check if /etc/sandbox.d/sync exists (%s).", strerror(errno));
|
|
}
|
|
|
|
// Execute /etc/sandbox.d/sync with the backing pool as the working directory
|
|
int exit_code;
|
|
// char* stdoutbuf;
|
|
char* stderrbuf;
|
|
|
|
// Get the path of the backing pool
|
|
char* pool_path;
|
|
result_t result = get_backing_pool_path(&pool_path);
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Execute the sync script
|
|
result = execute(&exit_code, NULL, &stderrbuf, pool_path, "/etc/sandbox.d/sync", NULL);
|
|
|
|
free(pool_path);
|
|
|
|
// Check for errors during the execution
|
|
if (result != success())
|
|
return result;
|
|
|
|
// Check the exit code
|
|
if (exit_code != 0) {
|
|
// Remove all newlines from the stderr buffer
|
|
for (char* c = stderrbuf; *c != '\0'; c++)
|
|
if (*c == '\n')
|
|
*c = ' ';
|
|
|
|
result = failure("Failed to synchronize the backing pool (%s).", stderrbuf);
|
|
free(stderrbuf);
|
|
return result;
|
|
}
|
|
|
|
// Free the stderr buffer as it is not needed anymore
|
|
free(stderrbuf);
|
|
|
|
return success();
|
|
}
|