Merge all 5 batch PRs: security, CLI features, protocol, performance, tests/docs #143
@@ -305,7 +305,7 @@ static int scan_directory_multithreaded(void* pipeline_context) {
|
|||||||
context->config->send_directory, context->config->use_metadata, context->config->chunk_size,
|
context->config->send_directory, context->config->use_metadata, context->config->chunk_size,
|
||||||
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, 4);
|
context->config->min_size, context->config->max_depth, 4);
|
||||||
|
|
||||||
Chunk* current_chunk;
|
Chunk* current_chunk;
|
||||||
while ((current_chunk = parallel_scanner_next(scanner)) != NULL) {
|
while ((current_chunk = parallel_scanner_next(scanner)) != NULL) {
|
||||||
@@ -368,7 +368,7 @@ int send_files(Config* config) {
|
|||||||
DirectoryScanner* scanner = directory_scanner_create(
|
DirectoryScanner* scanner = directory_scanner_create(
|
||||||
config->send_directory, config->use_metadata, config->chunk_size, config->exclude_patterns,
|
config->send_directory, config->use_metadata, config->chunk_size, config->exclude_patterns,
|
||||||
config->exclude_count, config->include_patterns, config->include_count, config->max_size,
|
config->exclude_count, config->include_patterns, config->include_count, config->max_size,
|
||||||
config->min_size);
|
config->min_size, config->max_depth);
|
||||||
Chunk* chunk;
|
Chunk* chunk;
|
||||||
int file_count = 0;
|
int file_count = 0;
|
||||||
unsigned long long total_bytes = 0;
|
unsigned long long total_bytes = 0;
|
||||||
@@ -421,7 +421,7 @@ int send_files(Config* config) {
|
|||||||
DirectoryScanner* scanner = directory_scanner_create(
|
DirectoryScanner* scanner = directory_scanner_create(
|
||||||
config->send_directory, config->use_metadata, config->chunk_size, config->exclude_patterns,
|
config->send_directory, config->use_metadata, config->chunk_size, config->exclude_patterns,
|
||||||
config->exclude_count, config->include_patterns, config->include_count, config->max_size,
|
config->exclude_count, config->include_patterns, config->include_count, config->max_size,
|
||||||
config->min_size);
|
config->min_size, config->max_depth);
|
||||||
Chunk* current_chunk;
|
Chunk* current_chunk;
|
||||||
unsigned long long total_bytes = 0;
|
unsigned long long total_bytes = 0;
|
||||||
time_t last_progress = 0;
|
time_t last_progress = 0;
|
||||||
@@ -510,7 +510,7 @@ int send_files_multithreaded(Config* config) {
|
|||||||
DirectoryScanner* scanner = directory_scanner_create(
|
DirectoryScanner* scanner = directory_scanner_create(
|
||||||
config->send_directory, config->use_metadata, config->chunk_size, config->exclude_patterns,
|
config->send_directory, config->use_metadata, config->chunk_size, config->exclude_patterns,
|
||||||
config->exclude_count, config->include_patterns, config->include_count, config->max_size,
|
config->exclude_count, config->include_patterns, config->include_count, config->max_size,
|
||||||
config->min_size);
|
config->min_size, config->max_depth);
|
||||||
Chunk* chunk;
|
Chunk* chunk;
|
||||||
int file_count = 0;
|
int file_count = 0;
|
||||||
unsigned long long total_bytes = 0;
|
unsigned long long total_bytes = 0;
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ typedef struct {
|
|||||||
int include_count;
|
int include_count;
|
||||||
unsigned long long max_size;
|
unsigned long long max_size;
|
||||||
unsigned long long min_size;
|
unsigned long long min_size;
|
||||||
|
int max_depth;
|
||||||
} ParallelWorkerArg;
|
} ParallelWorkerArg;
|
||||||
|
|
||||||
static int parallel_worker_thread(void* arg) {
|
static int parallel_worker_thread(void* arg) {
|
||||||
@@ -222,7 +223,7 @@ static int parallel_worker_thread(void* arg) {
|
|||||||
for (int i = 0; i < wa->dir_count; i++) {
|
for (int i = 0; i < wa->dir_count; i++) {
|
||||||
DirectoryScanner* ds = directory_scanner_create(
|
DirectoryScanner* ds = directory_scanner_create(
|
||||||
wa->dirs[i], wa->use_metadata, wa->chunk_size, wa->exclude_patterns, wa->exclude_count,
|
wa->dirs[i], wa->use_metadata, wa->chunk_size, wa->exclude_patterns, wa->exclude_count,
|
||||||
wa->include_patterns, wa->include_count, wa->max_size, wa->min_size);
|
wa->include_patterns, wa->include_count, wa->max_size, wa->min_size, wa->max_depth);
|
||||||
Chunk* chunk;
|
Chunk* chunk;
|
||||||
while ((chunk = directory_scanner_next(ds)) != NULL) {
|
while ((chunk = directory_scanner_next(ds)) != NULL) {
|
||||||
queue_enqueue_multithreaded(wa->ps->result_queue, chunk, &wa->ps->result_mutex,
|
queue_enqueue_multithreaded(wa->ps->result_queue, chunk, &wa->ps->result_mutex,
|
||||||
@@ -248,7 +249,8 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata
|
|||||||
unsigned long long chunk_size, char** exclude_patterns,
|
unsigned long long chunk_size, char** exclude_patterns,
|
||||||
int exclude_count, char** include_patterns,
|
int exclude_count, char** include_patterns,
|
||||||
int include_count, unsigned long long max_size,
|
int include_count, unsigned long long max_size,
|
||||||
unsigned long long min_size, int num_threads) {
|
unsigned long long min_size, int max_depth,
|
||||||
|
int num_threads) {
|
||||||
ParallelScanner* ps = calloc(1, sizeof(ParallelScanner));
|
ParallelScanner* ps = calloc(1, sizeof(ParallelScanner));
|
||||||
if (!ps)
|
if (!ps)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -406,6 +408,7 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata
|
|||||||
wa->include_count = include_count;
|
wa->include_count = include_count;
|
||||||
wa->max_size = max_size;
|
wa->max_size = max_size;
|
||||||
wa->min_size = min_size;
|
wa->min_size = min_size;
|
||||||
|
wa->max_depth = max_depth;
|
||||||
start += count;
|
start += count;
|
||||||
if (thrd_create(&ps->threads[t], parallel_worker_thread, wa) != thrd_success) {
|
if (thrd_create(&ps->threads[t], parallel_worker_thread, wa) != thrd_success) {
|
||||||
for (int j = 0; j < count; j++)
|
for (int j = 0; j < count; j++)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ typedef struct {
|
|||||||
int include_count;
|
int include_count;
|
||||||
unsigned long long max_size;
|
unsigned long long max_size;
|
||||||
unsigned long long min_size;
|
unsigned long long min_size;
|
||||||
|
int max_depth;
|
||||||
|
int current_depth;
|
||||||
} DirectoryScanner;
|
} DirectoryScanner;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -33,11 +35,11 @@ typedef struct {
|
|||||||
Chunk* initial_chunk;
|
Chunk* initial_chunk;
|
||||||
} ParallelScanner;
|
} ParallelScanner;
|
||||||
|
|
||||||
DirectoryScanner* directory_scanner_create(char* root_directory, bool use_metadata,
|
DirectoryScanner* directory_scanner_create(const char* root_directory, bool use_metadata,
|
||||||
unsigned long long chunk_size, char** exclude_patterns,
|
unsigned long long chunk_size, char** exclude_patterns,
|
||||||
int exclude_count, char** include_patterns,
|
int exclude_count, char** include_patterns,
|
||||||
int include_count, unsigned long long max_size,
|
int include_count, unsigned long long max_size,
|
||||||
unsigned long long min_size);
|
unsigned long long min_size, int max_depth);
|
||||||
Chunk* directory_scanner_next(DirectoryScanner* scanner);
|
Chunk* directory_scanner_next(DirectoryScanner* scanner);
|
||||||
void directory_scanner_destroy(DirectoryScanner* scanner);
|
void directory_scanner_destroy(DirectoryScanner* scanner);
|
||||||
|
|
||||||
@@ -45,7 +47,8 @@ ParallelScanner* parallel_scanner_create(char* root_directory, bool use_metadata
|
|||||||
unsigned long long chunk_size, char** exclude_patterns,
|
unsigned long long chunk_size, char** exclude_patterns,
|
||||||
int exclude_count, char** include_patterns,
|
int exclude_count, char** include_patterns,
|
||||||
int include_count, unsigned long long max_size,
|
int include_count, unsigned long long max_size,
|
||||||
unsigned long long min_size, int num_threads);
|
unsigned long long min_size, int max_depth,
|
||||||
|
int num_threads);
|
||||||
Chunk* parallel_scanner_next(ParallelScanner* scanner);
|
Chunk* parallel_scanner_next(ParallelScanner* scanner);
|
||||||
void parallel_scanner_destroy(ParallelScanner* scanner);
|
void parallel_scanner_destroy(ParallelScanner* scanner);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
typedef enum { FILE_TYPE_REGULAR, FILE_TYPE_SYMLINK, FILE_TYPE_DIR } FileType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ void log_set_file(FILE* fp) {
|
|||||||
log_fp = fp;
|
log_fp = fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_message(LogLevel log_level, char* format, ...) {
|
void log_message(LogLevel log_level, const char* format, ...) {
|
||||||
if (log_level < current_log_level)
|
if (log_level < current_log_level)
|
||||||
return;
|
return;
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
|
|||||||
+27
-7
@@ -61,6 +61,19 @@ char* str_dup(const char* string) {
|
|||||||
bool glob_match(const char* pattern, const char* str) {
|
bool glob_match(const char* pattern, const char* str) {
|
||||||
while (*pattern) {
|
while (*pattern) {
|
||||||
if (*pattern == '*') {
|
if (*pattern == '*') {
|
||||||
|
if (*(pattern + 1) == '*') {
|
||||||
|
pattern += 2;
|
||||||
|
if (*pattern == '\0')
|
||||||
|
return true;
|
||||||
|
if (*pattern == '/')
|
||||||
|
pattern++;
|
||||||
|
while (*str) {
|
||||||
|
if (glob_match(pattern, str))
|
||||||
|
return true;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
return glob_match(pattern, str);
|
||||||
|
}
|
||||||
pattern++;
|
pattern++;
|
||||||
while (*str && *str != '/') {
|
while (*str && *str != '/') {
|
||||||
if (glob_match(pattern, str))
|
if (glob_match(pattern, str))
|
||||||
@@ -74,8 +87,15 @@ bool glob_match(const char* pattern, const char* str) {
|
|||||||
pattern++;
|
pattern++;
|
||||||
str++;
|
str++;
|
||||||
} else {
|
} else {
|
||||||
if (*pattern != *str)
|
if (*pattern != *str) {
|
||||||
|
if (*pattern == '/' && *(pattern + 1) == '*' && *(pattern + 2) == '*') {
|
||||||
|
const char* rest = pattern + 3;
|
||||||
|
if (*rest == '/')
|
||||||
|
rest++;
|
||||||
|
return glob_match(rest, str);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
pattern++;
|
pattern++;
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
@@ -168,18 +188,18 @@ bool has_path_traversal(const char* path) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* path_cat(const char* path1, char* path2) {
|
char* path_cat(const char* path1, const char* path2) {
|
||||||
if (path1 == NULL || *path1 == '\0')
|
if (path1 == NULL || *path1 == '\0')
|
||||||
return str_dup(path2);
|
return str_dup(path2);
|
||||||
if (path2 == NULL || *path2 == '\0')
|
if (path2 == NULL || *path2 == '\0')
|
||||||
return str_dup(path1);
|
return str_dup(path1);
|
||||||
int path1_len = strlen(path1);
|
size_t path1_len = strlen(path1);
|
||||||
int path2_len = strlen(path2);
|
size_t path2_len = strlen(path2);
|
||||||
char* path2_pointer = path2;
|
size_t offset = 0;
|
||||||
if (path1[path1_len - 1] == '/')
|
if (path1[path1_len - 1] == '/')
|
||||||
path1_len -= 1;
|
path1_len -= 1;
|
||||||
if (path2[0] == '/') {
|
if (path2[0] == '/') {
|
||||||
path2_pointer += 1;
|
offset = 1;
|
||||||
path2_len -= 1;
|
path2_len -= 1;
|
||||||
}
|
}
|
||||||
char* new_path = malloc(path1_len + path2_len + 2);
|
char* new_path = malloc(path1_len + path2_len + 2);
|
||||||
@@ -187,7 +207,7 @@ char* path_cat(const char* path1, char* path2) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
memcpy(new_path, path1, path1_len);
|
memcpy(new_path, path1, path1_len);
|
||||||
new_path[path1_len] = '/';
|
new_path[path1_len] = '/';
|
||||||
memcpy(new_path + path1_len + 1, path2_pointer, path2_len);
|
memcpy(new_path + path1_len + 1, path2 + offset, path2_len);
|
||||||
new_path[path1_len + path2_len + 1] = '\0';
|
new_path[path1_len + path2_len + 1] = '\0';
|
||||||
return new_path;
|
return new_path;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,10 +217,6 @@ static void test_file_send_no_path() {
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(p[1]);
|
close(p[1]);
|
||||||
// Read file type indicator
|
|
||||||
int file_type;
|
|
||||||
EXPECT_TRUE(receive_int(p[0], &file_type));
|
|
||||||
EXPECT_EQ_INT(file_type, (int)FILE_TYPE_REGULAR);
|
|
||||||
Data* received = receive_data(p[0]);
|
Data* received = receive_data(p[0]);
|
||||||
close(p[0]);
|
close(p[0]);
|
||||||
|
|
||||||
|
|||||||
@@ -224,10 +224,6 @@ static void test_sendfile_no_path() {
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(p[1]);
|
close(p[1]);
|
||||||
/* When send_path is false, the sender still sends file_type + data */
|
|
||||||
int file_type;
|
|
||||||
EXPECT_TRUE(receive_int(p[0], &file_type));
|
|
||||||
EXPECT_EQ_INT(file_type, (int)FILE_TYPE_REGULAR);
|
|
||||||
Data* received = receive_data(p[0]);
|
Data* received = receive_data(p[0]);
|
||||||
close(p[0]);
|
close(p[0]);
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ static void test_scanner_exclude_pattern() {
|
|||||||
|
|
||||||
char* exclude[] = {"*.tmp"};
|
char* exclude[] = {"*.tmp"};
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)dir, false, 0, exclude, 1, NULL, 0, 0, 0);
|
directory_scanner_create((char*)dir, false, 0, exclude, 1, NULL, 0, 0, 0, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
Chunk* chunk = directory_scanner_next(scanner);
|
Chunk* chunk = directory_scanner_next(scanner);
|
||||||
@@ -169,7 +169,7 @@ static void test_scanner_exclude_subdirectory() {
|
|||||||
|
|
||||||
char* exclude[] = {"*.tmp"};
|
char* exclude[] = {"*.tmp"};
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)root, false, 0, exclude, 1, NULL, 0, 0, 0);
|
directory_scanner_create((char*)root, false, 0, exclude, 1, NULL, 0, 0, 0, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
int total = 0;
|
int total = 0;
|
||||||
@@ -207,7 +207,7 @@ static void test_scanner_include_and_exclude() {
|
|||||||
char* exclude[] = {"*.bak"};
|
char* exclude[] = {"*.bak"};
|
||||||
char* include[] = {"*.txt", "*.log"};
|
char* include[] = {"*.txt", "*.log"};
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)dir, false, 0, exclude, 1, include, 2, 0, 0);
|
directory_scanner_create((char*)dir, false, 0, exclude, 1, include, 2, 0, 0, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
Chunk* chunk = directory_scanner_next(scanner);
|
Chunk* chunk = directory_scanner_next(scanner);
|
||||||
@@ -244,7 +244,7 @@ static void test_scanner_max_size() {
|
|||||||
|
|
||||||
/* max_size = 10 — only files <= 10 bytes */
|
/* max_size = 10 — only files <= 10 bytes */
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 10, 0);
|
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 10, 0, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
Chunk* chunk = directory_scanner_next(scanner);
|
Chunk* chunk = directory_scanner_next(scanner);
|
||||||
@@ -272,7 +272,7 @@ static void test_scanner_min_size() {
|
|||||||
|
|
||||||
/* min_size = 1 — only files >= 1 byte */
|
/* min_size = 1 — only files >= 1 byte */
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 1);
|
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 1, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
Chunk* chunk = directory_scanner_next(scanner);
|
Chunk* chunk = directory_scanner_next(scanner);
|
||||||
@@ -302,7 +302,7 @@ static void test_scanner_size_range() {
|
|||||||
|
|
||||||
/* Only files between 3 and 20 bytes */
|
/* Only files between 3 and 20 bytes */
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 20, 3);
|
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 20, 3, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
Chunk* chunk = directory_scanner_next(scanner);
|
Chunk* chunk = directory_scanner_next(scanner);
|
||||||
@@ -338,7 +338,7 @@ static void test_scanner_mixed_patterns() {
|
|||||||
char* exclude[] = {"*.bak"};
|
char* exclude[] = {"*.bak"};
|
||||||
char* include[] = {"*.txt"};
|
char* include[] = {"*.txt"};
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)dir, false, 0, exclude, 1, include, 1, 10, 3);
|
directory_scanner_create((char*)dir, false, 0, exclude, 1, include, 1, 10, 3, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
Chunk* chunk = directory_scanner_next(scanner);
|
Chunk* chunk = directory_scanner_next(scanner);
|
||||||
@@ -369,7 +369,7 @@ static void test_scanner_no_patterns() {
|
|||||||
create_test_file(f2, "second");
|
create_test_file(f2, "second");
|
||||||
|
|
||||||
DirectoryScanner* scanner =
|
DirectoryScanner* scanner =
|
||||||
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 0);
|
directory_scanner_create((char*)dir, false, 0, NULL, 0, NULL, 0, 0, 0, 0);
|
||||||
EXPECT_NOT_NULL(scanner);
|
EXPECT_NOT_NULL(scanner);
|
||||||
|
|
||||||
Chunk* chunk = directory_scanner_next(scanner);
|
Chunk* chunk = directory_scanner_next(scanner);
|
||||||
|
|||||||
Reference in New Issue
Block a user