fix: features and enhancements — issues #70, #36, #34, #33, #32, #57, #40, #37
CI / lint (pull_request) Failing after 3s
CI / build-and-test (pull_request) Has been skipped
CI / sanitizers (address) (pull_request) Has been skipped
CI / sanitizers (undefined) (pull_request) Has been skipped
CI / fuzz-build (pull_request) Has been skipped
CI / coverage (pull_request) Has been skipped
CI / valgrind (pull_request) Has been skipped

This commit is contained in:
2026-07-20 19:27:13 +02:00
parent 12ca4b13c8
commit 2dd40d6a5b
13 changed files with 553 additions and 60 deletions
+3
View File
@@ -55,6 +55,7 @@ static void print_usage(void) {
printf(" --cert <path> TLS certificate file (PEM)\n");
printf(" --key <path> TLS private key file (PEM)\n");
printf(" --ca <path> TLS CA certificate file (PEM)\n");
printf(" --partial Keep partial files on interrupted transfer\n");
printf(" --help Show this help\n");
printf(" -V, --version Show version and exit\n");
}
@@ -217,6 +218,8 @@ int main(int argc, char* argv[]) {
} else if (strcmp(argv[i], "--ca") == 0 && i + 1 < argc) {
free(config->tls_ca);
config->tls_ca = str_dup(argv[++i]);
} else if (strcmp(argv[i], "--partial") == 0) {
config->partial = true;
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
set_log_level(LOG_LEVEL_DEBUG);
} else if (argv[i][0] == '-') {
+76 -1
View File
@@ -16,6 +16,17 @@ DirectoryScanner* directory_scanner_create(char* root_directory, bool use_metada
int exclude_count, char** include_patterns,
int include_count, unsigned long long max_size,
unsigned long long min_size) {
return directory_scanner_create_full(root_directory, use_metadata, chunk_size,
exclude_patterns, exclude_count,
include_patterns, include_count,
max_size, min_size, true);
}
DirectoryScanner* directory_scanner_create_full(char* root_directory, bool use_metadata,
unsigned long long chunk_size, char** exclude_patterns,
int exclude_count, char** include_patterns,
int include_count, unsigned long long max_size,
unsigned long long min_size, bool follow_symlinks) {
DirectoryScanner* scanner = malloc(sizeof(DirectoryScanner));
if (scanner == NULL)
return NULL;
@@ -79,6 +90,7 @@ DirectoryScanner* directory_scanner_create(char* root_directory, bool use_metada
scanner->include_count = include_count;
scanner->max_size = max_size;
scanner->min_size = min_size;
scanner->follow_symlinks = follow_symlinks;
queue_enqueue(scanner->directories, str_dup(root_directory));
return scanner;
}
@@ -159,13 +171,76 @@ Chunk* directory_scanner_next(DirectoryScanner* scanner) {
char* cur_path = path_cat(scanner->current_path, entry->d_name);
struct stat stats;
if (stat(cur_path, &stats) != 0) {
// Use lstat to detect symlinks
if (lstat(cur_path, &stats) != 0) {
free(cur_path);
continue;
}
// If follow_symlinks is enabled and this is a symlink, resolve it
if (scanner->follow_symlinks && S_ISLNK(stats.st_mode)) {
struct stat target_stats;
if (stat(cur_path, &target_stats) != 0) {
// Broken symlink, skip
free(cur_path);
continue;
}
stats = target_stats;
}
if (S_ISDIR(stats.st_mode)) {
queue_enqueue(scanner->directories, (void*)cur_path);
} else if (S_ISLNK(stats.st_mode)) {
// Handle symlink (not following)
bool excluded = false;
for (int i = 0; i < scanner->exclude_count; i++) {
if (glob_match(scanner->exclude_patterns[i], entry->d_name)) {
excluded = true;
break;
}
}
if (excluded) {
free(cur_path);
continue;
}
if (scanner->include_count > 0) {
bool included = false;
for (int i = 0; i < scanner->include_count; i++) {
if (glob_match(scanner->include_patterns[i], entry->d_name)) {
included = true;
break;
}
}
if (!included) {
free(cur_path);
continue;
}
}
File* file = file_create(cur_path);
if (file == NULL) {
free(cur_path);
continue;
}
file->type = FILE_TYPE_SYMLINK;
// Read link target
char link_buf[4096];
ssize_t link_len = readlink(cur_path, link_buf, sizeof(link_buf) - 1);
if (link_len >= 0) {
link_buf[link_len] = '\0';
file->link_target = str_dup(link_buf);
}
file->data->size = 0;
if (scanner->use_metadata)
file->metadata = file_metadata_create(&stats);
array_list_add(chunk_data, file);
chunk_data_size += 1; // small size for symlinks
if (chunk_data_size > scanner->chunk_size) {
free(cur_path);
return chunk_data_to_chunk(chunk_data);
}
free(cur_path);
} else {
bool excluded = false;
for (int i = 0; i < scanner->exclude_count; i++) {
+6
View File
@@ -18,6 +18,7 @@ typedef struct {
int include_count;
unsigned long long max_size;
unsigned long long min_size;
bool follow_symlinks;
} DirectoryScanner;
DirectoryScanner* directory_scanner_create(char* root_directory, bool use_metadata,
@@ -25,6 +26,11 @@ DirectoryScanner* directory_scanner_create(char* root_directory, bool use_metada
int exclude_count, char** include_patterns,
int include_count, unsigned long long max_size,
unsigned long long min_size);
DirectoryScanner* directory_scanner_create_full(char* root_directory, bool use_metadata,
unsigned long long chunk_size, char** exclude_patterns,
int exclude_count, char** include_patterns,
int include_count, unsigned long long max_size,
unsigned long long min_size, bool follow_symlinks);
Chunk* directory_scanner_next(DirectoryScanner* scanner);
void directory_scanner_destroy(DirectoryScanner* scanner);