Merge fix/quality-cleanup into dev
fixes #260 (dead code, CLI =form/missing-arg diagnostics)
This commit is contained in:
+92
-22
@@ -58,7 +58,7 @@ static bool parse_positive_int(const char* s, int* out_val) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Duplicate a string argument into *dest, freeing the old value. Returns true on success, false on
|
/* Duplicate a string argument into *dest, freeing the old value. Returns 0 on success, -1 on
|
||||||
* failure. */
|
* failure. */
|
||||||
static int set_string_option(char** dest, const char* value, const char* option_name) {
|
static int set_string_option(char** dest, const char* value, const char* option_name) {
|
||||||
char* dup = str_dup(value);
|
char* dup = str_dup(value);
|
||||||
@@ -71,7 +71,7 @@ static int set_string_option(char** dest, const char* value, const char* option_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a string as a positive integer into *dest. Returns true on success, false on error. */
|
/* Parse a string as a positive integer into *dest. Returns 0 on success, -1 on error. */
|
||||||
static int set_positive_int_option(int* dest, const char* value, const char* option_name) {
|
static int set_positive_int_option(int* dest, const char* value, const char* option_name) {
|
||||||
if (!parse_positive_int(value, dest)) {
|
if (!parse_positive_int(value, dest)) {
|
||||||
log_message(LOG_LEVEL_ERROR, "%s must be a positive integer", option_name);
|
log_message(LOG_LEVEL_ERROR, "%s must be a positive integer", option_name);
|
||||||
@@ -103,7 +103,7 @@ static int set_compression_threads_option(int* dest, const char* value) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a string as a non-negative integer into *dest. Returns true on success, false on error. */
|
/* Parse a string as a non-negative integer into *dest. Returns 0 on success, -1 on error. */
|
||||||
static int set_nonneg_int_option(int* dest, const char* value, const char* option_name) {
|
static int set_nonneg_int_option(int* dest, const char* value, const char* option_name) {
|
||||||
if (!parse_nonneg_int(value, dest)) {
|
if (!parse_nonneg_int(value, dest)) {
|
||||||
log_message(LOG_LEVEL_ERROR, "%s must be a non-negative integer", option_name);
|
log_message(LOG_LEVEL_ERROR, "%s must be a non-negative integer", option_name);
|
||||||
@@ -238,7 +238,10 @@ static int parse_ull_arg(const char* val, unsigned long long* out, const char* o
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_size_arg(const char* value, unsigned long long* out) {
|
/* Parse a byte count with an optional single-letter binary suffix (K/M/G/T/P/E).
|
||||||
|
* When allow_zero is false, a bare 0 is rejected (size limits use true, since 0
|
||||||
|
* means "no limit"). Returns 0 on success, -1 on error. */
|
||||||
|
static int parse_size_arg_allow_zero(const char* value, unsigned long long* out, bool allow_zero) {
|
||||||
if (!value || *value < '0' || *value > '9')
|
if (!value || *value < '0' || *value > '9')
|
||||||
return -1;
|
return -1;
|
||||||
char* end;
|
char* end;
|
||||||
@@ -282,12 +285,16 @@ static int parse_size_arg(const char* value, unsigned long long* out) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (number == 0 || number > ULLONG_MAX / multiplier)
|
if ((!allow_zero && number == 0) || number > ULLONG_MAX / multiplier)
|
||||||
return -1;
|
return -1;
|
||||||
*out = number * multiplier;
|
*out = number * multiplier;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_size_arg(const char* value, unsigned long long* out) {
|
||||||
|
return parse_size_arg_allow_zero(value, out, false);
|
||||||
|
}
|
||||||
|
|
||||||
/* Append a duplicated pattern to a growable pattern array. Returns 0 on success, -1 on error. */
|
/* Append a duplicated pattern to a growable pattern array. Returns 0 on success, -1 on error. */
|
||||||
static int config_add_pattern(char*** patterns, int* count, const char* value,
|
static int config_add_pattern(char*** patterns, int* count, const char* value,
|
||||||
const char* optname) {
|
const char* optname) {
|
||||||
@@ -451,6 +458,8 @@ static const OptionEntry* find_table_option(const char* arg) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Match a "--opt=value" argument against table options that take a value. Flags,
|
||||||
|
* no-ops, and unsupported options do not accept an inline "=" value. */
|
||||||
static const OptionEntry* find_table_option_with_equals(const char* arg, const char** value) {
|
static const OptionEntry* find_table_option_with_equals(const char* arg, const char** value) {
|
||||||
const char* equals = strchr(arg, '=');
|
const char* equals = strchr(arg, '=');
|
||||||
if (!equals || equals == arg)
|
if (!equals || equals == arg)
|
||||||
@@ -461,8 +470,8 @@ static const OptionEntry* find_table_option_with_equals(const char* arg, const c
|
|||||||
if ((strlen(entry->name) == name_len && strncmp(arg, entry->name, name_len) == 0) ||
|
if ((strlen(entry->name) == name_len && strncmp(arg, entry->name, name_len) == 0) ||
|
||||||
(entry->alias && strlen(entry->alias) == name_len &&
|
(entry->alias && strlen(entry->alias) == name_len &&
|
||||||
strncmp(arg, entry->alias, name_len) == 0)) {
|
strncmp(arg, entry->alias, name_len) == 0)) {
|
||||||
if (strcmp(entry->name, "--compress-choice") == 0 ||
|
if (entry->kind == OPT_STRING || entry->kind == OPT_POS_INT ||
|
||||||
strcmp(entry->name, "--compress-level") == 0) {
|
entry->kind == OPT_NONNEG_INT || entry->kind == OPT_ULL) {
|
||||||
*value = equals + 1;
|
*value = equals + 1;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@@ -517,8 +526,13 @@ static int apply_table_option(Config* config, const OptionEntry* entry, const ch
|
|||||||
return set_nonneg_int_option((int*)field, value, entry->name);
|
return set_nonneg_int_option((int*)field, value, entry->name);
|
||||||
case OPT_ULL: {
|
case OPT_ULL: {
|
||||||
unsigned long long v;
|
unsigned long long v;
|
||||||
if (parse_ull_arg(value, &v, entry->name) != 0)
|
/* Size-limit options accept rsync-style suffixes (e.g. --max-size=2G); a
|
||||||
|
* plain byte count, including 0 ("no limit"), stays valid. */
|
||||||
|
if (parse_size_arg_allow_zero(value, &v, true) != 0) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "%s must be a non-negative size (B, K, M, G, T, P, or E)",
|
||||||
|
entry->name);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
*(unsigned long long*)field = v;
|
*(unsigned long long*)field = v;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -668,22 +682,38 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
config->use_multithreading = true;
|
config->use_multithreading = true;
|
||||||
config->use_metadata = true;
|
config->use_metadata = true;
|
||||||
log_info_message(LOG_INFO_MISC, "Enabled archive mode (-c -m -M)");
|
log_info_message(LOG_INFO_MISC, "Enabled archive mode (-c -m -M)");
|
||||||
} else if (opt_is(argv[i], "-p", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "-p", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (set_positive_int_option(&config->ssh_port, argv[++i], "-p") != 0)
|
if (set_positive_int_option(&config->ssh_port, argv[++i], "-p") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (config->ssh_port > 65535) {
|
if (config->ssh_port > 65535) {
|
||||||
log_message(LOG_LEVEL_ERROR, "SSH port must be 1-65535");
|
log_message(LOG_LEVEL_ERROR, "SSH port must be 1-65535");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (opt_is(argv[i], "--exclude", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--exclude", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (config_add_pattern(&config->exclude_patterns, &config->exclude_count, argv[++i],
|
if (config_add_pattern(&config->exclude_patterns, &config->exclude_count, argv[++i],
|
||||||
"--exclude") != 0)
|
"--exclude") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "--include", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--include", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (config_add_pattern(&config->include_patterns, &config->include_count, argv[++i],
|
if (config_add_pattern(&config->include_patterns, &config->include_count, argv[++i],
|
||||||
"--include") != 0)
|
"--include") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "--delta-block", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--delta-block", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
unsigned long long val;
|
unsigned long long val;
|
||||||
if (parse_ull_arg(argv[++i], &val, "--delta-block") != 0)
|
if (parse_ull_arg(argv[++i], &val, "--delta-block") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -691,7 +721,11 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
config->delta_block_size = (uint32_t)val;
|
config->delta_block_size = (uint32_t)val;
|
||||||
else
|
else
|
||||||
log_message(LOG_LEVEL_WARNING, "--delta-block value %llu out of range, using default", val);
|
log_message(LOG_LEVEL_WARNING, "--delta-block value %llu out of range, using default", val);
|
||||||
} else if (opt_is(argv[i], "--delta-max", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--delta-max", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
unsigned long long val;
|
unsigned long long val;
|
||||||
if (parse_ull_arg(argv[++i], &val, "--delta-max") != 0)
|
if (parse_ull_arg(argv[++i], &val, "--delta-max") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -732,7 +766,11 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
} else if (opt_is(argv[i], "-s", NULL)) {
|
} else if (opt_is(argv[i], "-s", NULL)) {
|
||||||
config->use_chunk_serialization = true;
|
config->use_chunk_serialization = true;
|
||||||
log_info_message(LOG_INFO_MISC, "Enabled Chunk Serialization");
|
log_info_message(LOG_INFO_MISC, "Enabled Chunk Serialization");
|
||||||
} else if (opt_is(argv[i], "--server-port", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--server-port", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (!parse_positive_int(argv[++i], &config->server_port)) {
|
if (!parse_positive_int(argv[++i], &config->server_port)) {
|
||||||
char* escaped = output_escape(argv[i], false);
|
char* escaped = output_escape(argv[i], false);
|
||||||
log_message(LOG_LEVEL_ERROR, "invalid --server-port value: %s",
|
log_message(LOG_LEVEL_ERROR, "invalid --server-port value: %s",
|
||||||
@@ -744,7 +782,11 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
log_message(LOG_LEVEL_ERROR, "server port must be 1-65535");
|
log_message(LOG_LEVEL_ERROR, "server port must be 1-65535");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (opt_is(argv[i], "--bwlimit", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--bwlimit", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
unsigned long long kbps;
|
unsigned long long kbps;
|
||||||
if (parse_ull_arg(argv[++i], &kbps, "--bwlimit") != 0)
|
if (parse_ull_arg(argv[++i], &kbps, "--bwlimit") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -758,7 +800,11 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
}
|
}
|
||||||
io_set_bwlimit(kbps * 1024);
|
io_set_bwlimit(kbps * 1024);
|
||||||
log_info_message(LOG_INFO_MISC, "Set bandwidth limit to %llu KB/s", kbps);
|
log_info_message(LOG_INFO_MISC, "Set bandwidth limit to %llu KB/s", kbps);
|
||||||
} else if (opt_is(argv[i], "--chunk-size", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--chunk-size", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
unsigned long long val;
|
unsigned long long val;
|
||||||
if (parse_ull_arg(argv[++i], &val, "--chunk-size") != 0)
|
if (parse_ull_arg(argv[++i], &val, "--chunk-size") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -767,7 +813,11 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
config->chunk_size = val;
|
config->chunk_size = val;
|
||||||
} else if (opt_is(argv[i], "--log-file", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--log-file", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (config->log_file) {
|
if (config->log_file) {
|
||||||
fclose(config->log_file);
|
fclose(config->log_file);
|
||||||
config->log_file = NULL;
|
config->log_file = NULL;
|
||||||
@@ -789,11 +839,19 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
} else if (opt_is(argv[i], "--stderr", NULL)) {
|
} else if (opt_is(argv[i], "--stderr", NULL)) {
|
||||||
if (i + 1 >= argc || set_stderr_mode(argv[++i]) != 0)
|
if (i + 1 >= argc || set_stderr_mode(argv[++i]) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "--exclude-from", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--exclude-from", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (read_patterns_from_file(argv[++i], &config->exclude_patterns, &config->exclude_count) !=
|
if (read_patterns_from_file(argv[++i], &config->exclude_patterns, &config->exclude_count) !=
|
||||||
0)
|
0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "--include-from", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--include-from", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (read_patterns_from_file(argv[++i], &config->include_patterns, &config->include_count) !=
|
if (read_patterns_from_file(argv[++i], &config->include_patterns, &config->include_count) !=
|
||||||
0)
|
0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -818,16 +876,28 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
|
|||||||
} else if (opt_is(argv[i], "--info", NULL)) {
|
} else if (opt_is(argv[i], "--info", NULL)) {
|
||||||
if (i + 1 >= argc || parse_info_flags(argv[++i], config) != 0)
|
if (i + 1 >= argc || parse_info_flags(argv[++i], config) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "-T", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "-T", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (set_positive_int_option(&config->timeout, argv[++i], "-T") != 0)
|
if (set_positive_int_option(&config->timeout, argv[++i], "-T") != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (strncmp(argv[i], "--skip-compress=", 16) == 0) {
|
} else if (strncmp(argv[i], "--skip-compress=", 16) == 0) {
|
||||||
if (parse_skip_compress(config, argv[i] + 16) != 0)
|
if (parse_skip_compress(config, argv[i] + 16) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "--skip-compress", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--skip-compress", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (parse_skip_compress(config, argv[++i]) != 0)
|
if (parse_skip_compress(config, argv[++i]) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "--compress-threads", NULL) && i + 1 < argc) {
|
} else if (opt_is(argv[i], "--compress-threads", NULL)) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (set_compression_threads_option(&config->compression_threads, argv[++i]) != 0)
|
if (set_compression_threads_option(&config->compression_threads, argv[++i]) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (opt_is(argv[i], "--checksum-choice", "--cc")) {
|
} else if (opt_is(argv[i], "--checksum-choice", "--cc")) {
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "chunk.h"
|
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "metadata.h"
|
|
||||||
#include "multiprocessing.h"
|
#include "multiprocessing.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "receiver.h"
|
#include "receiver.h"
|
||||||
#include "transport_tcp.h"
|
#include "transport_tcp.h"
|
||||||
#include "transport_tls.h"
|
#include "transport_tls.h"
|
||||||
#include "unistd.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@@ -17,7 +14,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
|
||||||
@@ -109,140 +105,6 @@ static bool __attribute__((unused)) configure_authorization(const char* root) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int receive_files(Config* config, int fd) {
|
|
||||||
Status status;
|
|
||||||
if (!receive_status(fd, &status))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
while (status == STATUS_NEXT || status == STATUS_CHUNK || status == STATUS_CHECK ||
|
|
||||||
status == STATUS_KEEPALIVE || status == STATUS_ABORT || status == STATUS_CHECK_BATCH) {
|
|
||||||
if (status == STATUS_KEEPALIVE) {
|
|
||||||
send_status(fd, STATUS_KEEPALIVE);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
if (status == STATUS_ABORT) {
|
|
||||||
log_message(LOG_LEVEL_INFO, "Received abort from client, cleaning up");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (status == STATUS_CHECK) {
|
|
||||||
bool skipped;
|
|
||||||
File* file = receive_incremental_check(fd, config, &skipped);
|
|
||||||
if (skipped)
|
|
||||||
goto next;
|
|
||||||
if (file == NULL && !skipped)
|
|
||||||
return -1;
|
|
||||||
if (config->save_to_disk &&
|
|
||||||
!file_save_to_disk(config->receive_root_directory, file, config)) {
|
|
||||||
file_destroy(file);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
file_destroy(file);
|
|
||||||
} else if (status == STATUS_CHUNK) {
|
|
||||||
Chunk* chunk = receive_chunk_data(fd, config);
|
|
||||||
if (chunk == NULL) {
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < chunk->element_count; i++) {
|
|
||||||
if (config->save_to_disk &&
|
|
||||||
!file_save_to_disk(config->receive_root_directory, chunk->items[i], config)) {
|
|
||||||
chunk_destroy(chunk);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chunk_destroy(chunk);
|
|
||||||
} else if (status == STATUS_CHECK_BATCH) {
|
|
||||||
int count;
|
|
||||||
/* Batch framing has no checksum field yet; never silently downgrade a
|
|
||||||
checksum-enabled transfer into mtime-only matching. */
|
|
||||||
if (config->checksum || !receive_int(fd, &count) || count < 0 || count > MAX_MANIFEST_ENTRIES)
|
|
||||||
return -1;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
char* check_path = receive_str(fd);
|
|
||||||
if (!check_path)
|
|
||||||
return -1;
|
|
||||||
unsigned long long check_size;
|
|
||||||
long long check_mtime;
|
|
||||||
if (!receive_n_data(fd, &check_size, sizeof(check_size)) ||
|
|
||||||
!receive_n_data(fd, &check_mtime, sizeof(check_mtime))) {
|
|
||||||
free(check_path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
long long check_mtime_nsec;
|
|
||||||
if (!receive_n_data(fd, &check_mtime_nsec, sizeof(check_mtime_nsec)) ||
|
|
||||||
check_mtime_nsec < 0 || check_mtime_nsec >= 1000000000LL) {
|
|
||||||
free(check_path);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!utils_valid_batch_path(check_path)) {
|
|
||||||
free(check_path);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
struct stat st;
|
|
||||||
char* full_path = path_cat(config->receive_root_directory, check_path);
|
|
||||||
if (!full_path) {
|
|
||||||
free(check_path);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
bool has_old = full_path && file_stat_secure(full_path, &st);
|
|
||||||
long long old_mtime_nsec = 0;
|
|
||||||
if (has_old) {
|
|
||||||
#ifdef __linux__
|
|
||||||
old_mtime_nsec = st.st_mtim.tv_nsec;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
bool match = !config->ignore_times && has_old &&
|
|
||||||
(unsigned long long)st.st_size == check_size &&
|
|
||||||
metadata_mtime_matches(st.st_mtime, old_mtime_nsec, (time_t)check_mtime,
|
|
||||||
(long)check_mtime_nsec, config->modify_window);
|
|
||||||
bool sent = send_status(fd, match ? STATUS_OK : STATUS_NEXT);
|
|
||||||
free(full_path);
|
|
||||||
free(check_path);
|
|
||||||
if (!sent)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
goto next;
|
|
||||||
} else {
|
|
||||||
File* file = file_receive(config, fd);
|
|
||||||
if (file == NULL) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to receive file");
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (config->save_to_disk &&
|
|
||||||
!file_save_to_disk(config->receive_root_directory, file, config)) {
|
|
||||||
file_destroy(file);
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
file_destroy(file);
|
|
||||||
}
|
|
||||||
next:
|
|
||||||
if (!receive_status(fd, &status)) {
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == STATUS_MANIFEST) {
|
|
||||||
if (receive_manifest(fd, config, &status) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (status != STATUS_FINISHED) {
|
|
||||||
log_message(LOG_LEVEL_ERROR, "Did not receive FINISHED Status");
|
|
||||||
send_status(fd, STATUS_ERROR);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
send_status(fd, STATUS_OK);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handler(int file_descriptor) {
|
void handler(int file_descriptor) {
|
||||||
SSL* ssl = io_get_ssl();
|
SSL* ssl = io_get_ssl();
|
||||||
ProtocolSession session;
|
ProtocolSession session;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "array_list.h"
|
#include "array_list.h"
|
||||||
#include "libgen.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -83,45 +82,6 @@ static int open_authorized_destination(const char* dest_root) {
|
|||||||
return dirfd;
|
return dirfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mkdir_r(const char* path) {
|
|
||||||
if (!path || *path == '\0')
|
|
||||||
return false;
|
|
||||||
char* duplicate = str_dup(path);
|
|
||||||
if (!duplicate)
|
|
||||||
return false;
|
|
||||||
int dirfd = open(path[0] == '/' ? "/" : ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
|
|
||||||
if (dirfd < 0) {
|
|
||||||
free(duplicate);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool ok = true;
|
|
||||||
char* saveptr = NULL;
|
|
||||||
char* component = strtok_r(duplicate, "/", &saveptr);
|
|
||||||
while (component) {
|
|
||||||
if (strcmp(component, "..") == 0) {
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (strcmp(component, ".") != 0) {
|
|
||||||
int next = openat(dirfd, component, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
|
|
||||||
if (next < 0 && errno == ENOENT) {
|
|
||||||
if (mkdirat(dirfd, component, 0755) == 0 || errno == EEXIST)
|
|
||||||
next = openat(dirfd, component, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
|
|
||||||
}
|
|
||||||
if (next < 0) {
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
close(dirfd);
|
|
||||||
dirfd = next;
|
|
||||||
}
|
|
||||||
component = strtok_r(NULL, "/", &saveptr);
|
|
||||||
}
|
|
||||||
close(dirfd);
|
|
||||||
free(duplicate);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* str_dup(const char* string) {
|
char* str_dup(const char* string) {
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
bool mkdir_r(const char* path);
|
|
||||||
char* str_dup(const char* string);
|
char* str_dup(const char* string);
|
||||||
char* output_escape(const char* string, bool eight_bit_output);
|
char* output_escape(const char* string, bool eight_bit_output);
|
||||||
char* path_cat(const char* path1, const char* path2);
|
char* path_cat(const char* path1, const char* path2);
|
||||||
|
|||||||
@@ -1088,6 +1088,70 @@ static void test_parse_args_rejects_invalid_compression_choice() {
|
|||||||
config_delete(cfg);
|
config_delete(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Every value-taking table option accepts an inline "--opt=value" form. */
|
||||||
|
static void test_parse_args_table_equals_size_options() {
|
||||||
|
Config* cfg = config_create();
|
||||||
|
char* argv[] = {"fastsync", "--max-size=2G", "--min-size=1K", "/src", "/dst"};
|
||||||
|
int positional_args[2];
|
||||||
|
int positional_count = 0;
|
||||||
|
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), 0);
|
||||||
|
EXPECT_TRUE(cfg->max_size == 2ULL * 1024 * 1024 * 1024);
|
||||||
|
EXPECT_TRUE(cfg->min_size == 1024ULL);
|
||||||
|
EXPECT_EQ_INT(positional_count, 2);
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_parse_args_table_equals_string_and_int_options() {
|
||||||
|
Config* cfg = config_create();
|
||||||
|
char* argv[] = {"fastsync", "--suffix=.bak", "--timeout=30", "--max-depth=5", "/src", "/dst"};
|
||||||
|
int positional_args[2];
|
||||||
|
int positional_count = 0;
|
||||||
|
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 6, argv, positional_args, &positional_count), 0);
|
||||||
|
EXPECT_EQ_STR(cfg->suffix, ".bak");
|
||||||
|
EXPECT_EQ_INT(cfg->timeout, 30);
|
||||||
|
EXPECT_EQ_INT(cfg->max_depth, 5);
|
||||||
|
EXPECT_EQ_INT(positional_count, 2);
|
||||||
|
config_delete(cfg);
|
||||||
|
|
||||||
|
cfg = config_create();
|
||||||
|
char* backup_argv[] = {"fastsync", "--backup-dir=/tmp/bak", "/src", "/dst"};
|
||||||
|
positional_count = 0;
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 4, backup_argv, positional_args, &positional_count), 0);
|
||||||
|
EXPECT_EQ_STR(cfg->backup_dir, "/tmp/bak");
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Options that take a separate value must report "missing argument", not the
|
||||||
|
* generic "Unknown option", when they are the final argv entry. */
|
||||||
|
static void test_parse_args_missing_argument_diagnostic() {
|
||||||
|
static const char* const options[] = {"--exclude", "--server-port", "--skip-compress", "-T"};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||||
|
Config* cfg = config_create();
|
||||||
|
char* argv[] = {"fastsync", (char*)options[i]};
|
||||||
|
int positional_args[2];
|
||||||
|
int positional_count = 0;
|
||||||
|
FILE* log_file = tmpfile();
|
||||||
|
char log_buffer[512] = {0};
|
||||||
|
|
||||||
|
EXPECT_NOT_NULL(log_file);
|
||||||
|
log_set_file(log_file);
|
||||||
|
|
||||||
|
EXPECT_EQ_INT(parse_args(cfg, 2, argv, positional_args, &positional_count), -1);
|
||||||
|
fflush(log_file);
|
||||||
|
rewind(log_file);
|
||||||
|
EXPECT_TRUE(fread(log_buffer, 1, sizeof(log_buffer) - 1, log_file) > 0);
|
||||||
|
EXPECT_TRUE(strstr(log_buffer, "missing argument") != NULL);
|
||||||
|
EXPECT_TRUE(strstr(log_buffer, "Unknown option") == NULL);
|
||||||
|
|
||||||
|
log_set_file(NULL);
|
||||||
|
fclose(log_file);
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void test_client_cli() {
|
void test_client_cli() {
|
||||||
test_validate_config_required_paths();
|
test_validate_config_required_paths();
|
||||||
test_validate_config_incompatible_options();
|
test_validate_config_incompatible_options();
|
||||||
@@ -1155,6 +1219,9 @@ void test_client_cli() {
|
|||||||
test_parse_args_compression_alias_equals();
|
test_parse_args_compression_alias_equals();
|
||||||
test_parse_args_rejects_invalid_compression_level_equals();
|
test_parse_args_rejects_invalid_compression_level_equals();
|
||||||
test_parse_args_rejects_invalid_compression_choice();
|
test_parse_args_rejects_invalid_compression_choice();
|
||||||
|
test_parse_args_table_equals_size_options();
|
||||||
|
test_parse_args_table_equals_string_and_int_options();
|
||||||
|
test_parse_args_missing_argument_diagnostic();
|
||||||
test_parse_args_partial_progress();
|
test_parse_args_partial_progress();
|
||||||
test_parse_args_checksum_choice_aliases();
|
test_parse_args_checksum_choice_aliases();
|
||||||
test_parse_args_checksum_choice_requires_value();
|
test_parse_args_checksum_choice_requires_value();
|
||||||
|
|||||||
Reference in New Issue
Block a user