feat(parity): rsync 3.4.1 checksum/timeout/temp-dir/connectivity parity (#289 #295 #296)

#289 checksum/compression:
- -c/--checksum now implies the incremental content quick-check (without
  implying -t), so an unchanged file is skipped like rsync.
- --checksum-choice/--cc accepts xxh64/xxhash, xxh3, xxh128, md5 and auto;
  md4/sha1/none and the two-name form are rejected by name.
- --compress-choice/--zc rejects lz4/zlib/zlibx by name (zstd/none/auto kept).
- --checksum-seed=0 is randomized per transfer and sent on the wire.
- --skip-compress uses rsync 3.4.1's default suffix list; slash separators and
  dot-less suffixes are accepted.
- add --no-whole-file.

#295 timeouts/alloc/temp-dir:
- --timeout default 0 (disabled), --contimeout default 60; 0 disables both,
  plus --no-timeout/--no-contimeout.
- --max-alloc=0 means no allocation limit (was rejected).
- --temp-dir accepts any dir, requires it to exist, and falls back to a
  non-atomic copy on EXDEV instead of aborting.

#296 connectivity/daemon:
- -M/--remote-option is rejected for daemon/TCP destinations (SSH-only).
- --trust-sender clarified as receiver-local; server-path tests added.
- --stop-at accepts rsync's full date form (y-m-dTh:m etc.).

Adds unit and integration coverage; no wire-field change, PROTOCOL_VERSION stays
2.22.0.
This commit is contained in:
2026-09-15 22:20:02 +02:00
parent 23552e823d
commit 3eec5a4cc3
25 changed files with 1095 additions and 222 deletions
+10 -8
View File
@@ -45,12 +45,12 @@ static void config_set_defaults(Config* config) {
config->server_port = 8080;
config->server_port_set = false;
config->server_host_set = false;
/* 0 means "--timeout not given": the transport keeps its own built-in 30 s
* socket timeout (tcp_set_timeouts ignores non-positive values) and the
* protocol layer keeps its built-in 60 s per-message deadline. A positive
* value overrides BOTH (see protocol_session_set_io_timeout). */
/* rsync defaults: --timeout=0 (I/O timeouts disabled) and --contimeout=60.
* A value of 0 disables the deadline on both the socket layer
* (tcp_set_timeouts) and the protocol layer
* (protocol_session_set_io_timeout); a positive value sets it. */
config->timeout = 0;
config->contimeout = 10;
config->contimeout = 60;
config->quiet = false;
config->stats = false;
config->max_depth = 0;
@@ -208,7 +208,7 @@ static bool validate_received_config(const Config* config) {
config->delta_block_size <= DELTA_BLOCK_SIZE_MAX &&
config->delta_max_file_size <= DELTA_MAX_FILE_SIZE && config->modify_window >= 0 &&
config->max_delete >= -1 && config->skip_compress_count >= 0 &&
config->skip_compress_count <= MAX_SKIP_COMPRESS_SUFFIXES && config->max_alloc > 0 &&
config->skip_compress_count <= MAX_SKIP_COMPRESS_SUFFIXES &&
(!config->chmod_spec || !*config->chmod_spec ||
chmod_apply(0, config->chmod_spec, &(mode_t){0})) &&
config->super_mode >= SUPER_MODE_AUTO && config->super_mode <= SUPER_MODE_OFF;
@@ -780,9 +780,11 @@ void config_delete(Config* config) {
* ------------------------------------------------------------------------- */
/* --max-alloc: raw 64-bit value, clamped server-side and installed as the
* session allocation ceiling. A zero value is rejected. */
* session allocation ceiling. Zero means "no alloc limit" (rsync's
* --max-alloc=0) and is passed through; a non-zero value is clamped to the
* server's own ceiling. */
static bool config_receive_max_alloc(int fd, unsigned long long* value) {
if (!receive_n_data(fd, value, sizeof(*value)) || *value == 0)
if (!receive_n_data(fd, value, sizeof(*value)))
return false;
if (*value > MAX_SERVER_ALLOC)
*value = MAX_SERVER_ALLOC;