diff --git a/src/backing.c b/src/backing.c index 91e884f..773d256 100644 --- a/src/backing.c +++ b/src/backing.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include char* get_backings_path(void) { @@ -22,10 +24,16 @@ bool is_valid_backing_name(const char* name) { if (length == 0) return false; - // Check that the name is a number (the number is the timestamp of the backing disk creation) + // 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') - return false; + if (name[i] >= '0' && name[i] <= '9') + timestamp_length++; + else + break; + + if (timestamp_length == 0) + return false; return true; } @@ -65,7 +73,112 @@ bool backing_exists(const char* backing) { return true; } -uint64_t get_backing_creation_time(const char* backing) { - // A valid backing name is a number, corresponding to the timestamp of the backing disk creation - return strtoull(backing, NULL, 10); +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; } diff --git a/src/backing.h b/src/backing.h index e698774..962e5f0 100644 --- a/src/backing.h +++ b/src/backing.h @@ -37,5 +37,6 @@ char* find_latest_backing(void); /// @brief Creates a new backing disk. /// @param backing_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* backing_disk); \ No newline at end of file +bool create_backing(const char* backing_disk, const char* description); \ No newline at end of file diff --git a/src/disk.c b/src/disk.c index a855a92..d121134 100644 --- a/src/disk.c +++ b/src/disk.c @@ -12,7 +12,7 @@ bool create_empty_disk(const char* disk, uint64_t size) { char* stderrb = NULL; // Create an empty disk - int result = execute(&stdoutb, &stderrb, "qemu-img", "create", "-c", "-f", "qcow2", disk, format("%" PRIu64, size), NULL); + int result = execute(&stdoutb, &stderrb, "qemu-img", "create", "-f", "qcow2", disk, format("%" PRIu64, size), NULL); // Check if the command was successful if (result != 0) { @@ -40,7 +40,7 @@ bool create_backed_disk(const char* disk, const char* backing) { char* stderrb = NULL; // Create a backed disk - int result = execute(&stdoutb, &stderrb, "qemu-img", "create", "-c", "-f", "qcow2", "-F", "qcow2", "-b", backing, disk, NULL); + int result = execute(&stdoutb, &stderrb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing, disk, NULL); // Check if the command was successful if (result != 0) { diff --git a/src/entry.c b/src/entry.c index 474d2bf..62ede8e 100644 --- a/src/entry.c +++ b/src/entry.c @@ -5,10 +5,15 @@ #include #include +#include #include #include +#include +#include +#include #include #include +#include char* get_entries_path(void) { return format("%s/%s", MASTER_DIRECTORY, ENTRIES_DIRECTORY); @@ -70,21 +75,135 @@ bool entry_exists(const char* entry) { 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 - int result = mkdir(entry_path, 0755); + bool result = create_directory(entry_path, 0700, 0, 0); // Free the entry path free(entry_path); - if (result != 0) - return false; - - return true; + return result; } bool delete_entry(const char* entry) { @@ -93,7 +212,7 @@ bool delete_entry(const char* entry) { return false; // Delete the entry directory - bool result = delete_directory(entry_path); + bool result = delete_directory(entry_path, 0); // Free the entry path free(entry_path); diff --git a/src/entry.h b/src/entry.h index a0f4f48..95024cc 100644 --- a/src/entry.h +++ b/src/entry.h @@ -3,6 +3,8 @@ #include #include +#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); @@ -22,11 +24,16 @@ char* get_entry_path(const char* entry); /// @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 Get the last access time of the specified entry. +/// @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); @@ -45,11 +52,6 @@ bool create_entry(const char* entry); /// @return true if the entry was deleted, otherwise false. bool delete_entry(const char* entry); -/// @brief Updates the last access time of the specified entry. -/// @param entry The valid entry name, which must exist. -/// @return true if the last access time was updated, otherwise false. -bool update_entry_last_access(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); diff --git a/src/sandbox.c b/src/sandbox.c index 027de13..fbfb88e 100644 --- a/src/sandbox.c +++ b/src/sandbox.c @@ -10,7 +10,12 @@ #include #include #include +#include +#include int main(int argc, char* argv[]) { - log_msg(LOG_INFO, "Available space : %lld", get_available_space()); + while (1) { + char* oldest_entry = find_oldest_entry(); + free(oldest_entry); + } } \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 2e13013..c8a8d0e 100755 --- a/src/utils.c +++ b/src/utils.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -270,11 +271,32 @@ int execute(char** stdout_buffer, char** stderr_buffer, const char* file, ...) { return WIFEXITED(status) ? WEXITSTATUS(status) : -1; } -bool delete_directory(const char* directory) { +bool create_directory(const char* directory, mode_t mode, uid_t uid, gid_t gid) { + // Create the directory + if (mkdir(directory, mode) != 0) { + log_msg(LOG_ERROR, "Failed to create directory '%s' (%s).", directory, strerror(errno)); + return false; + } + + // Change the owner of the directory + if (chown(directory, uid, gid) != 0) { + log_msg(LOG_ERROR, "Failed to change the owner of directory '%s' (%s).", directory, strerror(errno)); + return false; + } + + return true; +} + +bool delete_directory(const char* directory, int level) { + if (level > MAX_RECURSION_LEVEL) { + log_msg(LOG_ERROR, "Too many levels of recursion when deleting directory '%s'.", directory); + return false; + } + // Open the directory DIR* dir = opendir(directory); if (dir == NULL) { - log_msg(LOG_ERROR, "Failed to open directory '%s'.", directory); + log_msg(LOG_ERROR, "Failed to open directory '%s' (%s).", directory, strerror(errno)); return false; } @@ -297,7 +319,7 @@ bool delete_directory(const char* directory) { struct stat statbuf; int result = stat(entry_path, &statbuf); if (result != 0) { - log_msg(LOG_ERROR, "Failed to get information about entry '%s'.", entry_path); + log_msg(LOG_ERROR, "Failed to get information about file '%s' (%s).", entry_path, strerror(errno)); free(entry_path); closedir(dir); return false; @@ -305,7 +327,9 @@ bool delete_directory(const char* directory) { if (S_ISDIR(statbuf.st_mode)) { // Delete the directory - if (!delete_directory(entry_path)) { + if (!delete_directory(entry_path, level + 1)) { + log_msg(LOG_ERROR, "Failed to delete directory '%s'.", entry_path); + free(entry_path); closedir(dir); return false; @@ -313,7 +337,8 @@ bool delete_directory(const char* directory) { } else { // Delete the file if (unlink(entry_path) != 0) { - log_msg(LOG_ERROR, "Failed to delete entry '%s'.", entry_path); + log_msg(LOG_ERROR, "Failed to delete file '%s' (%s).", entry_path, strerror(errno)); + free(entry_path); closedir(dir); return false; @@ -328,7 +353,7 @@ bool delete_directory(const char* directory) { // Delete the directory if (rmdir(directory) != 0) { - log_msg(LOG_ERROR, "Failed to delete directory '%s'.", directory); + log_msg(LOG_ERROR, "Failed to delete directory '%s' (%s).", directory, strerror(errno)); return false; } diff --git a/src/utils.h b/src/utils.h index c8df5d4..c31ff7a 100755 --- a/src/utils.h +++ b/src/utils.h @@ -2,6 +2,9 @@ #include #include +#include + +#define MAX_RECURSION_LEVEL 256 /// @brief The log levels. typedef enum { @@ -31,7 +34,16 @@ void log_msg(LogLevel level, const char* fmt, ...); /// @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); +bool delete_directory(const char* directory, int level);