fix: refactoring and portability — issues #61, #51, #52
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 / 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
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 / 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:
@@ -55,6 +55,7 @@ static void print_usage(void) {
|
|||||||
printf(" --cert <path> TLS certificate file (PEM)\n");
|
printf(" --cert <path> TLS certificate file (PEM)\n");
|
||||||
printf(" --key <path> TLS private key file (PEM)\n");
|
printf(" --key <path> TLS private key file (PEM)\n");
|
||||||
printf(" --ca <path> TLS CA certificate file (PEM)\n");
|
printf(" --ca <path> TLS CA certificate file (PEM)\n");
|
||||||
|
printf(" -V, --version Show version information\n");
|
||||||
printf(" --help Show this help\n");
|
printf(" --help Show this help\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +81,9 @@ int main(int argc, char* argv[]) {
|
|||||||
if (strcmp(argv[i], "--help") == 0) {
|
if (strcmp(argv[i], "--help") == 0) {
|
||||||
print_usage();
|
print_usage();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
} else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-V") == 0) {
|
||||||
|
printf("FastSync version %s\n", PROTOCOL_VERSION);
|
||||||
|
goto cleanup;
|
||||||
} else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--archive") == 0) {
|
} else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--archive") == 0) {
|
||||||
config->use_compression = true;
|
config->use_compression = true;
|
||||||
config->use_multithreading = true;
|
config->use_multithreading = true;
|
||||||
|
|||||||
+57
-2
@@ -24,9 +24,58 @@ DirectoryScanner* directory_scanner_create(char* root_directory, bool use_metada
|
|||||||
scanner->current_path = NULL;
|
scanner->current_path = NULL;
|
||||||
scanner->use_metadata = use_metadata;
|
scanner->use_metadata = use_metadata;
|
||||||
scanner->chunk_size = chunk_size > 0 ? chunk_size : DESIRED_CHUNK_SIZE;
|
scanner->chunk_size = chunk_size > 0 ? chunk_size : DESIRED_CHUNK_SIZE;
|
||||||
scanner->exclude_patterns = exclude_patterns;
|
/* Deep-copy exclude patterns */
|
||||||
|
if (exclude_count > 0 && exclude_patterns != NULL) {
|
||||||
|
scanner->exclude_patterns = malloc((size_t)exclude_count * sizeof(char*));
|
||||||
|
if (scanner->exclude_patterns == NULL) {
|
||||||
|
queue_destroy(scanner->directories);
|
||||||
|
free(scanner);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < exclude_count; i++) {
|
||||||
|
scanner->exclude_patterns[i] = str_dup(exclude_patterns[i]);
|
||||||
|
if (scanner->exclude_patterns[i] == NULL) {
|
||||||
|
for (int j = 0; j < i; j++)
|
||||||
|
free(scanner->exclude_patterns[j]);
|
||||||
|
free(scanner->exclude_patterns);
|
||||||
|
queue_destroy(scanner->directories);
|
||||||
|
free(scanner);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scanner->exclude_patterns = NULL;
|
||||||
|
}
|
||||||
scanner->exclude_count = exclude_count;
|
scanner->exclude_count = exclude_count;
|
||||||
scanner->include_patterns = include_patterns;
|
|
||||||
|
/* Deep-copy include patterns */
|
||||||
|
if (include_count > 0 && include_patterns != NULL) {
|
||||||
|
scanner->include_patterns = malloc((size_t)include_count * sizeof(char*));
|
||||||
|
if (scanner->include_patterns == NULL) {
|
||||||
|
for (int i = 0; i < exclude_count; i++)
|
||||||
|
free(scanner->exclude_patterns[i]);
|
||||||
|
free(scanner->exclude_patterns);
|
||||||
|
queue_destroy(scanner->directories);
|
||||||
|
free(scanner);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < include_count; i++) {
|
||||||
|
scanner->include_patterns[i] = str_dup(include_patterns[i]);
|
||||||
|
if (scanner->include_patterns[i] == NULL) {
|
||||||
|
for (int j = 0; j < i; j++)
|
||||||
|
free(scanner->include_patterns[j]);
|
||||||
|
free(scanner->include_patterns);
|
||||||
|
for (int j = 0; j < exclude_count; j++)
|
||||||
|
free(scanner->exclude_patterns[j]);
|
||||||
|
free(scanner->exclude_patterns);
|
||||||
|
queue_destroy(scanner->directories);
|
||||||
|
free(scanner);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scanner->include_patterns = NULL;
|
||||||
|
}
|
||||||
scanner->include_count = include_count;
|
scanner->include_count = include_count;
|
||||||
scanner->max_size = max_size;
|
scanner->max_size = max_size;
|
||||||
scanner->min_size = min_size;
|
scanner->min_size = min_size;
|
||||||
@@ -42,6 +91,12 @@ void directory_scanner_destroy(DirectoryScanner* scanner) {
|
|||||||
scanner->current_dir = NULL;
|
scanner->current_dir = NULL;
|
||||||
}
|
}
|
||||||
free(scanner->current_path);
|
free(scanner->current_path);
|
||||||
|
for (int i = 0; i < scanner->exclude_count; i++)
|
||||||
|
free(scanner->exclude_patterns[i]);
|
||||||
|
free(scanner->exclude_patterns);
|
||||||
|
for (int i = 0; i < scanner->include_count; i++)
|
||||||
|
free(scanner->include_patterns[i]);
|
||||||
|
free(scanner->include_patterns);
|
||||||
queue_destroy(scanner->directories);
|
queue_destroy(scanner->directories);
|
||||||
free(scanner);
|
free(scanner);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ static void print_server_usage(void) {
|
|||||||
printf(" --key <path> TLS private key file (PEM)\n");
|
printf(" --key <path> TLS private key file (PEM)\n");
|
||||||
printf(" --ca <path> TLS CA certificate file (PEM)\n");
|
printf(" --ca <path> TLS CA certificate file (PEM)\n");
|
||||||
printf(" -v, --verbose Enable debug logging\n");
|
printf(" -v, --verbose Enable debug logging\n");
|
||||||
|
printf(" -V, --version Show version information\n");
|
||||||
printf(" --help Show this help\n");
|
printf(" --help Show this help\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +150,9 @@ int main(int argc, char* argv[]) {
|
|||||||
if (strcmp(argv[i], "--help") == 0) {
|
if (strcmp(argv[i], "--help") == 0) {
|
||||||
print_server_usage();
|
print_server_usage();
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-V") == 0) {
|
||||||
|
printf("FastSync Server version %s\n", PROTOCOL_VERSION);
|
||||||
|
return 0;
|
||||||
} else if (strcmp(argv[i], "--stdio") == 0) {
|
} else if (strcmp(argv[i], "--stdio") == 0) {
|
||||||
io_set_fds(STDIN_FILENO, STDOUT_FILENO);
|
io_set_fds(STDIN_FILENO, STDOUT_FILENO);
|
||||||
handler(STDIN_FILENO);
|
handler(STDIN_FILENO);
|
||||||
|
|||||||
@@ -66,8 +66,16 @@ Data* data_decompress(Data* compressed_data) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t buf_size =
|
size_t buf_size = INITIAL_DECOMPRESS_BUF_SIZE;
|
||||||
(!ZSTD_isError(dst_size) && dst_size > 0) ? (size_t)dst_size : INITIAL_DECOMPRESS_BUF_SIZE;
|
if (!ZSTD_isError(dst_size) && dst_size > 0) {
|
||||||
|
if (dst_size > SIZE_MAX) {
|
||||||
|
log_message(LOG_LEVEL_ERROR,
|
||||||
|
"Decompressed size %llu exceeds addressable memory, using fallback buffer",
|
||||||
|
dst_size);
|
||||||
|
} else {
|
||||||
|
buf_size = (size_t)dst_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
Data* uncompressed_data = data_create_empty(buf_size);
|
Data* uncompressed_data = data_create_empty(buf_size);
|
||||||
if (!uncompressed_data) {
|
if (!uncompressed_data) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to allocate decompression buffer");
|
log_message(LOG_LEVEL_ERROR, "Failed to allocate decompression buffer");
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "stdlib.h"
|
#include "stdlib.h"
|
||||||
|
|
||||||
Data* data_create_empty(size_t data_size) {
|
Data* data_create_empty(size_t data_size) {
|
||||||
|
if (data_size == 0)
|
||||||
|
data_size = 1;
|
||||||
void* data = malloc(data_size);
|
void* data = malloc(data_size);
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Could not allocate memory for empty data");
|
log_message(LOG_LEVEL_ERROR, "Could not allocate memory for empty data");
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
#ifndef DATA_H
|
#ifndef DATA_H
|
||||||
#define DATA_H
|
#define DATA_H
|
||||||
|
|
||||||
#include "stdlib.h"
|
#include <stdlib.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* data;
|
void* data;
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ void set_log_level(LogLevel level) {
|
|||||||
current_log_level = level;
|
current_log_level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
typedef enum { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR } LogLevel;
|
typedef enum { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR } LogLevel;
|
||||||
|
|
||||||
void log_message(LogLevel log_level, char* message, ...);
|
void log_message(LogLevel log_level, const char* message, ...);
|
||||||
void set_log_level(LogLevel level);
|
void set_log_level(LogLevel level);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+55
-30
@@ -4,6 +4,7 @@
|
|||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -16,16 +17,21 @@ void metadata_to_buf(char** buf, const FileMetadata* m) {
|
|||||||
*buf += sizeof(int);
|
*buf += sizeof(int);
|
||||||
if (m == NULL)
|
if (m == NULL)
|
||||||
return;
|
return;
|
||||||
memcpy(*buf, &m->mode, sizeof(mode_t));
|
int32_t tmp32 = (int32_t)m->mode;
|
||||||
*buf += sizeof(mode_t);
|
memcpy(*buf, &tmp32, sizeof(int32_t));
|
||||||
memcpy(*buf, &m->uid, sizeof(uid_t));
|
*buf += sizeof(int32_t);
|
||||||
*buf += sizeof(uid_t);
|
tmp32 = (int32_t)m->uid;
|
||||||
memcpy(*buf, &m->gid, sizeof(gid_t));
|
memcpy(*buf, &tmp32, sizeof(int32_t));
|
||||||
*buf += sizeof(gid_t);
|
*buf += sizeof(int32_t);
|
||||||
memcpy(*buf, &m->mtime_sec, sizeof(time_t));
|
tmp32 = (int32_t)m->gid;
|
||||||
*buf += sizeof(time_t);
|
memcpy(*buf, &tmp32, sizeof(int32_t));
|
||||||
memcpy(*buf, &m->mtime_nsec, sizeof(long));
|
*buf += sizeof(int32_t);
|
||||||
*buf += sizeof(long);
|
int64_t tmp64 = (int64_t)m->mtime_sec;
|
||||||
|
memcpy(*buf, &tmp64, sizeof(int64_t));
|
||||||
|
*buf += sizeof(int64_t);
|
||||||
|
tmp64 = (int64_t)m->mtime_nsec;
|
||||||
|
memcpy(*buf, &tmp64, sizeof(int64_t));
|
||||||
|
*buf += sizeof(int64_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileMetadata* metadata_from_buf(char** buf) {
|
FileMetadata* metadata_from_buf(char** buf) {
|
||||||
@@ -35,16 +41,23 @@ FileMetadata* metadata_from_buf(char** buf) {
|
|||||||
if (!present)
|
if (!present)
|
||||||
return NULL;
|
return NULL;
|
||||||
FileMetadata* m = malloc(sizeof(FileMetadata));
|
FileMetadata* m = malloc(sizeof(FileMetadata));
|
||||||
memcpy(&m->mode, *buf, sizeof(mode_t));
|
int32_t tmp32;
|
||||||
*buf += sizeof(mode_t);
|
int64_t tmp64;
|
||||||
memcpy(&m->uid, *buf, sizeof(uid_t));
|
memcpy(&tmp32, *buf, sizeof(int32_t));
|
||||||
*buf += sizeof(uid_t);
|
*buf += sizeof(int32_t);
|
||||||
memcpy(&m->gid, *buf, sizeof(gid_t));
|
m->mode = (mode_t)tmp32;
|
||||||
*buf += sizeof(gid_t);
|
memcpy(&tmp32, *buf, sizeof(int32_t));
|
||||||
memcpy(&m->mtime_sec, *buf, sizeof(time_t));
|
*buf += sizeof(int32_t);
|
||||||
*buf += sizeof(time_t);
|
m->uid = (uid_t)tmp32;
|
||||||
memcpy(&m->mtime_nsec, *buf, sizeof(long));
|
memcpy(&tmp32, *buf, sizeof(int32_t));
|
||||||
*buf += sizeof(long);
|
*buf += sizeof(int32_t);
|
||||||
|
m->gid = (gid_t)tmp32;
|
||||||
|
memcpy(&tmp64, *buf, sizeof(int64_t));
|
||||||
|
*buf += sizeof(int64_t);
|
||||||
|
m->mtime_sec = (time_t)tmp64;
|
||||||
|
memcpy(&tmp64, *buf, sizeof(int64_t));
|
||||||
|
*buf += sizeof(int64_t);
|
||||||
|
m->mtime_nsec = (long)tmp64;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,12 +67,17 @@ bool metadata_send(int file_descriptor, FileMetadata* m) {
|
|||||||
return send_n_data(file_descriptor, &zero, sizeof(int));
|
return send_n_data(file_descriptor, &zero, sizeof(int));
|
||||||
}
|
}
|
||||||
int present = 1;
|
int present = 1;
|
||||||
|
int32_t mode_i32 = (int32_t)m->mode;
|
||||||
|
int32_t uid_i32 = (int32_t)m->uid;
|
||||||
|
int32_t gid_i32 = (int32_t)m->gid;
|
||||||
|
int64_t mtime_sec_i64 = (int64_t)m->mtime_sec;
|
||||||
|
int64_t mtime_nsec_i64 = (int64_t)m->mtime_nsec;
|
||||||
return send_n_data(file_descriptor, &present, sizeof(int)) &&
|
return send_n_data(file_descriptor, &present, sizeof(int)) &&
|
||||||
send_n_data(file_descriptor, &m->mode, sizeof(mode_t)) &&
|
send_n_data(file_descriptor, &mode_i32, sizeof(int32_t)) &&
|
||||||
send_n_data(file_descriptor, &m->uid, sizeof(uid_t)) &&
|
send_n_data(file_descriptor, &uid_i32, sizeof(int32_t)) &&
|
||||||
send_n_data(file_descriptor, &m->gid, sizeof(gid_t)) &&
|
send_n_data(file_descriptor, &gid_i32, sizeof(int32_t)) &&
|
||||||
send_n_data(file_descriptor, &m->mtime_sec, sizeof(time_t)) &&
|
send_n_data(file_descriptor, &mtime_sec_i64, sizeof(int64_t)) &&
|
||||||
send_n_data(file_descriptor, &m->mtime_nsec, sizeof(long));
|
send_n_data(file_descriptor, &mtime_nsec_i64, sizeof(int64_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
FileMetadata* metadata_receive(int file_descriptor, int* ok) {
|
FileMetadata* metadata_receive(int file_descriptor, int* ok) {
|
||||||
@@ -80,16 +98,23 @@ FileMetadata* metadata_receive(int file_descriptor, int* ok) {
|
|||||||
*ok = 0;
|
*ok = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!receive_n_data(file_descriptor, &m->mode, sizeof(mode_t)) ||
|
int32_t mode_i32, uid_i32, gid_i32;
|
||||||
!receive_n_data(file_descriptor, &m->uid, sizeof(uid_t)) ||
|
int64_t mtime_sec_i64, mtime_nsec_i64;
|
||||||
!receive_n_data(file_descriptor, &m->gid, sizeof(gid_t)) ||
|
if (!receive_n_data(file_descriptor, &mode_i32, sizeof(int32_t)) ||
|
||||||
!receive_n_data(file_descriptor, &m->mtime_sec, sizeof(time_t)) ||
|
!receive_n_data(file_descriptor, &uid_i32, sizeof(int32_t)) ||
|
||||||
!receive_n_data(file_descriptor, &m->mtime_nsec, sizeof(long))) {
|
!receive_n_data(file_descriptor, &gid_i32, sizeof(int32_t)) ||
|
||||||
|
!receive_n_data(file_descriptor, &mtime_sec_i64, sizeof(int64_t)) ||
|
||||||
|
!receive_n_data(file_descriptor, &mtime_nsec_i64, sizeof(int64_t))) {
|
||||||
free(m);
|
free(m);
|
||||||
if (ok)
|
if (ok)
|
||||||
*ok = 0;
|
*ok = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
m->mode = (mode_t)mode_i32;
|
||||||
|
m->uid = (uid_t)uid_i32;
|
||||||
|
m->gid = (gid_t)gid_i32;
|
||||||
|
m->mtime_sec = (time_t)mtime_sec_i64;
|
||||||
|
m->mtime_nsec = (long)mtime_nsec_i64;
|
||||||
if (ok)
|
if (ok)
|
||||||
*ok = 1;
|
*ok = 1;
|
||||||
return m;
|
return m;
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#define FILE_METADATA_WIRE_SIZE \
|
#define FILE_METADATA_WIRE_SIZE (sizeof(int32_t) * 3 + sizeof(int64_t) * 2)
|
||||||
(sizeof(mode_t) + sizeof(uid_t) + sizeof(gid_t) + sizeof(time_t) + sizeof(long))
|
|
||||||
|
|
||||||
void metadata_to_buf(char** buf, const FileMetadata* m);
|
void metadata_to_buf(char** buf, const FileMetadata* m);
|
||||||
FileMetadata* metadata_from_buf(char** buf);
|
FileMetadata* metadata_from_buf(char** buf);
|
||||||
|
|||||||
+44
-6
@@ -34,11 +34,17 @@ static void bw_throttle(size_t bytes_written) {
|
|||||||
struct timespec now;
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
long long elapsed_ns =
|
/* Use unsigned long long for intermediate computation to avoid overflow.
|
||||||
(now.tv_sec - bw_last_refill.tv_sec) * 1000000000LL + (now.tv_nsec - bw_last_refill.tv_nsec);
|
* sec_diff * 1000000000LL could overflow a signed 64-bit if the elapsed
|
||||||
|
* time is very large; clamp to a safe maximum. */
|
||||||
|
unsigned long long sec_diff = (unsigned long long)(now.tv_sec - bw_last_refill.tv_sec);
|
||||||
|
if (sec_diff > 9223372036ULL)
|
||||||
|
sec_diff = 9223372036ULL;
|
||||||
|
unsigned long long elapsed_ns =
|
||||||
|
sec_diff * 1000000000ULL + (unsigned long long)(now.tv_nsec - bw_last_refill.tv_nsec);
|
||||||
bw_last_refill = now;
|
bw_last_refill = now;
|
||||||
|
|
||||||
long long tokens_to_add = (long long)((double)io_bwlimit * elapsed_ns / 1000000000.0);
|
long long tokens_to_add = (long long)((double)io_bwlimit * (double)elapsed_ns / 1000000000.0);
|
||||||
bw_tokens += tokens_to_add;
|
bw_tokens += tokens_to_add;
|
||||||
if (bw_tokens > (long long)io_bwlimit)
|
if (bw_tokens > (long long)io_bwlimit)
|
||||||
bw_tokens = (long long)io_bwlimit;
|
bw_tokens = (long long)io_bwlimit;
|
||||||
@@ -74,14 +80,24 @@ bool send_n_data(int file_descriptor, const void* data, size_t data_size) {
|
|||||||
if (io_bwlimit > 0 && chunk > 65536)
|
if (io_bwlimit > 0 && chunk > 65536)
|
||||||
chunk = 65536;
|
chunk = 65536;
|
||||||
ssize_t bytes_send;
|
ssize_t bytes_send;
|
||||||
if (io_ssl)
|
if (io_ssl) {
|
||||||
bytes_send = SSL_write(io_ssl, (const char*)data + total_bytes_send, chunk);
|
bytes_send = SSL_write(io_ssl, (const char*)data + total_bytes_send, chunk);
|
||||||
else
|
if (bytes_send <= 0) {
|
||||||
|
int err = SSL_get_error(io_ssl, (int)bytes_send);
|
||||||
|
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||||
|
// Non-fatal: retry without counting progress
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Could not send data (SSL error: %d)", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
bytes_send = write(fd, (const char*)data + total_bytes_send, chunk);
|
bytes_send = write(fd, (const char*)data + total_bytes_send, chunk);
|
||||||
if (bytes_send <= 0) {
|
if (bytes_send <= 0) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Could not send data");
|
log_message(LOG_LEVEL_ERROR, "Could not send data");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
bw_throttle((size_t)bytes_send);
|
bw_throttle((size_t)bytes_send);
|
||||||
total_bytes_send += bytes_send;
|
total_bytes_send += bytes_send;
|
||||||
}
|
}
|
||||||
@@ -95,10 +111,22 @@ bool receive_n_data(int file_descriptor, void* data, size_t data_size) {
|
|||||||
size_t total_bytes_received = 0;
|
size_t total_bytes_received = 0;
|
||||||
while (total_bytes_received < data_size) {
|
while (total_bytes_received < data_size) {
|
||||||
ssize_t bytes_received;
|
ssize_t bytes_received;
|
||||||
if (io_ssl)
|
if (io_ssl) {
|
||||||
bytes_received =
|
bytes_received =
|
||||||
SSL_read(io_ssl, (char*)data + total_bytes_received, data_size - total_bytes_received);
|
SSL_read(io_ssl, (char*)data + total_bytes_received, data_size - total_bytes_received);
|
||||||
|
if (bytes_received <= 0) {
|
||||||
|
int err = SSL_get_error(io_ssl, (int)bytes_received);
|
||||||
|
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
// Non-fatal: retry without counting progress
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (bytes_received == 0)
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Connection closed while receiving data");
|
||||||
else
|
else
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Could not receive bytes (SSL error: %d)", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
bytes_received =
|
bytes_received =
|
||||||
read(fd, (char*)data + total_bytes_received, data_size - total_bytes_received);
|
read(fd, (char*)data + total_bytes_received, data_size - total_bytes_received);
|
||||||
if (bytes_received <= 0) {
|
if (bytes_received <= 0) {
|
||||||
@@ -108,6 +136,7 @@ bool receive_n_data(int file_descriptor, void* data, size_t data_size) {
|
|||||||
log_message(LOG_LEVEL_ERROR, "Could not receive bytes");
|
log_message(LOG_LEVEL_ERROR, "Could not receive bytes");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
total_bytes_received += bytes_received;
|
total_bytes_received += bytes_received;
|
||||||
}
|
}
|
||||||
log_message(LOG_LEVEL_DEBUG, " Received n Data: %zu", total_bytes_received);
|
log_message(LOG_LEVEL_DEBUG, " Received n Data: %zu", total_bytes_received);
|
||||||
@@ -138,6 +167,10 @@ static const char* status_to_string(Status status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool send_str(int file_descriptor, const char* data) {
|
bool send_str(int file_descriptor, const char* data) {
|
||||||
|
if (data == NULL) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "send_str called with NULL data");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
size_t size = strlen(data);
|
size_t size = strlen(data);
|
||||||
if (!send_n_data(file_descriptor, &size, sizeof(size_t)))
|
if (!send_n_data(file_descriptor, &size, sizeof(size_t)))
|
||||||
return false;
|
return false;
|
||||||
@@ -151,6 +184,11 @@ char* receive_str(int file_descriptor) {
|
|||||||
size_t size;
|
size_t size;
|
||||||
if (!receive_n_data(file_descriptor, &size, sizeof(size_t)))
|
if (!receive_n_data(file_descriptor, &size, sizeof(size_t)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (size > MAX_STRING_SIZE) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "receive_str: size %zu exceeds maximum %zu", size,
|
||||||
|
(size_t)MAX_STRING_SIZE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
char* data = (char*)malloc(size + 1);
|
char* data = (char*)malloc(size + 1);
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* Maximum allowed string size for receive_str (10 MB) */
|
||||||
|
#define MAX_STRING_SIZE (10 * 1024 * 1024)
|
||||||
|
|
||||||
typedef struct ssl_st SSL;
|
typedef struct ssl_st SSL;
|
||||||
|
|
||||||
typedef int Status;
|
typedef int Status;
|
||||||
|
|||||||
@@ -124,7 +124,11 @@ Client* client_connect_ssh(const char* destination, int port) {
|
|||||||
else
|
else
|
||||||
snprintf(ssh_user, sizeof(ssh_user), "%s", r.host);
|
snprintf(ssh_user, sizeof(ssh_user), "%s", r.host);
|
||||||
|
|
||||||
char* ssh_argv[16];
|
/* Max entries: ssh + 3 options*2 each + -p + port + user + cmd + arg + NULL = 13 */
|
||||||
|
size_t ssh_argv_max = 32;
|
||||||
|
char** ssh_argv = calloc(ssh_argv_max, sizeof(char*));
|
||||||
|
if (ssh_argv == NULL)
|
||||||
|
_exit(1);
|
||||||
int ac = 0;
|
int ac = 0;
|
||||||
char port_str[16];
|
char port_str[16];
|
||||||
ssh_argv[ac++] = "ssh";
|
ssh_argv[ac++] = "ssh";
|
||||||
@@ -135,15 +139,24 @@ Client* client_connect_ssh(const char* destination, int port) {
|
|||||||
ssh_argv[ac++] = "-o";
|
ssh_argv[ac++] = "-o";
|
||||||
ssh_argv[ac++] = "ControlPath=~/.cache/fastsync-%r@%h:%p";
|
ssh_argv[ac++] = "ControlPath=~/.cache/fastsync-%r@%h:%p";
|
||||||
if (port > 0 && port != 22) {
|
if (port > 0 && port != 22) {
|
||||||
|
if ((size_t)ac + 2 >= ssh_argv_max) {
|
||||||
|
free(ssh_argv);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
ssh_argv[ac++] = "-p";
|
ssh_argv[ac++] = "-p";
|
||||||
snprintf(port_str, sizeof(port_str), "%d", port);
|
snprintf(port_str, sizeof(port_str), "%d", port);
|
||||||
ssh_argv[ac++] = port_str;
|
ssh_argv[ac++] = port_str;
|
||||||
}
|
}
|
||||||
|
if ((size_t)ac + 3 >= ssh_argv_max) {
|
||||||
|
free(ssh_argv);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
ssh_argv[ac++] = ssh_user;
|
ssh_argv[ac++] = ssh_user;
|
||||||
ssh_argv[ac++] = "fastsync-server";
|
ssh_argv[ac++] = "fastsync-server";
|
||||||
ssh_argv[ac++] = "--stdio";
|
ssh_argv[ac++] = "--stdio";
|
||||||
ssh_argv[ac] = NULL;
|
ssh_argv[ac] = NULL;
|
||||||
execvp("ssh", ssh_argv);
|
execvp("ssh", ssh_argv);
|
||||||
|
free(ssh_argv);
|
||||||
perror("exec of ssh failed");
|
perror("exec of ssh failed");
|
||||||
ssize_t wret = write(exec_pipe[1], "x", 1);
|
ssize_t wret = write(exec_pipe[1], "x", 1);
|
||||||
(void)wret;
|
(void)wret;
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
#ifndef TRANSPORT_TCP_H
|
#ifndef TRANSPORT_TCP_H
|
||||||
#define TRANSPORT_TCP_H
|
#define TRANSPORT_TCP_H
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
typedef struct Server {
|
typedef struct Server {
|
||||||
struct sockaddr_in address;
|
struct sockaddr_storage address;
|
||||||
unsigned int address_length;
|
socklen_t address_length;
|
||||||
int file_descriptor;
|
int file_descriptor;
|
||||||
|
int port;
|
||||||
void* ssl_ctx;
|
void* ssl_ctx;
|
||||||
} Server;
|
} Server;
|
||||||
|
|
||||||
typedef struct Client {
|
typedef struct Client {
|
||||||
struct sockaddr_in address;
|
struct sockaddr_storage address;
|
||||||
unsigned int address_length;
|
socklen_t address_length;
|
||||||
int file_descriptor;
|
int file_descriptor;
|
||||||
pid_t ssh_child_pid;
|
pid_t ssh_child_pid;
|
||||||
void* ssl;
|
void* ssl;
|
||||||
|
|||||||
+20
-10
@@ -10,19 +10,23 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
bool mkdir_r(const char* path) {
|
bool mkdir_r(const char* path) {
|
||||||
char* path_duplicate = malloc(strlen(path) + 1);
|
size_t path_len = strlen(path);
|
||||||
|
char* path_duplicate = malloc(path_len + 1);
|
||||||
if (!path_duplicate)
|
if (!path_duplicate)
|
||||||
return false;
|
return false;
|
||||||
strcpy(path_duplicate, path);
|
memcpy(path_duplicate, path, path_len + 1);
|
||||||
char* path_current = (char*)malloc((strlen(path) + 2) * sizeof(char));
|
/* Buffer for building subpaths: path_len + 1 for leading '/' + 1 for null */
|
||||||
|
size_t buf_size = path_len + 2;
|
||||||
|
char* path_current = (char*)malloc(buf_size);
|
||||||
if (!path_current) {
|
if (!path_current) {
|
||||||
free(path_duplicate);
|
free(path_duplicate);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char* path_current_position = path_current;
|
size_t pos = 0;
|
||||||
if (path[0] == '/') {
|
if (path[0] == '/') {
|
||||||
strcpy(path_current, "/");
|
path_current[0] = '/';
|
||||||
path_current_position += 1;
|
path_current[1] = '\0';
|
||||||
|
pos = 1;
|
||||||
} else {
|
} else {
|
||||||
path_current[0] = '\0';
|
path_current[0] = '\0';
|
||||||
}
|
}
|
||||||
@@ -31,10 +35,16 @@ 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);
|
if (pos + part_len + 1 >= buf_size) {
|
||||||
strcpy(path_current_position, "/");
|
ok = false;
|
||||||
path_current_position += sizeof(char);
|
break;
|
||||||
|
}
|
||||||
|
memcpy(path_current + pos, part, part_len);
|
||||||
|
pos += part_len;
|
||||||
|
path_current[pos] = '/';
|
||||||
|
pos++;
|
||||||
|
path_current[pos] = '\0';
|
||||||
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) {
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ static void test_data_create_empty() {
|
|||||||
data_destroy(d);
|
data_destroy(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_data_create_empty_zero() {
|
||||||
|
Data* d = data_create_empty(0);
|
||||||
|
EXPECT_NOT_NULL(d);
|
||||||
|
EXPECT_NOT_NULL(d->data);
|
||||||
|
EXPECT_EQ_INT((int)d->size, 0);
|
||||||
|
data_destroy(d);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_data_create_reserve() {
|
static void test_data_create_reserve() {
|
||||||
Data* d = data_create_reserve(1024);
|
Data* d = data_create_reserve(1024);
|
||||||
EXPECT_NOT_NULL(d);
|
EXPECT_NOT_NULL(d);
|
||||||
@@ -44,6 +52,7 @@ static void test_data_destroy_normal() {
|
|||||||
void test_data() {
|
void test_data() {
|
||||||
test_data_create();
|
test_data_create();
|
||||||
test_data_create_empty();
|
test_data_create_empty();
|
||||||
|
test_data_create_empty_zero();
|
||||||
test_data_create_reserve();
|
test_data_create_reserve();
|
||||||
test_data_destroy_null();
|
test_data_destroy_null();
|
||||||
test_data_destroy_normal();
|
test_data_destroy_normal();
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
#include "test_file_sendfile.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static void test_sendfile_basic() {
|
||||||
|
const char* content = "Hello sendfile test content!";
|
||||||
|
size_t len = strlen(content);
|
||||||
|
EXPECT_TRUE(to_disk("test_sendfile_basic.txt", content, len));
|
||||||
|
|
||||||
|
File* file = file_create("test_sendfile_basic.txt");
|
||||||
|
EXPECT_NOT_NULL(file);
|
||||||
|
file->data->size = len;
|
||||||
|
file->data->data = malloc(len);
|
||||||
|
EXPECT_NOT_NULL(file->data->data);
|
||||||
|
memcpy(file->data->data, content, len);
|
||||||
|
|
||||||
|
int p[2];
|
||||||
|
EXPECT_EQ_INT(pipe(p), 0);
|
||||||
|
io_set_fds(p[0], p[1]);
|
||||||
|
io_set_bwlimit(0);
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
close(p[1]);
|
||||||
|
// Receive path: read size then data
|
||||||
|
unsigned long long recv_size;
|
||||||
|
EXPECT_TRUE(receive_n_data(p[0], &recv_size, sizeof(recv_size)));
|
||||||
|
EXPECT_EQ_INT((int)recv_size, (int)len);
|
||||||
|
|
||||||
|
char* buf = malloc(recv_size + 1);
|
||||||
|
EXPECT_NOT_NULL(buf);
|
||||||
|
EXPECT_TRUE(receive_n_data(p[0], buf, recv_size));
|
||||||
|
buf[recv_size] = '\0';
|
||||||
|
EXPECT_EQ_INT(memcmp(buf, content, len), 0);
|
||||||
|
free(buf);
|
||||||
|
close(p[0]);
|
||||||
|
_exit(0);
|
||||||
|
} else {
|
||||||
|
close(p[0]);
|
||||||
|
bool sent = file_send_sendfile(file, p[1], false, 0, false);
|
||||||
|
close(p[1]);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
|
file_destroy(file);
|
||||||
|
unlink("test_sendfile_basic.txt");
|
||||||
|
|
||||||
|
EXPECT_TRUE(sent);
|
||||||
|
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_sendfile_with_path() {
|
||||||
|
const char* content = "Sendfile with path test";
|
||||||
|
size_t len = strlen(content);
|
||||||
|
EXPECT_TRUE(to_disk("test_sendfile_path.txt", content, len));
|
||||||
|
|
||||||
|
File* file = file_create("test_sendfile_path.txt");
|
||||||
|
EXPECT_NOT_NULL(file);
|
||||||
|
file->data->size = len;
|
||||||
|
file->data->data = malloc(len);
|
||||||
|
EXPECT_NOT_NULL(file->data->data);
|
||||||
|
memcpy(file->data->data, content, len);
|
||||||
|
|
||||||
|
int p[2];
|
||||||
|
EXPECT_EQ_INT(pipe(p), 0);
|
||||||
|
io_set_fds(p[0], p[1]);
|
||||||
|
io_set_bwlimit(0);
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
close(p[1]);
|
||||||
|
char* recv_path = receive_str(p[0]);
|
||||||
|
EXPECT_NOT_NULL(recv_path);
|
||||||
|
EXPECT_EQ_STR(recv_path, "test_sendfile_path.txt");
|
||||||
|
free(recv_path);
|
||||||
|
|
||||||
|
unsigned long long recv_size;
|
||||||
|
EXPECT_TRUE(receive_n_data(p[0], &recv_size, sizeof(recv_size)));
|
||||||
|
|
||||||
|
char* buf = malloc(recv_size + 1);
|
||||||
|
EXPECT_NOT_NULL(buf);
|
||||||
|
EXPECT_TRUE(receive_n_data(p[0], buf, recv_size));
|
||||||
|
buf[recv_size] = '\0';
|
||||||
|
EXPECT_EQ_INT(memcmp(buf, content, len), 0);
|
||||||
|
free(buf);
|
||||||
|
close(p[0]);
|
||||||
|
_exit(0);
|
||||||
|
} else {
|
||||||
|
close(p[0]);
|
||||||
|
bool sent = file_send_sendfile(file, p[1], false, 0, true);
|
||||||
|
close(p[1]);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
|
file_destroy(file);
|
||||||
|
unlink("test_sendfile_path.txt");
|
||||||
|
|
||||||
|
EXPECT_TRUE(sent);
|
||||||
|
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_sendfile_empty_file() {
|
||||||
|
const char* content = "";
|
||||||
|
size_t len = 0;
|
||||||
|
EXPECT_TRUE(to_disk("test_sendfile_empty.txt", content, len));
|
||||||
|
|
||||||
|
File* file = file_create("test_sendfile_empty.txt");
|
||||||
|
EXPECT_NOT_NULL(file);
|
||||||
|
file->data->size = len;
|
||||||
|
file->data->data = NULL;
|
||||||
|
|
||||||
|
int p[2];
|
||||||
|
EXPECT_EQ_INT(pipe(p), 0);
|
||||||
|
io_set_fds(p[0], p[1]);
|
||||||
|
io_set_bwlimit(0);
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
close(p[1]);
|
||||||
|
unsigned long long recv_size;
|
||||||
|
EXPECT_TRUE(receive_n_data(p[0], &recv_size, sizeof(recv_size)));
|
||||||
|
EXPECT_EQ_INT((int)recv_size, 0);
|
||||||
|
close(p[0]);
|
||||||
|
_exit(0);
|
||||||
|
} else {
|
||||||
|
close(p[0]);
|
||||||
|
bool sent = file_send_sendfile(file, p[1], false, 0, false);
|
||||||
|
close(p[1]);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
|
file_destroy(file);
|
||||||
|
unlink("test_sendfile_empty.txt");
|
||||||
|
|
||||||
|
EXPECT_TRUE(sent);
|
||||||
|
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_sendfile_with_compression_fallback() {
|
||||||
|
// When compression_level > 0, sendfile falls back to file_send_single_calls
|
||||||
|
const char* content = "Sendfile compression fallback test data here.";
|
||||||
|
size_t len = strlen(content);
|
||||||
|
EXPECT_TRUE(to_disk("test_sendfile_comp.txt", content, len));
|
||||||
|
|
||||||
|
File* file = file_create("test_sendfile_comp.txt");
|
||||||
|
EXPECT_NOT_NULL(file);
|
||||||
|
file->data->size = len;
|
||||||
|
file->data->data = malloc(len);
|
||||||
|
EXPECT_NOT_NULL(file->data->data);
|
||||||
|
memcpy(file->data->data, content, len);
|
||||||
|
|
||||||
|
Config* cfg = config_create(str_dup(PROTOCOL_VERSION), str_dup("/tmp"), str_dup("/tmp"), false,
|
||||||
|
false, false, false, false, 0, false, 0);
|
||||||
|
|
||||||
|
int p[2];
|
||||||
|
EXPECT_EQ_INT(pipe(p), 0);
|
||||||
|
io_set_fds(p[0], p[1]);
|
||||||
|
io_set_bwlimit(0);
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
close(p[1]);
|
||||||
|
File* received = file_receive(cfg, p[0]);
|
||||||
|
close(p[0]);
|
||||||
|
bool ok = true;
|
||||||
|
if (!received) ok = false;
|
||||||
|
else {
|
||||||
|
if (!received->data || received->data->size != len) ok = false;
|
||||||
|
else if (memcmp(received->data->data, content, len) != 0) ok = false;
|
||||||
|
}
|
||||||
|
file_destroy(received);
|
||||||
|
config_delete(cfg);
|
||||||
|
_exit(ok ? 0 : 1);
|
||||||
|
} else {
|
||||||
|
close(p[0]);
|
||||||
|
// Send with compression_level=1, should fall back to file_send_single_calls
|
||||||
|
bool sent = file_send_sendfile(file, p[1], false, 1, true);
|
||||||
|
close(p[1]);
|
||||||
|
|
||||||
|
int status;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
|
file_destroy(file);
|
||||||
|
config_delete(cfg);
|
||||||
|
unlink("test_sendfile_comp.txt");
|
||||||
|
|
||||||
|
EXPECT_TRUE(sent);
|
||||||
|
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_sendfile_nonexistent_file() {
|
||||||
|
File* file = file_create("nonexistent_file_xyz_sendfile_test.txt");
|
||||||
|
EXPECT_NOT_NULL(file);
|
||||||
|
file->data->size = 100;
|
||||||
|
file->data->data = malloc(100);
|
||||||
|
EXPECT_NOT_NULL(file->data->data);
|
||||||
|
memset(file->data->data, 0, 100);
|
||||||
|
|
||||||
|
int p[2];
|
||||||
|
EXPECT_EQ_INT(pipe(p), 0);
|
||||||
|
io_set_fds(p[0], p[1]);
|
||||||
|
io_set_bwlimit(0);
|
||||||
|
close(p[0]);
|
||||||
|
|
||||||
|
bool sent = file_send_sendfile(file, p[1], false, 0, false);
|
||||||
|
close(p[1]);
|
||||||
|
|
||||||
|
file_destroy(file);
|
||||||
|
// File doesn't exist on disk, sendfile should fail
|
||||||
|
EXPECT_FALSE(sent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_file_sendfile() {
|
||||||
|
test_sendfile_basic();
|
||||||
|
test_sendfile_with_path();
|
||||||
|
test_sendfile_empty_file();
|
||||||
|
test_sendfile_with_compression_fallback();
|
||||||
|
test_sendfile_nonexistent_file();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TEST_FILE_SENDFILE_H
|
||||||
|
#define TEST_FILE_SENDFILE_H
|
||||||
|
|
||||||
|
void test_file_sendfile();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#include "test_log.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Helper to redirect stderr temporarily
|
||||||
|
static int stderr_pipe[2];
|
||||||
|
|
||||||
|
static void capture_stderr_start() {
|
||||||
|
fflush(stderr);
|
||||||
|
EXPECT_EQ_INT(pipe(stderr_pipe), 0);
|
||||||
|
EXPECT_EQ_INT(dup2(stderr_pipe[1], STDERR_FILENO), STDERR_FILENO);
|
||||||
|
close(stderr_pipe[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void capture_stderr_end() {
|
||||||
|
fflush(stderr);
|
||||||
|
close(stderr_pipe[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_log_message_debug() {
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
// Should output at DEBUG level
|
||||||
|
log_message(LOG_LEVEL_DEBUG, "Debug message test: %d", 42);
|
||||||
|
log_message(LOG_LEVEL_INFO, "Info message test");
|
||||||
|
log_message(LOG_LEVEL_WARNING, "Warning message test");
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Error message test");
|
||||||
|
// If we get here without crashing, the test passes
|
||||||
|
EXPECT_TRUE(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_log_message_level_filtering() {
|
||||||
|
set_log_level(LOG_LEVEL_WARNING);
|
||||||
|
capture_stderr_start();
|
||||||
|
log_message(LOG_LEVEL_DEBUG, "Should NOT appear");
|
||||||
|
log_message(LOG_LEVEL_INFO, "Should NOT appear");
|
||||||
|
log_message(LOG_LEVEL_WARNING, "Should appear");
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Should appear");
|
||||||
|
capture_stderr_end();
|
||||||
|
// We can't easily check the content, but we verified it doesn't crash
|
||||||
|
EXPECT_TRUE(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_log_message_error() {
|
||||||
|
set_log_level(LOG_LEVEL_ERROR);
|
||||||
|
log_message(LOG_LEVEL_ERROR, "Error only: %s", "critical");
|
||||||
|
EXPECT_TRUE(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_set_log_level() {
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
log_message(LOG_LEVEL_DEBUG, "debug visible");
|
||||||
|
set_log_level(LOG_LEVEL_WARNING);
|
||||||
|
log_message(LOG_LEVEL_DEBUG, "debug hidden (no crash)");
|
||||||
|
log_message(LOG_LEVEL_WARNING, "warning visible");
|
||||||
|
set_log_level(LOG_LEVEL_INFO);
|
||||||
|
log_message(LOG_LEVEL_INFO, "info visible");
|
||||||
|
EXPECT_TRUE(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_log_message_various_args() {
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
log_message(LOG_LEVEL_INFO, "String: %s, Int: %d, Hex: %x", "test", 123, 0xFF);
|
||||||
|
log_message(LOG_LEVEL_WARNING, "Warning with number %d", 42);
|
||||||
|
log_message(LOG_LEVEL_DEBUG, "Debug with pointer %p", (void*)0x1234);
|
||||||
|
EXPECT_TRUE(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_log() {
|
||||||
|
test_log_message_debug();
|
||||||
|
test_log_message_level_filtering();
|
||||||
|
test_log_message_error();
|
||||||
|
test_set_log_level();
|
||||||
|
test_log_message_various_args();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TEST_LOG_H
|
||||||
|
#define TEST_LOG_H
|
||||||
|
|
||||||
|
void test_log();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#include "test_multiprocessing.h"
|
||||||
|
#include "multiprocessing.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void test_pipeline_context_sender_create_destroy() {
|
||||||
|
Config* cfg = config_create(str_dup(PROTOCOL_VERSION), str_dup("/tmp"), str_dup("/tmp"), false,
|
||||||
|
false, false, false, false, 0, false, 0);
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
|
||||||
|
Queue* q_scanner = queue_create(10, free);
|
||||||
|
EXPECT_NOT_NULL(q_scanner);
|
||||||
|
|
||||||
|
Queue* q_loader = queue_create(10, free);
|
||||||
|
EXPECT_NOT_NULL(q_loader);
|
||||||
|
|
||||||
|
PipelineContextSender* ctx = pipeline_context_sender_create(cfg, q_scanner, q_loader);
|
||||||
|
EXPECT_NOT_NULL(ctx);
|
||||||
|
EXPECT_EQ_INT(ctx->scanner_done, false);
|
||||||
|
EXPECT_EQ_INT(ctx->loader_done, false);
|
||||||
|
EXPECT_NULL(ctx->manifest);
|
||||||
|
|
||||||
|
pipeline_context_sender_destroy(ctx);
|
||||||
|
// cfg, q_scanner, q_loader are destroyed by pipeline_context_sender_destroy
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pipeline_context_sender_create_null_config() {
|
||||||
|
// Passing NULL config to pipeline_context_sender_create - it will be used
|
||||||
|
// and destroyed, so this should be tested carefully.
|
||||||
|
Queue* q_scanner = queue_create(10, free);
|
||||||
|
EXPECT_NOT_NULL(q_scanner);
|
||||||
|
Queue* q_loader = queue_create(10, free);
|
||||||
|
EXPECT_NOT_NULL(q_loader);
|
||||||
|
|
||||||
|
PipelineContextSender* ctx = pipeline_context_sender_create(NULL, q_scanner, q_loader);
|
||||||
|
EXPECT_NOT_NULL(ctx);
|
||||||
|
EXPECT_NULL(ctx->config);
|
||||||
|
|
||||||
|
// Destroy without config - config_delete(NULL) should be safe
|
||||||
|
pipeline_context_sender_destroy(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pipeline_context_sender_destroy_null() {
|
||||||
|
pipeline_context_sender_destroy(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pipeline_context_receiver_create_destroy() {
|
||||||
|
Config* cfg = config_create(str_dup(PROTOCOL_VERSION), str_dup("/tmp"), str_dup("/tmp"), false,
|
||||||
|
false, false, false, false, 0, false, 0);
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
|
||||||
|
Queue* q = queue_create(10, free);
|
||||||
|
EXPECT_NOT_NULL(q);
|
||||||
|
|
||||||
|
PipelineContextReceiver* ctx = pipeline_context_receiver_create(cfg, q, 42);
|
||||||
|
EXPECT_NOT_NULL(ctx);
|
||||||
|
EXPECT_EQ_INT(ctx->file_descriptor, 42);
|
||||||
|
EXPECT_EQ_INT(ctx->receiver_done, false);
|
||||||
|
|
||||||
|
pipeline_context_receiver_destroy(ctx);
|
||||||
|
// cfg, q are destroyed by pipeline_context_receiver_destroy
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pipeline_context_receiver_create_null_config() {
|
||||||
|
Queue* q = queue_create(10, free);
|
||||||
|
EXPECT_NOT_NULL(q);
|
||||||
|
|
||||||
|
PipelineContextReceiver* ctx = pipeline_context_receiver_create(NULL, q, -1);
|
||||||
|
EXPECT_NOT_NULL(ctx);
|
||||||
|
EXPECT_NULL(ctx->config);
|
||||||
|
EXPECT_EQ_INT(ctx->file_descriptor, -1);
|
||||||
|
|
||||||
|
pipeline_context_receiver_destroy(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pipeline_context_receiver_destroy_null() {
|
||||||
|
pipeline_context_receiver_destroy(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_multiprocessing() {
|
||||||
|
test_pipeline_context_sender_create_destroy();
|
||||||
|
test_pipeline_context_sender_create_null_config();
|
||||||
|
test_pipeline_context_sender_destroy_null();
|
||||||
|
test_pipeline_context_receiver_create_destroy();
|
||||||
|
test_pipeline_context_receiver_create_null_config();
|
||||||
|
test_pipeline_context_receiver_destroy_null();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TEST_MULTIPROCESSING_H
|
||||||
|
#define TEST_MULTIPROCESSING_H
|
||||||
|
|
||||||
|
void test_multiprocessing();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include "test_transport_ssh.h"
|
||||||
|
#include "transport_ssh.h"
|
||||||
|
#include "transport_tcp.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void test_ssh_connect_bad_destination() {
|
||||||
|
Client* client = client_connect_ssh("nosuchhost.local", 22);
|
||||||
|
// SSH connection to a non-existent host should fail (return NULL)
|
||||||
|
EXPECT_NULL(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ssh_connect_null_destination() {
|
||||||
|
Client* client = client_connect_ssh(NULL, 22);
|
||||||
|
EXPECT_NULL(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ssh_connect_bad_port() {
|
||||||
|
Client* client = client_connect_ssh("localhost", -1);
|
||||||
|
EXPECT_NULL(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ssh_connect_zero_port() {
|
||||||
|
Client* client = client_connect_ssh("localhost", 0);
|
||||||
|
EXPECT_NULL(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_transport_ssh() {
|
||||||
|
test_ssh_connect_bad_destination();
|
||||||
|
test_ssh_connect_null_destination();
|
||||||
|
test_ssh_connect_bad_port();
|
||||||
|
test_ssh_connect_zero_port();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TEST_TRANSPORT_SSH_H
|
||||||
|
#define TEST_TRANSPORT_SSH_H
|
||||||
|
|
||||||
|
void test_transport_ssh();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#include "test_transport_tcp.h"
|
||||||
|
#include "transport_tcp.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
static void test_client_create_delete() {
|
||||||
|
Client* client = client_create();
|
||||||
|
EXPECT_NOT_NULL(client);
|
||||||
|
EXPECT_EQ_INT(client->file_descriptor, -1);
|
||||||
|
EXPECT_EQ_INT(client->ssh_child_pid, -1);
|
||||||
|
client_delete(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_client_delete_null() {
|
||||||
|
client_delete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_client_disconnect_null() {
|
||||||
|
client_disconnect(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_client_connect_bad_host() {
|
||||||
|
Client* client = client_create();
|
||||||
|
EXPECT_NOT_NULL(client);
|
||||||
|
|
||||||
|
// Connecting to a non-existent host should fail
|
||||||
|
bool connected = client_connect(client, "192.0.2.999", 12345);
|
||||||
|
EXPECT_FALSE(connected);
|
||||||
|
|
||||||
|
client_disconnect(client);
|
||||||
|
client_delete(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_client_connect_bad_port() {
|
||||||
|
Client* client = client_create();
|
||||||
|
EXPECT_NOT_NULL(client);
|
||||||
|
|
||||||
|
// Connecting to port 0 should fail
|
||||||
|
bool connected = client_connect(client, "127.0.0.1", 0);
|
||||||
|
EXPECT_FALSE(connected);
|
||||||
|
|
||||||
|
client_disconnect(client);
|
||||||
|
client_delete(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_client_connect_null_host() {
|
||||||
|
Client* client = client_create();
|
||||||
|
EXPECT_NOT_NULL(client);
|
||||||
|
|
||||||
|
bool connected = client_connect(client, NULL, 8080);
|
||||||
|
EXPECT_FALSE(connected);
|
||||||
|
|
||||||
|
client_disconnect(client);
|
||||||
|
client_delete(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_server_create_delete() {
|
||||||
|
Server* server = server_create(0);
|
||||||
|
// server_create may return NULL if it fails to bind, but we need to check
|
||||||
|
// if it succeeds. Port 0 should bind to an ephemeral port.
|
||||||
|
if (server != NULL) {
|
||||||
|
EXPECT_NOT_NULL(server);
|
||||||
|
server_delete(&server);
|
||||||
|
EXPECT_NULL(server);
|
||||||
|
} else {
|
||||||
|
// On some systems port 0 may fail; that's okay
|
||||||
|
EXPECT_TRUE(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_server_delete_null() {
|
||||||
|
Server* server = NULL;
|
||||||
|
server_delete(&server);
|
||||||
|
EXPECT_NULL(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_transport_tcp() {
|
||||||
|
test_client_create_delete();
|
||||||
|
test_client_delete_null();
|
||||||
|
test_client_disconnect_null();
|
||||||
|
test_client_connect_bad_host();
|
||||||
|
test_client_connect_bad_port();
|
||||||
|
test_client_connect_null_host();
|
||||||
|
test_server_create_delete();
|
||||||
|
test_server_delete_null();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TEST_TRANSPORT_TCP_H
|
||||||
|
#define TEST_TRANSPORT_TCP_H
|
||||||
|
|
||||||
|
void test_transport_tcp();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#include "test_transport_tls.h"
|
||||||
|
#include "transport_tls.h"
|
||||||
|
#include "transport_tcp.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void test_tls_global_init() {
|
||||||
|
bool ok = tls_global_init();
|
||||||
|
// Should succeed in normal environments with OpenSSL
|
||||||
|
// May fail in minimal environments, but we just test it doesn't crash
|
||||||
|
EXPECT_TRUE(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_client_connect_tls_bad_host() {
|
||||||
|
// Ensure TLS is initialized
|
||||||
|
tls_global_init();
|
||||||
|
|
||||||
|
Client* client = client_create();
|
||||||
|
EXPECT_NOT_NULL(client);
|
||||||
|
|
||||||
|
// Connecting without TLS setup should fail
|
||||||
|
bool connected = client_connect_tls(client, "192.0.2.1", 443, NULL, NULL, NULL);
|
||||||
|
EXPECT_FALSE(connected);
|
||||||
|
|
||||||
|
client_disconnect(client);
|
||||||
|
client_delete(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_client_connect_tls_null_params() {
|
||||||
|
tls_global_init();
|
||||||
|
|
||||||
|
Client* client = client_create();
|
||||||
|
EXPECT_NOT_NULL(client);
|
||||||
|
|
||||||
|
bool connected = client_connect_tls(client, NULL, 0, NULL, NULL, NULL);
|
||||||
|
EXPECT_FALSE(connected);
|
||||||
|
|
||||||
|
client_disconnect(client);
|
||||||
|
client_delete(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_server_create_tls_no_cert() {
|
||||||
|
Server* server = server_create(0);
|
||||||
|
if (server != NULL) {
|
||||||
|
// Trying to set up TLS without cert/key files should fail
|
||||||
|
bool ok = server_create_tls(server, "/nonexistent/cert.pem", "/nonexistent/key.pem",
|
||||||
|
"/nonexistent/ca.pem");
|
||||||
|
EXPECT_FALSE(ok);
|
||||||
|
server_delete(&server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_server_create_tls_null_files() {
|
||||||
|
Server* server = server_create(0);
|
||||||
|
if (server != NULL) {
|
||||||
|
bool ok = server_create_tls(server, NULL, NULL, NULL);
|
||||||
|
EXPECT_FALSE(ok);
|
||||||
|
server_delete(&server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_transport_tls() {
|
||||||
|
test_tls_global_init();
|
||||||
|
test_client_connect_tls_bad_host();
|
||||||
|
test_client_connect_tls_null_params();
|
||||||
|
test_server_create_tls_no_cert();
|
||||||
|
test_server_create_tls_null_files();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef TEST_TRANSPORT_TLS_H
|
||||||
|
#define TEST_TRANSPORT_TLS_H
|
||||||
|
|
||||||
|
void test_transport_tls();
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user