Discarded some code that does not work well with the needs

This commit is contained in:
Alexei KADIR 2024-02-16 02:22:24 +01:00
parent 96b522d521
commit 9cd9ca03af
9 changed files with 50 additions and 279 deletions

View File

@ -1,163 +0,0 @@
#include "backing.h"
#include "utils.h"
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
bool is_valid_backing_name(const char* name) {
if (name == NULL)
return false;
size_t len = strlen(name);
if (len == 0)
return false;
for (size_t i = 0; i < len; i++)
if (name[i] == '/')
return false;
if (name[0] < '0' || name[0] > '9')
return false;
return true;
}
char* get_backing_path(const char* name) {
if (name == NULL)
return NULL;
return format("%s/%s", "/var/lib/sandbox/backings", name); // TODO: Use a setting for the backings directory
}
bool backing_exists(const char* name) {
if (name == NULL)
return false;
char* path = get_backing_path(name);
if (path == NULL)
return false;
// Check if the backing exists
struct stat st;
if (stat(path, &st) != 0) {
free(path);
return false;
}
// Free the path, as it is no longer needed
free(path);
if (!S_ISREG(st.st_mode))
return false;
return true;
}
char** list_backings(void) {
// Open the backings directory
DIR* dir = opendir("/var/lib/sandbox/backings"); // TODO: Use a setting for the backings directory
if (dir == NULL)
return NULL;
// Count the number of backings
size_t count = 0;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (is_valid_backing_name(entry->d_name))
count++;
}
// Allocate the array
char** backings = calloc(count + 1, sizeof(char*));
if (backings == NULL) {
closedir(dir);
return NULL;
}
// Reset the directory stream
rewinddir(dir);
// Fill the array
size_t i = 0;
while ((entry = readdir(dir)) != NULL) {
if (is_valid_backing_name(entry->d_name)) {
backings[i] = strdup(entry->d_name);
if (backings[i] == NULL) {
closedir(dir);
for (size_t j = 0; j < i; j++)
free(backings[j]);
free(backings);
return NULL;
}
i++;
}
}
// Close the directory
closedir(dir);
// Terminate the array
backings[count] = NULL;
return backings;
}
time_t get_backing_ctime(const char* name) {
// As the name starts with the ctime, we can just convert the first part of the name to a time_t
size_t len = strlen(name);
size_t ctime_len = 0;
for (size_t i = 0; i < len; i++) {
if (name[i] < '0' || name[i] > '9')
break;
ctime_len++;
}
char* ctime_str = strndup(name, ctime_len);
if (ctime_str == NULL)
return 0;
time_t ctime = (time_t)strtoull(ctime_str, NULL, 10);
free(ctime_str);
return ctime;
}
char* get_latest_backing(void) {
// Get the backings
char** backings = list_backings();
if (backings == NULL)
return NULL;
// Find the latest backing
time_t latest_ctime = 0;
char* latest_backing = NULL;
for (size_t i = 0; backings[i] != NULL; i++) {
time_t ctime = get_backing_ctime(backings[i]);
if (ctime >= latest_ctime) {
latest_ctime = ctime;
latest_backing = backings[i];
}
}
// Duplicate the latest backing
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;
}

View File

@ -1,41 +1,6 @@
#pragma once #pragma once
#include "sandbox.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <time.h>
/// @brief Returns whether the given name is a valid backing name. Backing names are valid if they start with a number (used as the ctime). #define BACKING_POOL "/var/lib/sandbox/backings"
/// @param name The name to check.
/// @return Whether the name is valid or not.
bool is_valid_backing_name(const char* name);
/// @brief Returns the backing path for the given name.
/// @param name The name of the backing. Note: This function does not check if the name is valid.
/// @return The backing path.
char* get_backing_path(const char* name);
/// @brief Returns the backing path for the given name.
/// @param name The name of the backing. Note: This function does not check if the name is valid.
/// @return The backing path.
bool backing_exists(const char* name);
/// @brief Returns an array of all the backing names, terminated by a NULL pointer.
/// @return An array of all the backing names. Note: The array must be freed by the caller, including every string in the array.
char** list_backings(void);
/// @brief Returns the ctime of the backing with the given name.
/// @param name The name of the backing.
/// @return The ctime of the backing.
time_t get_backing_ctime(const char* name);
/// @brief Returns the latest backing's name.
/// @return The latest backing's name.
char* get_latest_backing(void);
/// @brief Creates a new backing with the given name from the given source disk.
/// @param name The name of the new backing.
/// @param source_disk The source disk to create the backing from. Note: If the source disk has a backing file, it must be a existing sandbox backing disk.
/// @return Whether the backing was created successfully or not.
bool create_backing(const char* name, const char* source_disk);

View File

@ -16,26 +16,26 @@ bool create_empty_disk(const char* path, uint64_t size) {
// Convert the size to a string // Convert the size to a string
char* size_str = format("%lu", size); char* size_str = format("%lu", size);
if (size_str == NULL) { if (size_str == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the size string."); log_message(LOG_ERROR, "Failed to allocate memory for the size string.");
return false; return false;
} }
// Create the disk // Create the disk
int ret = exec(NULL, &errb, "qemu-img", "create", "-f", "qcow2", path, size_str, NULL); int ret = execute(NULL, &errb, "qemu-img", "create", "-f", "qcow2", path, size_str, NULL);
free(size_str); free(size_str);
// Check for errors // Check for errors
if (ret != 0) { if (ret != 0) {
if (errb == NULL) if (errb == NULL)
logmsg(LOG_ERROR, "Failed to create disk %s.", path); log_message(LOG_ERROR, "Failed to create disk %s.", path);
else { else {
size_t length = strlen(errb); size_t length = strlen(errb);
for (size_t i = 0; i < length; i++) for (size_t i = 0; i < length; i++)
if (errb[i] == '\n') if (errb[i] == '\n')
errb[i] = ' '; errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to create disk %s (%s).", path, errb); log_message(LOG_ERROR, "Failed to create disk %s (%s).", path, errb);
} }
return false; return false;
@ -48,19 +48,19 @@ bool create_backed_disk(const char* path, const char* backing_disk) {
char* errb = NULL; char* errb = NULL;
// Create the disk // Create the disk
int ret = exec(NULL, &errb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_disk, path, NULL); int ret = execute(NULL, &errb, "qemu-img", "create", "-f", "qcow2", "-F", "qcow2", "-b", backing_disk, path, NULL);
// Check for errors // Check for errors
if (ret != 0) { if (ret != 0) {
if (errb == NULL) if (errb == NULL)
logmsg(LOG_ERROR, "Failed to create disk %s.", path); log_message(LOG_ERROR, "Failed to create disk %s.", path);
else { else {
size_t length = strlen(errb); size_t length = strlen(errb);
for (size_t i = 0; i < length; i++) for (size_t i = 0; i < length; i++)
if (errb[i] == '\n') if (errb[i] == '\n')
errb[i] = ' '; errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to create disk %s (%s).", path, errb); log_message(LOG_ERROR, "Failed to create disk %s (%s).", path, errb);
} }
return false; return false;
@ -72,7 +72,7 @@ bool create_backed_disk(const char* path, const char* backing_disk) {
bool trim_disk(const char* path) { bool trim_disk(const char* path) {
char* tmp_path = format("%s.tmp", path); char* tmp_path = format("%s.tmp", path);
if (tmp_path == NULL) { if (tmp_path == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the temporary disk path."); log_message(LOG_ERROR, "Failed to allocate memory for the temporary disk path.");
return false; return false;
} }
@ -90,18 +90,18 @@ bool trim_disk(const char* path) {
if (info.backing_file != NULL) { if (info.backing_file != NULL) {
char* backing_str = format("backing_file=%s", info.backing_file); char* backing_str = format("backing_file=%s", info.backing_file);
if (backing_str == NULL) { if (backing_str == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the backing file string."); log_message(LOG_ERROR, "Failed to allocate memory for the backing file string.");
free_disk_info(&info); free_disk_info(&info);
free(tmp_path); free(tmp_path);
return false; return false;
} }
ret = exec(NULL, &errb, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_str, path, tmp_path, NULL); ret = execute(NULL, &errb, "qemu-img", "convert", "-f", "qcow2", "-F", "qcow2", "-O", "qcow2", "-o", backing_str, path, tmp_path, NULL);
free(backing_str); free(backing_str);
} else } else
ret = exec(NULL, &errb, "qemu-img", "convert", "-f", "qcow2", "-O", "qcow2", path, tmp_path, NULL); ret = execute(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 the disk info as we don't need it anymore
free_disk_info(&info); free_disk_info(&info);
@ -109,14 +109,14 @@ bool trim_disk(const char* path) {
// Check for errors // Check for errors
if (ret != 0) { if (ret != 0) {
if (errb == NULL) if (errb == NULL)
logmsg(LOG_ERROR, "Failed to trim disk %s.", path); log_message(LOG_ERROR, "Failed to trim disk %s.", path);
else { else {
size_t length = strlen(errb); size_t length = strlen(errb);
for (size_t i = 0; i < length; i++) for (size_t i = 0; i < length; i++)
if (errb[i] == '\n') if (errb[i] == '\n')
errb[i] = ' '; errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to trim disk %s (%s).", path, errb); log_message(LOG_ERROR, "Failed to trim disk %s (%s).", path, errb);
} }
unlink(tmp_path); unlink(tmp_path);
@ -127,7 +127,7 @@ bool trim_disk(const char* path) {
// Replace the original disk with the trimmed disk // Replace the original disk with the trimmed disk
if (rename(tmp_path, path) != 0) { if (rename(tmp_path, path) != 0) {
logmsg(LOG_ERROR, "Failed to replace disk %s with the trimmed disk (%s).", path, strerror(errno)); log_message(LOG_ERROR, "Failed to replace disk %s with the trimmed disk (%s).", path, strerror(errno));
unlink(tmp_path); unlink(tmp_path);
@ -143,19 +143,19 @@ bool rebase_disk(const char* path, const char* backing_disk) {
char* errb = NULL; char* errb = NULL;
// Rebase the disk // Rebase the disk
int ret = exec(NULL, &errb, "qemu-img", "rebase", "-u", "-F", "qcow2", "-b", backing_disk, path, NULL); int ret = execute(NULL, &errb, "qemu-img", "rebase", "-u", "-F", "qcow2", "-b", backing_disk, path, NULL);
// Check for errors // Check for errors
if (ret != 0) { if (ret != 0) {
if (errb == NULL) if (errb == NULL)
logmsg(LOG_ERROR, "Failed to rebase disk %s.", path); log_message(LOG_ERROR, "Failed to rebase disk %s.", path);
else { else {
size_t length = strlen(errb); size_t length = strlen(errb);
for (size_t i = 0; i < length; i++) for (size_t i = 0; i < length; i++)
if (errb[i] == '\n') if (errb[i] == '\n')
errb[i] = ' '; errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to rebase disk %s (%s).", path, errb); log_message(LOG_ERROR, "Failed to rebase disk %s (%s).", path, errb);
} }
return false; return false;
@ -168,18 +168,18 @@ bool get_disk_info(const char* path, DiskInfo* info) {
char* outb = NULL; char* outb = NULL;
char* errb = NULL; char* errb = NULL;
int ret = exec(&outb, &errb, "qemu-img", "info", "--output=json", path, NULL); int ret = execute(&outb, &errb, "qemu-img", "info", "--output=json", path, NULL);
if (ret != 0) { if (ret != 0) {
if (errb == NULL) if (errb == NULL)
logmsg(LOG_ERROR, "Failed to get information about disk %s.", path); log_message(LOG_ERROR, "Failed to get information about disk %s.", path);
else { else {
size_t length = strlen(errb); size_t length = strlen(errb);
for (size_t i = 0; i < length; i++) for (size_t i = 0; i < length; i++)
if (errb[i] == '\n') if (errb[i] == '\n')
errb[i] = ' '; errb[i] = ' ';
logmsg(LOG_ERROR, "Failed to get information about disk %s (%s).", path, errb); log_message(LOG_ERROR, "Failed to get information about disk %s (%s).", path, errb);
} }
free(outb); free(outb);
@ -195,29 +195,32 @@ bool get_disk_info(const char* path, DiskInfo* info) {
free(outb); free(outb);
if (root == NULL) { if (root == NULL) {
logmsg(LOG_ERROR, "Failed to parse the JSON output from qemu-img."); log_message(LOG_ERROR, "Failed to parse the JSON output from qemu-img.");
return false; return false;
} }
// Get the virtual size
json_object* virtual_size = json_object_object_get(root, "virtual-size"); json_object* virtual_size = json_object_object_get(root, "virtual-size");
if (virtual_size == NULL) if (virtual_size == NULL)
info->virtual_size = 0; info->virtual_size = 0;
else else
info->virtual_size = json_object_get_int64(virtual_size); info->virtual_size = json_object_get_int64(virtual_size);
// Get the actual size
json_object* actual_size = json_object_object_get(root, "actual-size"); json_object* actual_size = json_object_object_get(root, "actual-size");
if (actual_size == NULL) if (actual_size == NULL)
info->actual_size = 0; info->actual_size = 0;
else else
info->actual_size = json_object_get_int64(actual_size); info->actual_size = json_object_get_int64(actual_size);
// Get the backing file
json_object* backing_file = json_object_object_get(root, "backing-filename"); json_object* backing_file = json_object_object_get(root, "backing-filename");
if (backing_file == NULL) if (backing_file == NULL)
info->backing_file = NULL; info->backing_file = NULL;
else { else {
info->backing_file = strdup(json_object_get_string(backing_file)); info->backing_file = strdup(json_object_get_string(backing_file));
if (info->backing_file == NULL) { if (info->backing_file == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the backing file path."); log_message(LOG_ERROR, "Failed to allocate memory for the backing file path.");
json_object_put(root); json_object_put(root);
return false; return false;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
typedef struct DiskInfo { typedef struct DiskInfo {
uint64_t virtual_size; uint64_t virtual_size;

View File

@ -2,26 +2,5 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <time.h>
bool is_valid_entry_name(const char* name); #define ENTRY_POOL "/var/lib/sandbox/entries"
char* get_entry_path(const char* name);
bool entry_exists(const char* name);
char* get_entry_disk_path(const char* name);
char** list_entries(void);
time_t get_entry_mtime(const char* name);
char* find_oldest_entry(void);
bool create_entry(const char* name);
bool delete_entry(const char* name);
uint64_t get_available_size(void);
void reserve_space(uint64_t size);

View File

@ -2,26 +2,10 @@
#include "utils.h" #include "utils.h"
#include "backing.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
char** backings = list_backings();
if (backings == NULL) {
logmsg(LOG_ERROR, "Failed to list backings.");
return 1;
}
for (size_t i = 0; backings[i] != NULL; i++) {
printf("%s\n", backings[i]);
free(backings[i]);
}
free(backings);
return 0; return 0;
} }

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
int main(int argc, char* argv[]); int main(int argc, char* argv[]);

View File

@ -8,8 +8,11 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/stat.h>
#include <ftw.h>
char* format(const char* fmt, ...) { char* format(const char* fmt, ...) {
va_list args; va_list args;
@ -26,7 +29,7 @@ char* format(const char* fmt, ...) {
// Allocate a buffer for the formatted string // Allocate a buffer for the formatted string
char* buffer = calloc(length, sizeof(char)); char* buffer = calloc(length, sizeof(char));
if (buffer == NULL) { if (buffer == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the formatted string."); log_message(LOG_ERROR, "Failed to allocate memory for the formatted string.");
return NULL; return NULL;
} }
@ -40,7 +43,7 @@ char* format(const char* fmt, ...) {
return buffer; return buffer;
} }
void logmsg(LogLevel level, const char* fmt, ...) { void log_message(LogLevel level, const char* fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@ -90,7 +93,7 @@ void logmsg(LogLevel level, const char* fmt, ...) {
va_end(args); va_end(args);
} }
int exec(char** outb, char** errb, const char* file, ...) { int execute(char** outb, char** errb, const char* file, ...) {
// Count the number of arguments // Count the number of arguments
int argc = 1; // The first argument is the file name int argc = 1; // The first argument is the file name
@ -105,7 +108,7 @@ int exec(char** outb, char** errb, const char* file, ...) {
// Allocate an array for the arguments // Allocate an array for the arguments
char** argv = calloc(argc + 1, sizeof(char*)); char** argv = calloc(argc + 1, sizeof(char*));
if (argv == NULL) { if (argv == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the arguments array."); log_message(LOG_ERROR, "Failed to allocate memory for the arguments array.");
return -1; return -1;
} }
@ -114,7 +117,7 @@ int exec(char** outb, char** errb, const char* file, ...) {
argv[0] = strdup(file); argv[0] = strdup(file);
if (argv[0] == NULL) { if (argv[0] == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the file name."); log_message(LOG_ERROR, "Failed to allocate memory for the file name.");
free(argv); free(argv);
va_end(args); va_end(args);
@ -125,7 +128,7 @@ int exec(char** outb, char** errb, const char* file, ...) {
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
argv[i] = strdup(va_arg(args, const char*)); argv[i] = strdup(va_arg(args, const char*));
if (argv[i] == NULL) { if (argv[i] == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the argument %d.", i); log_message(LOG_ERROR, "Failed to allocate memory for the argument %d.", i);
for (int j = 0; j < i; j++) for (int j = 0; j < i; j++)
free(argv[j]); free(argv[j]);
@ -144,7 +147,7 @@ int exec(char** outb, char** errb, const char* file, ...) {
// Create a pipe for the standard output // Create a pipe for the standard output
int outp[2]; int outp[2];
if (pipe(outp) != 0) { if (pipe(outp) != 0) {
logmsg(LOG_ERROR, "Failed to create a pipe for the standard output."); log_message(LOG_ERROR, "Failed to create a pipe for the standard output.");
for (int i = 0; i < argc; i++) for (int i = 0; i < argc; i++)
free(argv[i]); free(argv[i]);
@ -156,7 +159,7 @@ int exec(char** outb, char** errb, const char* file, ...) {
// Create a pipe for the standard error // Create a pipe for the standard error
int errp[2]; int errp[2];
if (pipe(errp) != 0) { if (pipe(errp) != 0) {
logmsg(LOG_ERROR, "Failed to create a pipe for the standard error."); log_message(LOG_ERROR, "Failed to create a pipe for the standard error.");
close(outp[0]); close(outp[0]);
close(outp[1]); close(outp[1]);
@ -172,7 +175,7 @@ int exec(char** outb, char** errb, const char* file, ...) {
pid_t pid = fork(); pid_t pid = fork();
if (pid == -1) { if (pid == -1) {
logmsg(LOG_ERROR, "Failed to fork the process (%s).", strerror(errno)); log_message(LOG_ERROR, "Failed to fork the process (%s).", strerror(errno));
close(outp[0]); close(outp[0]);
close(outp[1]); close(outp[1]);
@ -244,7 +247,7 @@ char* read_file(int fd) {
while ((n = read(fd, buffer, sizeof(buffer))) > 0) { while ((n = read(fd, buffer, sizeof(buffer))) > 0) {
char* new_data = realloc(data, length + n + 1); char* new_data = realloc(data, length + n + 1);
if (new_data == NULL) { if (new_data == NULL) {
logmsg(LOG_ERROR, "Failed to allocate memory for the file data."); log_message(LOG_ERROR, "Failed to allocate memory for the file data.");
free(data); free(data);
@ -262,7 +265,7 @@ char* read_file(int fd) {
// Check for read errors // Check for read errors
if (n < 0) { if (n < 0) {
logmsg(LOG_ERROR, "Failed to read the file (%s).", strerror(errno)); log_message(LOG_ERROR, "Failed to read the file (%s).", strerror(errno));
free(data); free(data);
@ -276,14 +279,14 @@ bool copy_file(const char* src, const char* dst, mode_t mode) {
// Open the source file // Open the source file
int src_fd = open(src, O_RDONLY); int src_fd = open(src, O_RDONLY);
if (src_fd == -1) { if (src_fd == -1) {
logmsg(LOG_ERROR, "Failed to open the source file %s (%s).", src, strerror(errno)); log_message(LOG_ERROR, "Failed to open the source file %s (%s).", src, strerror(errno));
return false; return false;
} }
// Open the destination file // Open the destination file
int dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, mode); int dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, mode);
if (dst_fd == -1) { if (dst_fd == -1) {
logmsg(LOG_ERROR, "Failed to open the destination file %s (%s).", dst, strerror(errno)); log_message(LOG_ERROR, "Failed to open the destination file %s (%s).", dst, strerror(errno));
close(src_fd); close(src_fd);
@ -296,7 +299,7 @@ bool copy_file(const char* src, const char* dst, mode_t mode) {
while ((n = read(src_fd, buffer, sizeof(buffer))) > 0) { while ((n = read(src_fd, buffer, sizeof(buffer))) > 0) {
if (write(dst_fd, buffer, n) != n) { if (write(dst_fd, buffer, n) != n) {
logmsg(LOG_ERROR, "Failed to write to the destination file %s (%s).", dst, strerror(errno)); log_message(LOG_ERROR, "Failed to write to the destination file %s (%s).", dst, strerror(errno));
close(src_fd); close(src_fd);
close(dst_fd); close(dst_fd);
@ -307,7 +310,7 @@ bool copy_file(const char* src, const char* dst, mode_t mode) {
// Check for read errors // Check for read errors
if (n < 0) { if (n < 0) {
logmsg(LOG_ERROR, "Failed to read the source file %s (%s).", src, strerror(errno)); log_message(LOG_ERROR, "Failed to read the source file %s (%s).", src, strerror(errno));
close(src_fd); close(src_fd);
close(dst_fd); close(dst_fd);

View File

@ -13,10 +13,10 @@ typedef enum {
char* format(const char* fmt, ...); char* format(const char* fmt, ...);
void logmsg(LogLevel level, const char* fmt, ...); void log_message(LogLevel level, const char* fmt, ...);
int exec(char** outb, char** errb, const char* file, ...); int execute(char** outb, char** errb, const char* file, ...);
char* read_file(int fd); char* read_file(int fd);
bool copy_file(const char* src, const char* dst, mode_t mode); bool copy_file(const char* src, const char* dst, mode_t mode);