Perfected the disk utilities

This commit is contained in:
Alexei KADIR 2024-02-15 19:42:22 +01:00
parent e6bc757343
commit 01ea511b2d
10 changed files with 581 additions and 1183 deletions

View File

@ -1,227 +0,0 @@
#include "backing.h"
#include "utils.h"
#include "sandbox.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <time.h>
#include <stdio.h>
#include <sys/stat.h>
char* get_backings_path(void) {
return format("%s/%s", MASTER_DIRECTORY, BACKINGS_DIRECTORY);
}
bool is_valid_backing_name(const char* name) {
if (name == NULL)
return false;
// Check that the name is not empty
size_t length = strlen(name);
if (length == 0)
return false;
// Check that the name starts with a number, corresponding to the timestamp of the backing disk creation
size_t timestamp_length = 0;
for (size_t i = 0; i < length; i++)
if (name[i] >= '0' && name[i] <= '9')
timestamp_length++;
else
break;
if (timestamp_length == 0)
return false;
return true;
}
char* get_backing_path(const char* backing) {
char* backings_path = get_backings_path();
if (backings_path == NULL)
return NULL;
// Combine the backings path with the backing name
char* backing_path = format("%s/%s", backings_path, backing);
// Free the backings path
free(backings_path);
return backing_path;
}
bool backing_exists(const char* backing) {
char* backing_path = get_backing_path(backing);
if (backing_path == NULL)
return false;
// Check if the backing path exists
struct stat statbuf;
int result = stat(backing_path, &statbuf);
// Free the backing path
free(backing_path);
if (result != 0)
return false;
if (!S_ISREG(statbuf.st_mode))
return false;
return true;
}
char** list_backings(void) {
char* path = get_backings_path();
if (path == NULL)
return NULL;
// Open the directory
DIR* dir = opendir(path);
if (dir == NULL) {
log_msg(LOG_ERROR, "Failed to open directory '%s' (%s).", path, strerror(errno));
free(path);
return NULL;
}
free(path);
// Count the number of entries
size_t count = 0;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL)
if (is_valid_backing_name(entry->d_name) && backing_exists(entry->d_name))
count++;
// Allocate the array of strings
char** backings = malloc((count + 1) * sizeof(char*));
if (backings == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the backings array.");
closedir(dir);
return NULL;
}
// Fill the array of strings
rewinddir(dir);
size_t index = 0;
while ((entry = readdir(dir)) != NULL) {
if (is_valid_backing_name(entry->d_name) && backing_exists(entry->d_name)) {
backings[index] = strdup(entry->d_name);
if (backings[index] == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the backing name.");
for (size_t i = 0; i < index; i++)
free(backings[i]);
free(backings);
closedir(dir);
return NULL;
}
index++;
}
}
// Terminate the array of strings
backings[count] = NULL;
// Close the directory
closedir(dir);
return backings;
}
uint64_t get_backing_creation_time(const char* backing) {
size_t length = strlen(backing);
size_t timestamp_length = 0;
// Find the length of the timestamp
for (size_t i = 0; i < length; i++)
if (backing[i] >= '0' && backing[i] <= '9')
timestamp_length++;
else
break;
// Extract the timestamp
char* timestamp = strndup(backing, timestamp_length);
if (timestamp == NULL)
return 0;
// Convert the timestamp to a number
uint64_t creation_time = strtoull(timestamp, NULL, 10);
// Free the timestamp
free(timestamp);
return creation_time;
}
char* find_latest_backing(void) {
char** backings = list_backings();
if (backings == NULL)
return NULL;
// Find the latest backing disk
char* latest_backing = NULL;
uint64_t latest_time = 0;
for (size_t i = 0; backings[i] != NULL; i++) {
uint64_t time = get_backing_creation_time(backings[i]);
if (time >= latest_time) {
latest_time = time;
latest_backing = backings[i];
}
}
// Duplicate the latest backing disk
if (latest_backing != NULL)
latest_backing = strdup(latest_backing);
// Free the backings
for (size_t i = 0; backings[i] != NULL; i++)
free(backings[i]);
free(backings);
return latest_backing;
}
bool create_backing(const char* disk, const char* description) {
// Create the backing disk name
uint64_t timestamp = (uint64_t)time(NULL);
char* backing_name = NULL;
if (description == NULL || strlen(description) == 0)
backing_name = format("%lu", timestamp);
else
backing_name = format("%lu-%s", timestamp, description);
if (backing_name == NULL) {
log_msg(LOG_ERROR, "Failed to create the backing disk name.");
return false;
}
// Create the backing disk path
char* backings_path = get_backings_path();
if (backings_path == NULL) {
free(backing_name);
return false;
}
char* backing_path = format("%s/%s", backings_path, backing_name);
free(backing_name);
free(backings_path);
if (backing_path == NULL)
return false;
// Copy the disk to the backing disk
if (!copy_file(disk, backing_path, 0755, 0, 0)) {
free(backing_path);
return false;
}
// Free the backing disk path
free(backing_path);
return true;
}

View File

@ -1,42 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
/// @brief Returns the directory path where the backing disks are stored.
/// @return The directory path where the backing disks are stored. The caller is responsible for freeing the returned string. This function can return NULL.
char* get_backings_path(void);
/// @brief Checks if the specified backing disk name is valid.
/// @param name The backing disk name to check.
/// @return true if the backing disk name is valid, otherwise false.
bool is_valid_backing_name(const char* name);
/// @brief Returns the path to the specified backing disk.
/// @param backing The valid backing disk name.
/// @return The path to the specified backing disk. The caller is responsible for freeing the returned string. This function can return NULL.
char* get_backing_path(const char* backing);
/// @brief Checks if the specified backing disk exists.
/// @param backing The valid backing disk name.
/// @return true if the backing disk exists, otherwise false.
bool backing_exists(const char* backing);
/// @brief Lists the backing disks.
/// @return The list of backing disks. The caller is responsible for freeing the returned array and strings. This function can return NULL.
char** list_backings(void);
/// @brief Get the creation time of the specified backing disk.
/// @param backing The valid backing disk name.
/// @return The creation time of the specified backing disk. If the backing disk does not exist, this function returns 0.
uint64_t get_backing_creation_time(const char* backing);
/// @brief Finds the latest backing disk.
/// @return The latest backing disk. The caller is responsible for freeing the returned string. This function can return NULL.
char* find_latest_backing(void);
/// @brief Creates a new backing disk.
/// @param disk The disk to use as the backing disk. Warning: the disk must be part of the backing disks.
/// @param description The description of the backing disk.
/// @return true if the backing disk was created, otherwise false.
bool create_backing(const char* disk, const char* description);

View File

@ -2,120 +2,233 @@
#include "utils.h"
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <json-c/json.h>
bool create_empty_disk(const char* disk, uint64_t size) {
char* stdoutb = NULL;
char* stderrb = NULL;
bool create_empty_disk(const char* path, uint64_t size) {
char* errb = NULL;
// Create an empty disk
int result = execute(&stdoutb, &stderrb, "qemu-img", "create", "-f", "qcow2", disk, format("%" PRIu64, size), NULL);
// Convert the size to a string
char* size_str = format("%lu", size);
if (size_str == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the size string.");
return false;
}
// Check if the command was successful
if (result != 0) {
// Remove the newline character from the stderr buffer
size_t length = strlen(stderrb);
for (size_t i = 0; i < length; i++)
if (stderrb[i] == '\n')
stderrb[i] = ' ';
log_msg(LOG_ERROR, "Failed to create the empty disk %s (%s).", disk, stderrb);
// Create the disk
int ret = exec(NULL, &errb, "qemu-img", "create", "-f", "qcow2", path, size_str, NULL);
free(stdoutb);
free(stderrb);
free(size_str);
// Check for errors
if (ret != 0) {
if (errb == NULL)
logmsg(LOG_ERROR, "Failed to create disk %s.", path);
else {
size_t length = strlen(errb);
for (size_t i = 0; i < length; i++)
if (errb[i] == '\n')
errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to create disk %s (%s).", path, errb);
}
return false;
}
free(stdoutb);
free(stderrb);
return true;
}
bool create_backed_disk(const char* disk, const char* backing) {
char* stdoutb = NULL;
char* stderrb = NULL;
bool create_backed_disk(const char* path, const char* backing_disk) {
char* errb = NULL;
// Create a backed disk
int result = execute(&stdoutb, &stderrb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing, disk, NULL);
// Create the disk
int ret = exec(NULL, &errb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_disk, path, NULL);
// Check if the command was successful
if (result != 0) {
// Remove the newline character from the stderr buffer
size_t length = strlen(stderrb);
for (size_t i = 0; i < length; i++)
if (stderrb[i] == '\n')
stderrb[i] = ' ';
log_msg(LOG_ERROR, "Failed to create the backed disk %s (%s).", disk, stderrb);
// Check for errors
if (ret != 0) {
if (errb == NULL)
logmsg(LOG_ERROR, "Failed to create disk %s.", path);
else {
size_t length = strlen(errb);
for (size_t i = 0; i < length; i++)
if (errb[i] == '\n')
errb[i] = ' ';
free(stdoutb);
free(stderrb);
logmsg(LOG_ERROR, "Failed to create disk %s (%s).", path, errb);
}
return false;
}
free(stdoutb);
free(stderrb);
return true;
}
bool trim_disk(const char* path) {
char* tmp_path = format("%s.tmp", path);
if (tmp_path == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the temporary disk path.");
return false;
}
DiskInfo info;
if (!get_disk_info(path, &info)) {
free(tmp_path);
return false;
}
char* errb = NULL;
// Create the trimmed disk
int ret = 0;
if (info.backing_file != NULL) {
char* backing_str = format("backing_file=%s", info.backing_file);
if (backing_str == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the backing file string.");
free_disk_info(&info);
free(tmp_path);
return false;
}
ret = exec(NULL, &errb, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_str, path, tmp_path, NULL);
free(backing_str);
} else
ret = exec(NULL, &errb, "qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, tmp_path, NULL);
// Free the disk info as we don't need it anymore
free_disk_info(&info);
// Check for errors
if (ret != 0) {
if (errb == NULL)
logmsg(LOG_ERROR, "Failed to trim disk %s.", path);
else {
size_t length = strlen(errb);
for (size_t i = 0; i < length; i++)
if (errb[i] == '\n')
errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to trim disk %s (%s).", path, errb);
}
unlink(tmp_path);
free(tmp_path);
return false;
}
// Replace the original disk with the trimmed disk
if (rename(tmp_path, path) != 0) {
logmsg(LOG_ERROR, "Failed to replace disk %s with the trimmed disk (%s).", path, strerror(errno));
unlink(tmp_path);
free(tmp_path);
return false;
}
free(tmp_path);
return true;
}
bool rebase_disk(const char* path, const char* backing_disk) {
char* errb = NULL;
// Rebase the disk
int ret = exec(NULL, &errb, "qemu-img", "rebase", "-u", "-F", "qcow2", "-b", backing_disk, path, NULL);
// Check for errors
if (ret != 0) {
if (errb == NULL)
logmsg(LOG_ERROR, "Failed to rebase disk %s.", path);
else {
size_t length = strlen(errb);
for (size_t i = 0; i < length; i++)
if (errb[i] == '\n')
errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to rebase disk %s (%s).", path, errb);
}
return false;
}
return true;
}
char* get_backing_file(const char* disk) {
char* stdoutb = NULL;
char* stderrb = NULL;
bool get_disk_info(const char* path, DiskInfo* info) {
char* outb = NULL;
char* errb = NULL;
// Get the backing file of the disk
int result = execute(&stdoutb, &stderrb, "qemu-img", "info", "--output", "json", disk, NULL);
int ret = exec(&outb, &errb, "qemu-img", "info", "--output=json", path, NULL);
// Check if the command was successful
if (result != 0) {
// Remove the newline character from the stderr buffer
size_t length = strlen(stderrb);
for (size_t i = 0; i < length; i++)
if (stderrb[i] == '\n')
stderrb[i] = ' ';
log_msg(LOG_ERROR, "Failed to get the backing file of the disk %s (%s).", disk, stderrb);
if (ret != 0) {
if (errb == NULL)
logmsg(LOG_ERROR, "Failed to get information about disk %s.", path);
else {
size_t length = strlen(errb);
for (size_t i = 0; i < length; i++)
if (errb[i] == '\n')
errb[i] = ' ';
free(stdoutb);
free(stderrb);
logmsg(LOG_ERROR, "Failed to get information about disk %s (%s).", path, errb);
}
return NULL;
free(outb);
return false;
}
free(stderrb);
// Free the error buffer, as we don't need it anymore
free(errb);
json_object* root = json_tokener_parse(outb);
// Free the output buffer, as we don't need it anymore
free(outb);
// Parse the JSON output
json_object* root = json_tokener_parse(stdoutb);
if (root == NULL) {
log_msg(LOG_ERROR, "Failed to parse the JSON output of the command.");
free(stdoutb);
return NULL;
logmsg(LOG_ERROR, "Failed to parse the JSON output from qemu-img.");
return false;
}
// Get the backing file
json_object* backing_file = NULL;
if (!json_object_object_get_ex(root, "backing-filename", &backing_file)) {
json_object_put(root);
free(stdoutb);
json_object* virtual_size = json_object_object_get(root, "virtual-size");
if (virtual_size == NULL)
info->virtual_size = 0;
else
info->virtual_size = json_object_get_int64(virtual_size);
return NULL;
json_object* actual_size = json_object_object_get(root, "actual-size");
if (actual_size == NULL)
info->actual_size = 0;
else
info->actual_size = json_object_get_int64(actual_size);
json_object* backing_file = json_object_object_get(root, "backing-filename");
if (backing_file == NULL)
info->backing_file = NULL;
else {
info->backing_file = strdup(json_object_get_string(backing_file));
if (info->backing_file == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the backing file path.");
json_object_put(root);
return false;
}
}
// Get the backing file as a string0
char* backing_file_str = strdup(json_object_get_string(backing_file));
if (backing_file_str == NULL) {
log_msg(LOG_ERROR, "Failed to get the backing file of the disk %s.", disk);
json_object_put(root);
free(stdoutb);
return NULL;
}
return backing_file_str;
json_object_put(root);
return true;
}
void free_disk_info(DiskInfo* info) {
if (info->backing_file != NULL)
free(info->backing_file);
}

View File

@ -1,32 +1,43 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stdbool.h>
/// @brief Creates an empty disk.
/// @param disk The path to the disk to create. Warning: the disk must not exist, otherwise it will be overwritten.
/// @param size The size of the disk to create in bytes.
/// @return true if the disk is created, otherwise false.
bool create_empty_disk(const char* disk, uint64_t size);
typedef struct DiskInfo {
uint64_t virtual_size;
uint64_t actual_size;
char* backing_file;
} DiskInfo;
/// @brief Creates a backed disk.
/// @param disk The path to the disk to create. Warning: the disk must not exist, otherwise it will be overwritten.
/// @param backing The path to the backing file, which exists.
/// @return true if the disk is created, otherwise false.
bool create_backed_disk(const char* disk, const char* backing);
/// @brief Creates an empty disk at the given path with the given size.
/// @param path The path to the disk. Any existing file at this path will be overwritten.
/// @param size The size of the disk, in bytes.
/// @return True if the disk was created successfully, false otherwise.
bool create_empty_disk(const char* path, uint64_t size);
/// @brief Packs the disk to reduce its size.
/// @param disk The path to the disk to pack, which exists.
/// @return true if the disk is packed, otherwise false.
bool pack_disk(const char* disk);
/// @brief Creates a disk at the given path with the given backing disk.
/// @param path The path to the disk.
/// @param backing_disk The path to the backing disk.
/// @return True if the disk was created successfully, false otherwise.
bool create_backed_disk(const char* path, const char* backing_disk);
/// @brief Moves the backing file of a disk.
/// @param disk The path to the disk, which exists.
/// @param backing The path to the new backing file, which exists.
/// @return true if the backing file is moved, otherwise false.
bool move_backing_file(const char* disk, const char* backing);
/// @brief Trims the given disk to remove any unused space.
/// @param path The path to the disk.
/// @return True if the disk was trimmed successfully, false otherwise.
bool trim_disk(const char* path);
/// @brief Gets the backing file of a disk.
/// @param disk The path to the disk, which exists.
/// @return The backing file of the disk. The caller is responsible for freeing the returned string. This function can return NULL.
char* get_backing_file(const char* disk);
/// @brief Changes the backing disk of the given disk.
/// @param path The path to the disk.
/// @param backing_disk The path to the new backing disk.
/// @return True if the disk was rebased successfully, false otherwise.
bool rebase_disk(const char* path, const char* backing_disk);
/// @brief Gets information about the given disk.
/// @param path The path to the disk.
/// @param info The DiskInfo struct to fill with information about the disk.
/// @return True if the disk information was retrieved successfully, false otherwise.
bool get_disk_info(const char* path, DiskInfo* info);
/// @brief Frees the memory used by the given DiskInfo struct.
/// @param info The DiskInfo struct to free.
void free_disk_info(DiskInfo* info);

View File

@ -1,256 +0,0 @@
#include "entry.h"
#include "sandbox.h"
#include "utils.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <dirent.h>
char* get_entries_path(void) {
return format("%s/%s", MASTER_DIRECTORY, ENTRIES_DIRECTORY);
}
bool is_valid_entry_name(const char* name) {
if (name == NULL)
return false;
// Check that the name is not empty
size_t length = strlen(name);
if (length == 0)
return false;
// Check that the name does not contain /
for (size_t i = 0; i < length; i++)
if (name[i] == '/')
return false;
// Check that the name is not . or ..
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
return false;
return true;
}
char* get_entry_path(const char* entry) {
char* entries_path = get_entries_path();
if (entries_path == NULL)
return NULL;
// Combine the entries path with the entry name
char* entry_path = format("%s/%s", entries_path, entry);
// Free the entries path
free(entries_path);
return entry_path;
}
bool entry_exists(const char* entry) {
char* entry_path = get_entry_path(entry);
if (entry_path == NULL)
return false;
// Check if the entry path exists
struct stat statbuf;
int result = stat(entry_path, &statbuf);
// Free the entry path
free(entry_path);
if (result != 0)
return false;
if (!S_ISDIR(statbuf.st_mode))
return false;
return true;
}
char* get_entry_disk_path(const char* entry) {
char* entry_path = get_entry_path(entry);
if (entry_path == NULL)
return NULL;
// Combine the entry path with the disk name
char* disk_path = format("%s/%s", entry_path, DISK_PATH);
// Free the entry path
free(entry_path);
return disk_path;
}
char** list_entries(void) {
char* path = get_entries_path();
if (path == NULL)
return NULL;
// Open the directory
DIR* dir = opendir(path);
if (dir == NULL) {
log_msg(LOG_ERROR, "Failed to open directory '%s' (%s).", path, strerror(errno));
free(path);
return NULL;
}
free(path);
// Count the number of entries
size_t count = 0;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL)
if (is_valid_entry_name(entry->d_name) && entry_exists(entry->d_name))
count++;
// Allocate the array of entries
char** entries = malloc((count + 1) * sizeof(char*));
if (entries == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the entries array.");
closedir(dir);
return NULL;
}
// Fill the array of entries
rewinddir(dir);
size_t index = 0;
while ((entry = readdir(dir)) != NULL) {
if (is_valid_entry_name(entry->d_name) && entry_exists(entry->d_name)) {
entries[index] = strdup(entry->d_name);
if (entries[index] == NULL) {
log_msg(LOG_ERROR, "Failed to allocate memory for the entry name.");
for (size_t i = 0; i < index; i++)
free(entries[i]);
free(entries);
closedir(dir);
return NULL;
}
index++;
}
}
// Terminate the array of entries
entries[count] = NULL;
// Close the directory
closedir(dir);
return entries;
}
uint64_t get_entry_last_access(const char* entry) {
char* disk = get_entry_disk_path(entry);
if (disk == NULL)
return 0;
// Get the last access time of the disk
struct stat statbuf;
int result = stat(disk, &statbuf);
// Free the disk path
free(disk);
if (result != 0)
return 0;
return statbuf.st_atime;
}
char* find_oldest_entry(void) {
char** entries = list_entries();
if (entries == NULL)
return NULL;
// Find the oldest entry
char* oldest_entry = NULL;
uint64_t oldest_time = UINT64_MAX;
for (size_t i = 0; entries[i] != NULL; i++) {
uint64_t time = get_entry_last_access(entries[i]);
if (time <= oldest_time) {
oldest_time = time;
oldest_entry = entries[i];
}
}
// Duplicate the oldest entry
if (oldest_entry != NULL)
oldest_entry = strdup(oldest_entry);
// Free the entries
for (size_t i = 0; entries[i] != NULL; i++)
free(entries[i]);
free(entries);
return oldest_entry;
}
bool create_entry(const char* entry) {
char* entry_path = get_entry_path(entry);
if (entry_path == NULL)
return false;
// Create the entry directory
bool result = create_directory(entry_path, 0700, 0, 0);
// Free the entry path
free(entry_path);
return result;
}
bool delete_entry(const char* entry) {
char* entry_path = get_entry_path(entry);
if (entry_path == NULL)
return false;
// Delete the entry directory
bool result = delete_directory(entry_path, 0);
// Free the entry path
free(entry_path);
return result;
}
uint64_t get_available_space(void) {
char* entries_path = get_entries_path();
if (entries_path == NULL)
return 0;
// Get the available space in the entries directory
struct statvfs statbuf;
int result = statvfs(entries_path, &statbuf);
// Free the entries path
free(entries_path);
if (result != 0)
return -1;
return statbuf.f_bsize * statbuf.f_bavail;
}
bool reserve_space(uint64_t space) {
// While the available space is not enough, delete the oldest entry
while (get_available_space() < space) {
char* oldest_entry = find_oldest_entry();
if (oldest_entry == NULL)
return false;
bool result = delete_entry(oldest_entry);
free(oldest_entry);
if (!result)
return false;
}
return true;
}

View File

@ -1,62 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#define DISK_PATH "disk"
/// @brief Returns the directory path where the entries are stored.
/// @return The directory path where the entries are stored. The caller is responsible for freeing the returned string. This function can return NULL.
char* get_entries_path(void);
/// @brief Checks if the specified entry name is valid.
/// @param name The entry name to check.
/// @return true if the entry name is valid, otherwise false.
bool is_valid_entry_name(const char* name);
/// @brief Returns the path to the specified entry.
/// @param entry The valid entry name.
/// @return The path to the specified entry. The caller is responsible for freeing the returned string. This function can return NULL.
char* get_entry_path(const char* entry);
/// @brief Checks if the specified entry exists.
/// @param entry The valid entry name.
/// @return true if the entry exists, otherwise false.
bool entry_exists(const char* entry);
/// @brief Returns the path to the disk of the specified entry.
/// @param entry The valid entry name.
/// @return The path to the disk of the specified entry. The caller is responsible for freeing the returned string. This function can return NULL.
char* get_entry_disk_path(const char* entry);
/// @brief Lists the entries.
/// @return The list of entries. The caller is responsible for freeing the returned array and strings. This function can return NULL.
char** list_entries(void);
/// @brief Returns the last access time of the specified entry.
/// @param entry The valid entry name, which must exist.
/// @return The last access time of the specified entry. If the entry does not exist, this function returns 0.
uint64_t get_entry_last_access(const char* entry);
/// @brief Finds the oldest entry (based on last access time).
/// @return The oldest entry. The caller is responsible for freeing the returned string. This function can return NULL.
char* find_oldest_entry(void);
/// @brief Creates a new entry.
/// @param entry The valid entry name.
/// @return true if the entry was created, otherwise false.
bool create_entry(const char* entry);
/// @brief Deletes the specified entry.
/// @param entry The valid entry name.
/// @return true if the entry was deleted, otherwise false.
bool delete_entry(const char* entry);
/// @brief Returns the available space in the entries directory.
/// @return The available space in the entries directory. If the entries directory does not exist, this function returns 0.
uint64_t get_available_space(void);
/// @brief Reserves the specified space in the entries directory, by deleting the oldest entries.
/// @param space The space to reserve, in bytes.
/// @return true if the space was fully reserved, otherwise false.
bool reserve_space(uint64_t space);

View File

@ -2,30 +2,16 @@
#include "utils.h"
#include "backing.h"
#include "entry.h"
#include "disk.h"
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
#include <libgen.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
char** backings = list_backings();
create_empty_disk("test.qcow2", 1024 * 1024 * 1024);
create_backed_disk("test2.qcow2", "test.qcow2");
rebase_disk("test2.qcow2", "test.qcow2");
if (backings == NULL) {
fprintf(stderr, "Failed to list the backing disks: %s\n", strerror(errno));
return EXIT_FAILURE;
}
for (size_t i = 0; backings[i] != NULL; i++)
printf("%s\n", backings[i]);
for (size_t i = 0; backings[i] != NULL; i++)
free(backings[i]);
free(backings);
return 0;
}

11
src/sandbox.h Executable file → Normal file
View File

@ -1,8 +1,3 @@
#pragma once
#define MASTER_DIRECTORY "/var/lib/sandbox"
#define BACKINGS_DIRECTORY "backings"
#define ENTRIES_DIRECTORY "entries"
int main(int argc, char* argv[]);
#pragma once
int main(int argc, char* argv[]);

730
src/utils.c Executable file → Normal file

File diff suppressed because it is too large Load Diff

80
src/utils.h Executable file → Normal file
View File

@ -1,58 +1,22 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#define MAX_RECURSION_LEVEL 256
/// @brief The log levels.
typedef enum {
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERROR,
} LogLevel;
/// @brief Formats a string using the specified format and arguments.
/// @param fmt The format string.
/// @param ... The arguments to use when formatting the string.
/// @return The formatted string. The caller is responsible for freeing the returned string. This function can return NULL.
char* format(const char* fmt, ...);
/// @brief Logs a formatted message to the console.
/// @param level The log level.
/// @param fmt The format string.
/// @param ... The arguments to use when formatting the string.
void log_msg(LogLevel level, const char* fmt, ...);
/// @brief Executes a command and returns the exit code.
/// @param stdout_buffer A pointer to a buffer that will receive the standard output of the command. The caller is responsible for freeing the buffer. This parameter can be NULL.
/// @param stderr_buffer A pointer to a buffer that will receive the standard error of the command. The caller is responsible for freeing the buffer. This parameter can be NULL.
/// @param file The path to the command to execute.
/// @param ... The arguments to pass to the command, followed by a NULL pointer. No need to include the command name in the arguments.
/// @return The exit code of the command.
int execute(char** stdout_buffer, char** stderr_buffer, const char* file, ...);
/// @brief Creates the specified directory.
/// @param directory The directory to create.
/// @param mode The mode to use when creating the directory.
/// @param uid The user ID to use when creating the directory.
/// @param gid The group ID to use when creating the directory.
/// @return true if the directory was created, otherwise false.
bool create_directory(const char* directory, mode_t mode, uid_t uid, gid_t gid);
/// @brief Deletes the specified directory and all its contents.
/// @param directory The directory to delete.
/// @param level The current level of recursion. This parameter is used internally and should be set to 0.
/// @return true if the directory was deleted, otherwise false.
bool delete_directory(const char* directory, int level);
/// @brief Copies the specified file to the specified destination.
/// @param source The source file to copy.
/// @param destination The destination file.
/// @param mode The mode to use when creating the destination file.
/// @param uid The user ID to use when creating the destination file.
/// @param gid The group ID to use when creating the destination file.
/// @return true if the file was copied, otherwise false.
bool copy_file(const char* source, const char* destination, mode_t mode, uid_t uid, gid_t gid);
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
typedef enum {
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERROR,
} LogLevel;
char* format(const char* fmt, ...);
void logmsg(LogLevel level, const char* fmt, ...);
int exec(char** outb, char** errb, const char* file, ...);
char* read_file(int fd);
bool copy_file(const char* src, const char* dst, mode_t mode);