Add LZ4 compression support for TCP transfers

- Add bundled LZ4 library (lz4.h, lz4.c)
- Add conn_options_t for connection-level mode + compression negotiation
- Add COMPRESS_NONE and COMPRESS_LZ4 constants
- Extend file_meta_t with compress and compressed_size fields
- Client: compress files before sending when compression enabled
- Server: decompress received files when compression flag is set
- Add -c flag to DISABLE compression (default: ON)
- Update Makefile to compile lz4.o
- Compression falls back to uncompressed if LZ4 doesn't reduce size

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
taptap
2026-06-24 23:31:31 +02:00
parent 531c26803f
commit 954528a6c0
6 changed files with 3976 additions and 65 deletions
+97 -37
View File
@@ -1,4 +1,5 @@
#include "common.h"
#include "lz4.h"
#include <getopt.h>
#include <poll.h>
#include <time.h>
@@ -335,15 +336,17 @@ static void *client_thread(void *arg) {
void handle_client(int sock_fd, const char *dest_dir) {
printf("Client connected. Starting transfer session...\n");
// Read mode (first 4 bytes from client)
uint32_t mode = MODE_SYNC; // Default to sync mode
ssize_t n = readn(sock_fd, &mode, sizeof(mode));
if (n != sizeof(mode)) {
fprintf(stderr, "Failed to read mode from client\n");
// Read connection options (mode + compression)
conn_options_t opts;
ssize_t n = readn(sock_fd, &opts, sizeof(opts));
if (n != sizeof(opts)) {
fprintf(stderr, "Failed to read connection options from client\n");
return;
}
int use_raw = (mode == MODE_RAW);
printf("Mode: %s\n", use_raw ? "RAW" : "SYNC");
int use_raw = (opts.mode == MODE_RAW);
int use_compress = opts.compress; // COMPRESS_LZ4 or COMPRESS_NONE
printf("Mode: %s, Compression: %s\n", use_raw ? "RAW" : "SYNC",
use_compress ? "LZ4" : "none");
int use_udp = 0;
int udp_fd = -1;
@@ -464,33 +467,91 @@ void handle_client(int sock_fd, const char *dest_dir) {
continue;
}
printf("Receiving (pipelined): %s (%lu bytes)\n", filename, (unsigned long)meta.file_size);
printf("Receiving (pipelined%s): %s (%lu bytes)\n",
meta.compress ? "+compress" : "", filename, (unsigned long)meta.file_size);
// Read file data
char buffer[65536];
uint64_t bytes_left = meta.file_size;
int write_error = 0;
while (bytes_left > 0) {
size_t to_read = (bytes_left > sizeof(buffer)) ? sizeof(buffer) : bytes_left;
ssize_t nread = read(sock_fd, buffer, to_read);
if (nread < 0) {
if (errno == EINTR) continue;
perror("Socket read error");
if (meta.compress == COMPRESS_LZ4 && meta.compressed_size > 0) {
// Receive compressed data
char *comp_buf = malloc(meta.compressed_size);
if (!comp_buf) {
perror("malloc comp_buf");
write_error = 1;
break;
}
if (nread == 0) {
fprintf(stderr, "Unexpected socket EOF\n");
write_error = 1;
break;
}
if (!write_error) {
if (writen(out_fd, buffer, nread) != nread) {
perror("File write error");
write_error = 1;
} else {
// Read all compressed data
size_t total_read = 0;
while (total_read < meta.compressed_size && !write_error) {
size_t to_read = (meta.compressed_size - total_read > sizeof(buffer))
? sizeof(buffer) : meta.compressed_size - total_read;
ssize_t nread = read(sock_fd, buffer, to_read);
if (nread < 0) {
if (errno == EINTR) continue;
perror("Socket read error (compressed)");
write_error = 1;
break;
}
if (nread == 0) {
fprintf(stderr, "Unexpected socket EOF (compressed)\n");
write_error = 1;
break;
}
memcpy(comp_buf + total_read, buffer, nread);
total_read += nread;
}
if (!write_error && total_read == meta.compressed_size) {
// Decompress
char *decomp_buf = malloc(meta.file_size);
if (!decomp_buf) {
perror("malloc decomp_buf");
write_error = 1;
} else {
int decompressed = LZ4_decompress_safe(comp_buf, decomp_buf,
meta.compressed_size, meta.file_size);
if (decompressed != (int)meta.file_size) {
fprintf(stderr, "Decompression failed: expected %lu, got %d\n",
(unsigned long)meta.file_size, decompressed);
write_error = 1;
} else {
// Write decompressed data
if (writen(out_fd, decomp_buf, meta.file_size) != (ssize_t)meta.file_size) {
perror("File write error (decompressed)");
write_error = 1;
}
}
free(decomp_buf);
}
}
free(comp_buf);
}
} else {
// No compression: read and write directly
uint64_t bytes_left = meta.file_size;
while (bytes_left > 0 && !write_error) {
size_t to_read = (bytes_left > sizeof(buffer)) ? sizeof(buffer) : bytes_left;
ssize_t nread = read(sock_fd, buffer, to_read);
if (nread < 0) {
if (errno == EINTR) continue;
perror("Socket read error");
write_error = 1;
break;
}
if (nread == 0) {
fprintf(stderr, "Unexpected socket EOF\n");
write_error = 1;
break;
}
if (!write_error) {
if (writen(out_fd, buffer, nread) != nread) {
perror("File write error");
write_error = 1;
}
}
bytes_left -= nread;
}
bytes_left -= nread;
}
if (!write_error) {
fsync(out_fd); // Ensure data is flushed to disk
@@ -514,17 +575,16 @@ void handle_client(int sock_fd, const char *dest_dir) {
// We received a file metadata packet (read rest of it)
uint32_t name_len = 0;
uint64_t file_size = 0;
uint32_t mode = 0;
if (readn(sock_fd, &name_len, sizeof(name_len)) != sizeof(name_len) ||
readn(sock_fd, &file_size, sizeof(file_size)) != sizeof(file_size) ||
readn(sock_fd, &mode, sizeof(mode)) != sizeof(mode)) {
fprintf(stderr, "Failed to read file metadata fields\n");
file_meta_t meta;
if (readn(sock_fd, &meta, sizeof(meta)) != sizeof(meta)) {
fprintf(stderr, "Failed to read file metadata\n");
break;
}
uint32_t name_len = meta.name_len;
uint64_t file_size = meta.file_size;
int file_mode = meta.mode;
// Read filename
char filename[1024];
if (name_len >= sizeof(filename)) {
@@ -571,7 +631,7 @@ void handle_client(int sock_fd, const char *dest_dir) {
active.received_blocks = calloc(active.total_blocks, 1);
snprintf(active.target_path, sizeof(active.target_path), "%s", target_path);
active.out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
active.out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
if (active.out_fd < 0) {
perror("Failed to open target file for UDP sync");
free(active.received_blocks);
@@ -605,7 +665,7 @@ void handle_client(int sock_fd, const char *dest_dir) {
break;
}
int out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
int out_fd = open(target_path, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
if (out_fd < 0) {
perror("Failed to open target file for TCP sync");
break;