#include "backing.h" #include "sandbox.h" #include "container.h" #include "disk.h" #include #include #include #include #include #include #include #include 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(); }