Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 261683aaff | |||
| 15a861747a | |||
| 5f1b27c8ff | |||
| 732cd67735 | |||
| 50fb7185a8 | |||
| 3062927e07 | |||
| 88746c3396 | |||
| 80768d64d4 |
@@ -374,7 +374,8 @@ static int scan_directory_multithreaded(void* pipeline_context) {
|
|||||||
context->config->exclude_patterns, context->config->exclude_count,
|
context->config->exclude_patterns, context->config->exclude_count,
|
||||||
context->config->include_patterns, context->config->include_count, context->config->max_size,
|
context->config->include_patterns, context->config->include_count, context->config->max_size,
|
||||||
context->config->min_size, context->config->max_depth, 4, context->config->follow_symlinks,
|
context->config->min_size, context->config->max_depth, 4, context->config->follow_symlinks,
|
||||||
context->config->copy_links, context->config->safe_links, context->config->copy_unsafe_links, context->config->checksum);
|
context->config->copy_links, context->config->safe_links, context->config->copy_unsafe_links,
|
||||||
|
context->config->checksum);
|
||||||
|
|
||||||
Chunk* current_chunk;
|
Chunk* current_chunk;
|
||||||
while ((current_chunk = parallel_scanner_next(scanner)) != NULL) {
|
while ((current_chunk = parallel_scanner_next(scanner)) != NULL) {
|
||||||
@@ -575,8 +576,8 @@ int send_files(Config* config) {
|
|||||||
}
|
}
|
||||||
if (config->stats) {
|
if (config->stats) {
|
||||||
double rate = elapsed_total > 0 ? total_bytes / (1048576.0 * elapsed_total) : 0;
|
double rate = elapsed_total > 0 ? total_bytes / (1048576.0 * elapsed_total) : 0;
|
||||||
fprintf(stderr, "Stats: %d files, %.1f MB, %.1f MB/s\n", total_files,
|
fprintf(stderr, "Stats: %d files, %.1f MB, %.1f MB/s\n", total_files, total_bytes / 1048576.0,
|
||||||
total_bytes / 1048576.0, rate);
|
rate);
|
||||||
}
|
}
|
||||||
directory_scanner_destroy(scanner);
|
directory_scanner_destroy(scanner);
|
||||||
client_disconnect(client);
|
client_disconnect(client);
|
||||||
|
|||||||
@@ -347,11 +347,62 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata
|
|||||||
char* cur_path = path_cat(root_directory, entry->d_name);
|
char* cur_path = path_cat(root_directory, entry->d_name);
|
||||||
if (!cur_path)
|
if (!cur_path)
|
||||||
continue;
|
continue;
|
||||||
|
struct stat lstats;
|
||||||
|
if (lstat(cur_path, &lstats) != 0) {
|
||||||
|
free(cur_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool is_symlink = S_ISLNK(lstats.st_mode);
|
||||||
|
|
||||||
|
// Skip symlinks unless the user explicitly enabled following/copying them.
|
||||||
|
if (is_symlink && !follow_symlinks && !copy_links && !safe_links && !copy_unsafe_links) {
|
||||||
|
free(cur_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --safe-links: reject symlinks pointing outside the source tree.
|
||||||
|
if (is_symlink && safe_links) {
|
||||||
|
char link_target[4096];
|
||||||
|
ssize_t len = readlink(cur_path, link_target, sizeof(link_target) - 1);
|
||||||
|
if (len < 0) {
|
||||||
|
free(cur_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
link_target[len] = 0;
|
||||||
|
if (link_target[0] == '/') {
|
||||||
|
free(cur_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --copy-unsafe-links (without --copy-links): only copy absolute symlinks.
|
||||||
|
if (is_symlink && copy_unsafe_links && !copy_links) {
|
||||||
|
char link_target[4096];
|
||||||
|
ssize_t len = readlink(cur_path, link_target, sizeof(link_target) - 1);
|
||||||
|
if (len < 0) {
|
||||||
|
free(cur_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
link_target[len] = 0;
|
||||||
|
bool unsafe = (link_target[0] == '/');
|
||||||
|
if (!unsafe) {
|
||||||
|
free(cur_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine whether to use lstat or stat results for the entry.
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
bool use_lstat_res = is_symlink && follow_symlinks && !copy_links;
|
||||||
|
if (use_lstat_res) {
|
||||||
|
st = lstats;
|
||||||
|
} else {
|
||||||
if (stat(cur_path, &st) != 0) {
|
if (stat(cur_path, &st) != 0) {
|
||||||
free(cur_path);
|
free(cur_path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
array_list_add(subdirs, cur_path);
|
array_list_add(subdirs, cur_path);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -12,6 +12,9 @@
|
|||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
|
/* Maximum individual file data size within a chunk (64 MB) */
|
||||||
|
#define MAX_FILE_DATA_SIZE (64ULL * 1024 * 1024)
|
||||||
|
|
||||||
Chunk* chunk_create(File** items, int element_count) {
|
Chunk* chunk_create(File** items, int element_count) {
|
||||||
Chunk* chunk = (Chunk*)malloc(sizeof(Chunk));
|
Chunk* chunk = (Chunk*)malloc(sizeof(Chunk));
|
||||||
if (chunk == NULL) {
|
if (chunk == NULL) {
|
||||||
@@ -157,6 +160,14 @@ Chunk* chunk_deserialize(Data* data, bool use_metadata) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject individual file data larger than the maximum allowed size.
|
||||||
|
if (file_data_size > MAX_FILE_DATA_SIZE) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "File data size %zu exceeds maximum %llu", file_data_size,
|
||||||
|
(unsigned long long)MAX_FILE_DATA_SIZE);
|
||||||
|
array_list_delete(files);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void* file_data = malloc(file_data_size);
|
void* file_data = malloc(file_data_size);
|
||||||
if (file_data == NULL) {
|
if (file_data == NULL) {
|
||||||
perror("Could not allocate memory for file data");
|
perror("Could not allocate memory for file data");
|
||||||
@@ -210,6 +221,15 @@ Chunk* receive_chunk_data(int fd, const Config* config) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject chunks larger than the maximum allowed size to prevent OOM.
|
||||||
|
if (data_to_process->size > MAX_CHUNK_SIZE) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Chunk size %zu exceeds maximum %llu", data_to_process->size,
|
||||||
|
(unsigned long long)MAX_CHUNK_SIZE);
|
||||||
|
data_destroy(data_to_process);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Chunk* chunk = chunk_deserialize(data_to_process, config->use_metadata);
|
Chunk* chunk = chunk_deserialize(data_to_process, config->use_metadata);
|
||||||
data_destroy(data_to_process);
|
data_destroy(data_to_process);
|
||||||
if (chunk == NULL)
|
if (chunk == NULL)
|
||||||
|
|||||||
+8
-7
@@ -35,7 +35,8 @@ File* file_create(const char* path) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(file->path, path);
|
memcpy(file->path, path, path_len);
|
||||||
|
file->path[path_len] = '\0';
|
||||||
file->data = data_create_reserve(0);
|
file->data = data_create_reserve(0);
|
||||||
if (file->data == NULL) {
|
if (file->data == NULL) {
|
||||||
free(file->path);
|
free(file->path);
|
||||||
@@ -142,7 +143,8 @@ bool file_save_to_disk(const char* root_directory, File* file, const Config* con
|
|||||||
}
|
}
|
||||||
|
|
||||||
char* resolved_root = NULL;
|
char* resolved_root = NULL;
|
||||||
const char* actual_root = (partial_dir && config && config->partial) ? partial_dir : root_directory;
|
const char* actual_root =
|
||||||
|
(partial_dir && config && config->partial) ? partial_dir : root_directory;
|
||||||
resolved_root = realpath(actual_root, NULL);
|
resolved_root = realpath(actual_root, NULL);
|
||||||
if (resolved_root == NULL) {
|
if (resolved_root == NULL) {
|
||||||
if (mkdir_r(actual_root)) {
|
if (mkdir_r(actual_root)) {
|
||||||
@@ -187,7 +189,7 @@ bool file_save_to_disk(const char* root_directory, File* file, const Config* con
|
|||||||
if (backup_path) {
|
if (backup_path) {
|
||||||
char* backup_dir_path = str_dup(backup_path);
|
char* backup_dir_path = str_dup(backup_path);
|
||||||
if (backup_dir_path) {
|
if (backup_dir_path) {
|
||||||
char* bdir = dirname(backup_dir_path);
|
const char* bdir = dirname(backup_dir_path);
|
||||||
mkdir_r(bdir);
|
mkdir_r(bdir);
|
||||||
free(backup_dir_path);
|
free(backup_dir_path);
|
||||||
}
|
}
|
||||||
@@ -222,8 +224,7 @@ bool file_save_to_disk(const char* root_directory, File* file, const Config* con
|
|||||||
size_t root_len = strlen(resolved_root);
|
size_t root_len = strlen(resolved_root);
|
||||||
if (strncmp(resolved_dir, resolved_root, root_len) != 0 ||
|
if (strncmp(resolved_dir, resolved_root, root_len) != 0 ||
|
||||||
(resolved_dir[root_len] != '\0' && resolved_dir[root_len] != '/')) {
|
(resolved_dir[root_len] != '\0' && resolved_dir[root_len] != '/')) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Path escape detected: %s is outside %s", disk_path,
|
log_message(LOG_LEVEL_ERROR, "Path escape detected: %s is outside %s", disk_path, actual_root);
|
||||||
actual_root);
|
|
||||||
free(resolved_dir);
|
free(resolved_dir);
|
||||||
free(resolved_root);
|
free(resolved_root);
|
||||||
free(disk_path);
|
free(disk_path);
|
||||||
@@ -517,8 +518,8 @@ File* receive_incremental_check(int fd, const Config* config, bool* skipped) {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool to_disk(const char* path, const void* data, unsigned long long data_size,
|
bool to_disk(const char* path, const void* data, unsigned long long data_size, bool inplace,
|
||||||
bool inplace, bool sparse) {
|
bool sparse) {
|
||||||
char* tmp_path = NULL;
|
char* tmp_path = NULL;
|
||||||
char* directory = NULL;
|
char* directory = NULL;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -34,8 +34,8 @@ bool file_send_sendfile(File* file, int file_descriptor, bool use_metadata, int
|
|||||||
size_t file_content_to_buffer(File* file);
|
size_t file_content_to_buffer(File* file);
|
||||||
FileMetadata* file_metadata_create(const struct stat* stats);
|
FileMetadata* file_metadata_create(const struct stat* stats);
|
||||||
void file_metadata_destroy(void* metadata);
|
void file_metadata_destroy(void* metadata);
|
||||||
bool to_disk(const char* path, const void* data, unsigned long long data_size,
|
bool to_disk(const char* path, const void* data, unsigned long long data_size, bool inplace,
|
||||||
bool inplace, bool sparse);
|
bool sparse);
|
||||||
bool file_save_to_disk(const char* root_directory, File* file, const Config* config);
|
bool file_save_to_disk(const char* root_directory, File* file, const Config* config);
|
||||||
File* receive_incremental_check(int fd, const Config* config, bool* skipped);
|
File* receive_incremental_check(int fd, const Config* config, bool* skipped);
|
||||||
int receive_manifest(int fd, const Config* config, int* next_status);
|
int receive_manifest(int fd, const Config* config, int* next_status);
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
/* Maximum allowed data payload size for receive_data (100 MB) */
|
/* Maximum allowed data payload size for receive_data (100 MB) */
|
||||||
#define MAX_DATA_PAYLOAD_SIZE (100ULL * 1024 * 1024)
|
#define MAX_DATA_PAYLOAD_SIZE (100ULL * 1024 * 1024)
|
||||||
|
|
||||||
|
/* Maximum chunk size (64 MB) — prevents unbounded allocation from the wire */
|
||||||
|
#define MAX_CHUNK_SIZE (64ULL * 1024 * 1024)
|
||||||
|
|
||||||
typedef struct ssl_st SSL;
|
typedef struct ssl_st SSL;
|
||||||
|
|
||||||
typedef int Status;
|
typedef int Status;
|
||||||
|
|||||||
+14
-8
@@ -13,7 +13,7 @@ bool mkdir_r(const char* path) {
|
|||||||
char* path_duplicate = malloc(strlen(path) + 1);
|
char* path_duplicate = malloc(strlen(path) + 1);
|
||||||
if (!path_duplicate)
|
if (!path_duplicate)
|
||||||
return false;
|
return false;
|
||||||
strcpy(path_duplicate, path);
|
memcpy(path_duplicate, path, strlen(path) + 1);
|
||||||
char* path_current = (char*)malloc((strlen(path) + 2) * sizeof(char));
|
char* path_current = (char*)malloc((strlen(path) + 2) * sizeof(char));
|
||||||
if (!path_current) {
|
if (!path_current) {
|
||||||
free(path_duplicate);
|
free(path_duplicate);
|
||||||
@@ -21,7 +21,8 @@ bool mkdir_r(const char* path) {
|
|||||||
}
|
}
|
||||||
char* path_current_position = path_current;
|
char* path_current_position = path_current;
|
||||||
if (path[0] == '/') {
|
if (path[0] == '/') {
|
||||||
strcpy(path_current, "/");
|
path_current[0] = '/';
|
||||||
|
path_current[1] = '\0';
|
||||||
path_current_position += 1;
|
path_current_position += 1;
|
||||||
} else {
|
} else {
|
||||||
path_current[0] = '\0';
|
path_current[0] = '\0';
|
||||||
@@ -31,10 +32,12 @@ bool mkdir_r(const char* path) {
|
|||||||
const char* part = strtok_r(path_duplicate, delimiter, &saveptr);
|
const char* part = strtok_r(path_duplicate, delimiter, &saveptr);
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
while (part != NULL) {
|
while (part != NULL) {
|
||||||
strcpy(path_current_position, part);
|
size_t part_len = strlen(part);
|
||||||
path_current_position += strlen(part) * sizeof(char);
|
memcpy(path_current_position, part, part_len);
|
||||||
strcpy(path_current_position, "/");
|
path_current_position += part_len;
|
||||||
path_current_position += sizeof(char);
|
path_current_position[0] = '/';
|
||||||
|
path_current_position[1] = '\0';
|
||||||
|
path_current_position++;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path_current, &st) != 0) {
|
if (stat(path_current, &st) != 0) {
|
||||||
if (mkdir(path_current, 0755) != 0) {
|
if (mkdir(path_current, 0755) != 0) {
|
||||||
@@ -53,8 +56,11 @@ bool mkdir_r(const char* path) {
|
|||||||
char* str_dup(const char* string) {
|
char* str_dup(const char* string) {
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
char* new_string = (char*)malloc(strlen(string) + 1);
|
size_t str_len = strlen(string);
|
||||||
strcpy(new_string, string);
|
char* new_string = (char*)malloc(str_len + 1);
|
||||||
|
if (new_string == NULL)
|
||||||
|
return NULL;
|
||||||
|
memcpy(new_string, string, str_len + 1);
|
||||||
return new_string;
|
return new_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user