Files
FastSync/src/client/scanner.c
T
TapTap 38be7c090a
CI / lint (push) Failing after 3s
CI / build-and-test (push) Has been skipped
CI / sanitizers (address) (push) Has been skipped
CI / clang-tidy (push) Has been skipped
CI / lint (pull_request) Failing after 2s
CI / build-and-test (pull_request) Has been skipped
CI / sanitizers (address) (pull_request) Has been skipped
CI / clang-tidy (pull_request) Has been skipped
Fix 11 Gitea issues (#29, #30, #31, #35, #38, #41, #42, #43, #44, #45, #46)
#29 - compression_level now used in data_compress()
#30 - chunk_size no longer truncated to 32-bit
#31 - chmod/chown failures now logged
#35 - strtok -> strtok_r for thread safety
#38 - open_next_directory returns -1 on opendir failure
#41 - file_send_sendfile uses compression_level param
#42 - dirname() uses copy to avoid modifying input
#43 - delete_extras_walk checks manifest before rmdir
#44 - server_host/port moved into Config struct
#45 - SSH parse_remote_dest uses dynamic allocation
#46 - send_chunk refactored, reduced nesting/duplication
2026-07-20 17:09:12 +02:00

170 lines
4.9 KiB
C

#include "scanner.h"
#include "array_list.h"
#include "chunk.h"
#include "file.h"
#include "queue.h"
#include "utils.h"
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
DirectoryScanner* directory_scanner_create(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) {
DirectoryScanner* scanner = malloc(sizeof(DirectoryScanner));
if (scanner == NULL)
return NULL;
scanner->directories = queue_create(100, free);
scanner->current_dir = NULL;
scanner->current_path = NULL;
scanner->use_metadata = use_metadata;
scanner->chunk_size = chunk_size > 0 ? chunk_size : DESIRED_CHUNK_SIZE;
scanner->exclude_patterns = exclude_patterns;
scanner->exclude_count = exclude_count;
scanner->include_patterns = include_patterns;
scanner->include_count = include_count;
scanner->max_size = max_size;
scanner->min_size = min_size;
queue_enqueue(scanner->directories, str_dup(root_directory));
return scanner;
}
void directory_scanner_destroy(DirectoryScanner* scanner) {
if (scanner == NULL)
return;
if (scanner->current_dir) {
closedir(scanner->current_dir);
scanner->current_dir = NULL;
}
free(scanner->current_path);
queue_destroy(scanner->directories);
free(scanner);
}
static Chunk* chunk_data_to_chunk(ArrayList* chunk_data) {
void** chunk_items = array_list_to_array(chunk_data);
Chunk* chunk = chunk_create((File**)chunk_items, chunk_data->size);
free(chunk_items);
chunk_data->item_destroyer = NULL;
array_list_delete(chunk_data);
return chunk;
}
// Returns: 1 on success, 0 if no more directories in queue, -1 on opendir failure
static int open_next_directory(DirectoryScanner* scanner) {
if (scanner->current_dir) {
closedir(scanner->current_dir);
scanner->current_dir = NULL;
}
free(scanner->current_path);
if (queue_is_empty(scanner->directories))
return 0;
scanner->current_path = (char*)queue_dequeue(scanner->directories);
scanner->current_dir = opendir(scanner->current_path);
if (scanner->current_dir == NULL) {
perror("Could not open directory");
free(scanner->current_path);
scanner->current_path = NULL;
return -1;
}
return 1;
}
Chunk* directory_scanner_next(DirectoryScanner* scanner) {
ArrayList* chunk_data = array_list_create(file_destroy);
unsigned long long chunk_data_size = 0;
while (1) {
if (scanner->current_dir == NULL) {
int ret = open_next_directory(scanner);
if (ret == 0)
break;
if (ret < 0)
continue;
}
struct dirent* entry = readdir(scanner->current_dir);
if (entry == NULL) {
closedir(scanner->current_dir);
scanner->current_dir = NULL;
free(scanner->current_path);
scanner->current_path = NULL;
continue;
}
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
char* cur_path = path_cat(scanner->current_path, entry->d_name);
struct stat stats;
if (stat(cur_path, &stats) != 0) {
free(cur_path);
continue;
}
if (S_ISDIR(stats.st_mode)) {
queue_enqueue(scanner->directories, (void*)cur_path);
} else {
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;
}
}
if ((scanner->max_size > 0 && (unsigned long long)stats.st_size > scanner->max_size) ||
(scanner->min_size > 0 && (unsigned long long)stats.st_size < scanner->min_size)) {
free(cur_path);
continue;
}
File* file = file_create(cur_path);
if (file == NULL) {
free(cur_path);
continue;
}
file->data->size = stats.st_size;
if (scanner->use_metadata)
file->metadata = file_metadata_create(&stats);
array_list_add(chunk_data, file);
chunk_data_size += file->data->size;
if (chunk_data_size > scanner->chunk_size) {
free(cur_path);
return chunk_data_to_chunk(chunk_data);
}
free(cur_path);
}
}
if (chunk_data->size > 0)
return chunk_data_to_chunk(chunk_data);
array_list_delete(chunk_data);
return NULL;
}