fix(parity): receiver temp-dir confinement, server I/O floor, delete budget
Address review findings on feat/rsync-parity: - confine --temp-dir below the receive root (reject absolute/.. like backup-dir/partial-dir); keep EXDEV non-atomic fallback - floor server session I/O deadlines at SERVER_IO_TIMEOUT_SEC (60s) and install it on the socket layer at startup (slow-loris) - charge each --delete-missing-args directory removal once and clamp the extras-walk remaining budget so it can never underflow past --max-delete - normalize --compress-choice=auto to zstd client-side and accept it on receive so auto transfers no longer fail - map received --max-alloc=0 to MAX_SERVER_ALLOC (receive path only) - zero File.dest_state; include log-file-format in report_dest_info; add STATUS_DELETE_LIMIT name; recognize --skip-compress as a separate-value option; OOM-guard send_list_only root entry; drop the dead -M= branch; record the bare relative protected prefix for -R size-prunes in both scanners; refresh delete-manifest comment - pin the rsync tarball sha256 and bump integrator image to v11 Tests: temp-dir rejection/relative/cross-device, server timeout floor, delete-missing dir budget regression, compress-choice=auto e2e, max-alloc=0 receive mapping, dest_state, report_dest_info modes, skip-compress dash value, -M short forms, -R root size-prune mirror protection (rsync 3.4.1 confirmed).
This commit is contained in:
@@ -1518,6 +1518,9 @@ static void test_parse_args_compress_choice_parity() {
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), 0);
|
||||
/* "auto" is normalized to the canonical "zstd" the receiver accepts. */
|
||||
EXPECT_EQ_STR(cfg->compress_choice, strcmp(good[i], "auto") == 0 ? "zstd" : good[i]);
|
||||
EXPECT_EQ_INT(cfg->use_compression, strcmp(good[i], "none") != 0 ? 1 : 0);
|
||||
config_delete(cfg);
|
||||
}
|
||||
static const char* const bad[] = {"lz4", "zlib", "zlibx", "bogus"};
|
||||
@@ -2326,6 +2329,8 @@ static void test_parse_args_log_file_format() {
|
||||
int positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0);
|
||||
EXPECT_EQ_STR(cfg->log_file_format, "%n %M");
|
||||
/* The format alone is inert (no --log-file): no destination report needed. */
|
||||
EXPECT_FALSE(cfg->report_dest_info);
|
||||
config_delete(cfg);
|
||||
|
||||
cfg = config_create();
|
||||
@@ -2334,6 +2339,61 @@ static void test_parse_args_log_file_format() {
|
||||
EXPECT_EQ_INT(parse_args(cfg, 5, separate_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_EQ_STR(cfg->log_file_format, "%n %M");
|
||||
config_delete(cfg);
|
||||
|
||||
/* With --log-file the log-format is a real output mode whose %i/%n columns
|
||||
need the receiver's destination snapshot (same as -i/--out-format). */
|
||||
const char* log_path = "cli_log_fmt_test.txt";
|
||||
cfg = config_create();
|
||||
char log_arg[64];
|
||||
snprintf(log_arg, sizeof(log_arg), "--log-file=%s", log_path);
|
||||
char* both_argv[] = {"fastsync", log_arg, "--log-file-format=%i %n", "/src", "/dst"};
|
||||
positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 5, both_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_NOT_NULL(cfg->log_file);
|
||||
EXPECT_TRUE(cfg->report_dest_info);
|
||||
config_delete(cfg);
|
||||
remove(log_path);
|
||||
}
|
||||
|
||||
/* --skip-compress takes a separate value even when it starts with '-' (e.g. a
|
||||
* suffix typed as "-foo"); the cluster expander must copy it verbatim rather
|
||||
* than treat it as a short-option cluster. */
|
||||
static void test_parse_args_skip_compress_dash_value() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "--skip-compress", "-foo/bar", "/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->skip_compress_set);
|
||||
EXPECT_EQ_INT(cfg->skip_compress_count, 2);
|
||||
EXPECT_EQ_STR(cfg->skip_compress_suffixes[0], "-foo");
|
||||
EXPECT_EQ_STR(cfg->skip_compress_suffixes[1], "bar");
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* -i and --out-format also request the destination snapshot. */
|
||||
static void test_parse_args_report_dest_info_modes() {
|
||||
Config* cfg = config_create();
|
||||
char* itemize_argv[] = {"fastsync", "-i", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 3, itemize_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_TRUE(cfg->report_dest_info);
|
||||
config_delete(cfg);
|
||||
|
||||
cfg = config_create();
|
||||
char* out_argv[] = {"fastsync", "--out-format=%n", "/src", "/dst"};
|
||||
positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 3, out_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_TRUE(cfg->report_dest_info);
|
||||
config_delete(cfg);
|
||||
|
||||
cfg = config_create();
|
||||
char* plain_argv[] = {"fastsync", "/src", "/dst"};
|
||||
positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 3, plain_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_FALSE(cfg->report_dest_info);
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* --delay-updates is a plain boolean receiver option. */
|
||||
@@ -3344,6 +3404,24 @@ static void test_parse_args_remote_option_multiple() {
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* The -M=value and -Mvalue short forms are expanded by the cluster expander to
|
||||
* "-M value" before parsing; both must still collect the remote option (there
|
||||
* is no dedicated -M= branch). */
|
||||
static void test_parse_args_remote_option_short_forms() {
|
||||
static const char* const forms[] = {"-M=--allow-delete", "-M--allow-delete"};
|
||||
for (size_t i = 0; i < sizeof(forms) / sizeof(forms[0]); i++) {
|
||||
Config* cfg = valid_client_config();
|
||||
EXPECT_NOT_NULL(cfg);
|
||||
char* argv[] = {"fastsync", "--source-dir", "/src", "--dest-dir", "/dst", (char*)forms[i]};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 6, argv, positional_args, &positional_count), 0);
|
||||
EXPECT_EQ_INT(cfg->remote_option_count, 1);
|
||||
EXPECT_EQ_STR(cfg->remote_options[0], "--allow-delete");
|
||||
config_delete(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Space-separated form "--remote-option OPT" also parses. */
|
||||
static void test_parse_args_remote_option_space_form() {
|
||||
Config* cfg = valid_client_config();
|
||||
@@ -4225,6 +4303,8 @@ void test_client_cli() {
|
||||
test_parse_args_list_only();
|
||||
test_parse_args_out_format();
|
||||
test_parse_args_log_file_format();
|
||||
test_parse_args_report_dest_info_modes();
|
||||
test_parse_args_skip_compress_dash_value();
|
||||
test_parse_args_checksum_choice_aliases();
|
||||
test_parse_args_checksum_choice_requires_value();
|
||||
test_parse_args_checksum_choice_equals_forms();
|
||||
@@ -4253,6 +4333,7 @@ void test_client_cli() {
|
||||
test_parse_args_trust_sender_default_false();
|
||||
test_parse_args_trust_sender();
|
||||
test_parse_args_remote_option_multiple();
|
||||
test_parse_args_remote_option_short_forms();
|
||||
test_parse_args_remote_option_space_form();
|
||||
test_parse_args_remote_option_missing_value();
|
||||
test_parse_args_remote_option_rejects_bad_values();
|
||||
|
||||
Reference in New Issue
Block a user