Release v2.29.0 #312

Merged
TapTap merged 123 commits from dev into main 2026-09-23 02:05:14 +02:00
Owner

Release v2.29.0

Promotes dev to main (last release: v2.28.0, 2026-09-20). Wire-breaking: PROTOCOL_VERSION 2.28.0 → 2.29.0 (strict handshake — client and server must match). Config-frame layout unchanged (golden length 886).

Included

  • Transport I/O vtable over TCP/TLS; TLS multithreaded sendfile path fixed; per-thread SSL resolution hardened.
  • Symlink-xattr wire block (protocol 2.29.0): no-follow capture/apply of a symlink's own xattrs. Documented no-op on Linux (VFS refuses symlink xattrs), carried for correctness on platforms that support it.
  • No-wire parity burn-down: --inc-recursive no-op, in-root absolute --temp-dir, --delete-before phase-0 closed (single-threaded and --threads), rsync-interoperable --fake-super (user.rsync.%stat), --devices per-entry failure that continues, practical rsyncd.conf grammar subset with modules read-only by default and warnings for accepted-but-unenforced access-control keys.
  • Matrix: 119 ✅ / 14 ⚠️ / 24 ❌ = 157.
  • Two CI-hardening hotfixes (ASan/LSan dumpable flag).

Verification (on dev @ the release commit and each merged PR)

Strict -Werror, clang-format 18, cppcheck clean; unit 45/45 (ASan with leak detection, UBSan, valgrind); integration 900 passed; differential parity 62 passed; preflight/README-consistency passed. Full dev-push CI green.

Tag: v2.29.0 (annotated, on the release commit).

## Release v2.29.0 Promotes `dev` to `main` (last release: v2.28.0, 2026-09-20). **Wire-breaking: `PROTOCOL_VERSION` 2.28.0 → 2.29.0** (strict handshake — client and server must match). Config-frame layout unchanged (golden length 886). ### Included - **Transport I/O vtable** over TCP/TLS; TLS multithreaded sendfile path fixed; per-thread SSL resolution hardened. - **Symlink-xattr wire block** (protocol 2.29.0): no-follow capture/apply of a symlink's own xattrs. Documented no-op on Linux (VFS refuses symlink xattrs), carried for correctness on platforms that support it. - **No-wire parity burn-down**: `--inc-recursive` no-op, in-root absolute `--temp-dir`, `--delete-before` phase-0 closed (single-threaded and `--threads`), rsync-interoperable `--fake-super` (`user.rsync.%stat`), `--devices` per-entry failure that continues, practical `rsyncd.conf` grammar subset with modules **read-only by default** and warnings for accepted-but-unenforced access-control keys. - Matrix: **119 ✅ / 14 ⚠️ / 24 ❌ = 157**. - Two CI-hardening hotfixes (ASan/LSan dumpable flag). ### Verification (on `dev` @ the release commit and each merged PR) Strict `-Werror`, clang-format 18, cppcheck clean; unit 45/45 (ASan with leak detection, UBSan, valgrind); integration 900 passed; differential parity 62 passed; preflight/README-consistency passed. Full dev-push CI green. Tag: `v2.29.0` (annotated, on the release commit).
TapTap added 123 commits 2026-09-23 02:00:03 +02:00
docs(handoff): v2.28.0 released to main (PR #304, tag v2.28.0)
CI / lint (push) Successful in 2m0s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 19s
CI / sanitizers (address) (push) Successful in 51s
CI / sanitizers (undefined) (push) Successful in 43s
CI / build-and-test (push) Successful in 1m7s
CI / coverage (push) Failing after 31s
CI / fuzz-build (push) Successful in 47s
CI / valgrind (push) Successful in 2m13s
5597e74f6a
test: eliminate fork/write race in incremental-check server tests
CI / lint (push) Successful in 2m1s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 21s
CI / sanitizers (address) (push) Successful in 53s
CI / sanitizers (undefined) (push) Successful in 44s
CI / build-and-test (push) Successful in 1m11s
CI / fuzz-build (push) Successful in 47s
CI / coverage (push) Successful in 43s
CI / valgrind (push) Successful in 2m14s
558782d339
The parent sends the file data body after the receiver's STATUS_NEXT, but
the forked child exited as soon as receive_incremental_check returned.  The
parent's send_data could then race the child's exit into a spurious EPIPE
(seen in the coverage job as test_server.c:1222), or the reverse: the parent
could be descheduled past the child's exit.

Keep the child alive until the parent closes its write end (drain to EOF),
and close the parent's write end before waitpid so the child can observe EOF.
Applied to the three tests sharing the pattern: size-mismatch, FIFO
destination, and FIFO basis.  Child exit status remains the authoritative
assertion.
Collect each directory's entries up front and process extraneous
subdirectories first (descending name, depth-first), then extraneous
files (descending name), then descend into kept subdirectories in
ascending order.  Emit a trailing slash for deleted directories in
observers/dry-run output.  This matches rsync's delete order for
--delete-before/--delete-after/--delete-delay and for dry-run listings.
Resolve a relative --compare-dest/--copy-dest/--link-dest DIR against the
destination directory and append the file's transfer-relative name, as
rsync 3.4.1 does, instead of appending FastSync's source-mirrored wire
path (the historical spelling stays as a fallback for existing layouts).

Stop inheriting the ordinary delta engine's 16 KiB minimum and 10x size
ratio in the -y/--fuzzy candidate search: rsync's find_fuzzy has no
delta-size gate, so an oversized or sub-16-KiB sibling is now reused.
The ordinary delta path's bounds are unchanged.
Buffer and sort each directory's inspected entries (non-directories
ascending, then directories ascending) and walk them depth-first via a
LIFO directory stack, so the sequential scanner's stream matches rsync
3.4.1's flist order.  This makes the --info=name transfer order and the
--delete-during/--delete-delay deletion sequence byte-identical to rsync
(differential tests in test_parity_order.py); --threads stays unordered
(no rsync analogue) and is documented as such.

Adds LIFO queue_push/queue_pop over the existing ring buffer.
Transmit the complete --delete-during/--delete-delay per-directory plan
set before the first data frame, so a mid-transfer abort has already
applied every planned removal like rsync's generator; completed runs are
unchanged.  Route -d/--dirs through the same per-directory plans: the
generator records only directories whose direct children it enumerated,
so extras directly inside a listed directory are removed while an
untraversed subdirectory's mirror is shielded (rsync's -d DIR/ --delete).
Also shields a -x mount point's untraversed destination content.
--info=mount now prints rsync's mount-point skip line (matching rsync
3.4.1, which emits it for repeated -xx and drops the mount-point dir);
--info=stats enables the same block as --stats; -x is repeatable.
--stats counts traversed directories for the Number of files breakdown
even when no directory metadata is captured (-r without -t/-p).
--debug enables real output for flist/del/hash/deltasum/recv/filter/send
at their natural FastSync events (synthetic categories stay inert).

--stats and --debug rows keep their documented residual status.
test: assert partial --max-delete survivor order matches rsync
CI / lint (pull_request) Successful in 1m58s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-fast (pull_request) Successful in 18s
CI / build-and-test (pull_request) Successful in 54s
9b05972375
Merge PR #305: rsync-parity cycle 2.29 (120/10/27, no wire change)
CI / lint (push) Successful in 1m57s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 24s
CI / sanitizers (address) (push) Successful in 51s
CI / sanitizers (undefined) (push) Successful in 44s
CI / build-and-test (push) Successful in 1m16s
CI / fuzz-build (push) Successful in 46s
CI / coverage (push) Successful in 42s
CI / valgrind (push) Successful in 2m12s
0fbb9de915
MAX_DECOMPRESSED_SIZE was 100 MiB while the receiver advertises and the
sender compresses whole files up to MAX_RECEIVE_WHOLE_FILE_SIZE (256 MiB),
so -z on a 100-256 MiB regular file failed with 'Declared decompressed
size exceeds 104857600 bytes'.  Define the internal bomb-guard ceiling in
terms of the protocol constant so the two bounds cannot drift, and add
unit coverage for a 130 MiB payload (accepted) and an over-ceiling
declared size (still rejected).
rsync 3.4.1 resolves --partial-dir after option parsing and sets keep_partial,
so --partial-dir=DIR alone retains an interrupted transfer's partial file.
FastSync only used the partial dir when --partial was also given, silently
discarding it otherwise.

Set Config->partial in cli_finalize_config whenever partial_dir is set.
Following rsync, an explicit --no-partial does NOT win (verified on rsync
3.4.1 in either option order); --inplace is guarded because it writes the
destination in place with no partial staging.

Tests: CLI unit coverage for the implication/precedence/inplace guard, and a
deterministic integration case that blocks the final install (non-empty
directory at the destination) and asserts the staged partial survives under
--partial-dir alone.
Three receiver security fixes from the audit:

1. --temp-dir symlink escape (High): file_open_temp_dir() opened the
   client-controlled scratch dir with a bare open(), so a symlink planted
   under the receive root let a peer redirect receiver scratch files
   outside the authorized root.  The opened dir is now judged by the REAL
   path of its fd (via /proc/self/fd), and any target outside the
   authorized receive root is refused with a logged error (EACCES).  An
   in-root symlink (the EXDEV cross-filesystem fallback case) still works,
   and the no-root local batch path is unchanged.

2. setuid/setgid/sticky under SUPER_MODE_OFF (High): the special bits were
   applied under --perms (and via --chmod) even when the connection forbade
   super-user activities.  FileAttrPolicy gains super_permitted, set by
   file_attr_policy_from_config() from privilege_super_mode_permitted();
   metadata_mode_for_policy(), the symlink path, the special-node creation
   path, and the deferred directory-mode apply now strip the special bits
   when it is false.  Exact rsync semantics are preserved when permitted.

3. daemon umask (Low): daemonize() forced umask(0), so implied parent
   directories created without -p were world-writable 0777.  Set the
   conventional daemon umask 022 instead (rsync never forces 0); -p/-a mode
   preservation is unaffected because it restores modes via fchmod.

Tests: new unit tests for file_open_temp_dir confinement and the
masked/unmasked special-bit policy (incl. the --chmod path), a daemon
world-writable-dir regression test, an integration escape test, and a
root-only integration test asserting special bits are masked without
--allow-super.  The old cross-filesystem test encoded the vulnerable
behavior (symlink target outside the root) and is replaced by the escape
test; the EXDEV fallback code is retained for in-root links.
- multiprocessing: destroy mutex_progress on the dir_entries_mutex
  init-failure path (init >= 7); drop bogus log_perror
- delete_plan: capture errno before free() in apply_deferred_path
- queue/array_list: log_message instead of log_perror for non-errno
  conditions
- protocol: reject unknown wire Status values via status_is_valid() in
  receive_status, receive_status_timed and the keepalive reader; declare
  protocol_receive_status_timed in protocol.h
- protocol: %llu for unsigned long long debug counters
- tests: out-of-range status rejection test
The standalone filter_rule_parse() already rejected the rsync xattr-name
'x' modifier, but the list parser used by --filter/-f silently dropped the
flag for merge/dir-merge rules (and relied on a second parse for plain
rules).  Reject it explicitly in filter_list_parse_append_depth() with the
same diagnostic, so '-x', 'merge,x' and 'dir-merge,x' all fail cleanly.

Also reject the unimplemented rsync merge modifiers 'e', 'n' and 'w'
instead of folding them into the pattern, which previously produced
misleading errors such as "could not read merge file 'n file'".  Only a
token made up solely of modifier characters is treated as a modifier run,
so glued patterns ('-newfile', '-e2e') and mixed tokens ("H,!secret")
keep their historical parsing.

Adds tests/test_filter.c with focused rejection and supported-syntax
cases.
Client: replace the non-async-signal-safe signal(3) call inside
client_signal_handler() with a precomputed SIG_DFL sigaction(2), which is
on the POSIX async-signal-safe list.  The handler stays installed while a
transfer is armed so a repeated Ctrl-C still leads to a graceful abort
rather than a hard kill mid-cleanup.

Server: cleanup() now only calls _exit(2) (async-signal-safe).  The former
server_delete()/daemon_conf_free()/credentials_free() teardown called
free()/close()/SSL_CTX_free() from signal context, which can deadlock or
corrupt the heap if the signal lands inside malloc/free.  Handlers are
installed with sigaction(2) instead of signal(3).  The normal shutdown
path in main() still performs the full teardown; the signal path relies on
process exit to reclaim the parent daemon's socket, anonymous shared
mapping and heap (no named/persistent parent resource is left behind).
secret_file_open() previously opened --password-file/--early-input with
plain O_RDONLY, so a symlinked path was followed before the owner/mode
fstat gate ran, and an empty/planted FIFO could block fgets forever.
Open with O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC (mirroring the dummy-key
sidecar): ELOOP now fails closed, and a writer-less FIFO yields EOF/EAGAIN
instead of hanging. Clear O_NONBLOCK again for regular files, where it is
a no-op, so their stdio read path is unchanged.

file.c: drop the duplicate <fcntl.h>/<unistd.h> includes (kept the first
occurrences).

Tests: a symlinked password file is rejected, and a writer-less named
FIFO fails cleanly without hanging.
secret_file_open() opened secret files with O_NONBLOCK and only cleared it
for S_ISREG, so on a FIFO/process-substitution source (--password-file
<(...), --early-input <(...)) fgets() failed immediately with EAGAIN when
the writer had not yet produced data, breaking slow producers.

Keep O_NONBLOCK at open() (a writer-less FIFO must not block the open) and
route all three readers through a new secret_read_line() helper.  It
accumulates a line across reads and, on EAGAIN/EWOULDBLOCK (or a partial
line) with no newline and no EOF, clearerr()s and polls for readability
against one overall CLOCK_MONOTONIC deadline of
CREDENTIAL_FIFO_READ_TIMEOUT_MS (3000 ms); on timeout or a real read error
it fails with a clear message.  EOF finishes normally.  Regular files are
left blocking and read exactly as before.

Handles a line split across several write()s and keeps the owner/mode
fstat gate, O_NOFOLLOW and the /dev/fd/N exception unchanged.
The earlier modifier-rejection change rejected e/n/w on all rules, but rsync
3.4.1 accepts them (plus the '-' merge-only modifier) on merge and dir-merge
rules.  Restrict the rejection to non-merge rules and consume the merge-file
modifiers (e/n/w/-) so they no longer leak into the merge filename.

- is_merge_rule()/is_merge_modifier_char() gate the merge-only modifiers.
- scan vs consume sets: e/n/w still count as modifier-run chars on every rule
  (pure tokens like -new/-press stay rejected), but are only consumed on merge
  rules, preserving mixed-token parsing such as H,!secret -> ecret.
- '-' is accepted/consumed only on merge/dir-merge (e.g. dir-merge,- .rules).
- x remains rejected everywhere with its dedicated message.
- e/n/w/- semantics remain unimplemented and are documented as accepted-but-
  ignored in filter.h.

Tests: split the merge forms out of the rejection test into a new acceptance
test asserting the merge file is read and dir_merge_names keeps the modifier-
free basename; non-merge pure-modifier forms still rejected.
Update the docs for the audit follow-up fixes:
- --filter merge modifiers e/n/w/- are now accepted-and-consumed on
  merge/dir-merge rules (rejected on non-merge, x rejected everywhere);
  their semantics stay unimplemented, so the --filter row moves to Caveat
  and the tally becomes 119/11/27 = 157.
- --inplace + --partial-dir is rejected with rsync's message.
- secret_file_open() O_NOFOLLOW (symlinked credential paths fail closed;
  fd-backed paths exempt) and ~3 s bound-wait on FIFO reads.
- AGENTS setpriv wording corrected to the collected instance count.
- CHANGELOG [Unreleased] audit section extended with the follow-ups.
Merge branch 'fix/audit-docs3' into fix/audit-cycle
CI / lint (pull_request) Successful in 2m46s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-fast (pull_request) Successful in 31s
CI / build-and-test (pull_request) Successful in 54s
c062a0762e
Merge PR #306: codebase audit cycle (security, correctness, refactors, docs)
CI / lint (push) Successful in 2m31s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 23s
CI / sanitizers (undefined) (push) Successful in 47s
CI / sanitizers (address) (push) Successful in 1m13s
CI / build-and-test (push) Successful in 1m19s
CI / fuzz-build (push) Successful in 49s
CI / coverage (push) Successful in 44s
CI / valgrind (push) Successful in 2m18s
07dfec629f
Move the stats/progress reporting, scanner-preparation/scan helpers and
manifest/list/dry-run senders out of the ~3.9k-line client_send.c into
client_report.c, client_scan.c and client_manifest.c, sharing declarations
through the new internal client_send_internal.h.  client_send.c keeps the
transfer orchestration and is now ~2.1k lines.

Decompose the monolithic send_files into static phase helpers
(send_files_prepare/_prepare_delete/_run/_finalize/_cleanup) driven by a
single SendFilesState; ownership, ordering and exit codes are unchanged.

No behavior change.
Move the filter rule-tree/context helpers and entry inspection into
scanner_filter.c, the parallel scanner into scanner_parallel.c, and keep
the sequential scanner in scanner.c.  Shared internal declarations live in
the new scanner_internal.h; scanner.h stays the public façade.

Decompose directory_scanner_next into static helpers (skipped-entry,
selection-protection, mount/-x, directory-finish and per-entry handlers)
with no semantic change.
Pure structural split of src/shared/file_receive.c into focused translation
units behind the unchanged file_receive.h facade:

- file_save.c   : save-to-disk, special nodes, --delay-updates staging
- incremental_check.c : xattr/delta/basis/fuzzy receive + check state machine
- delete_commit.c : manifest receive + delete budget walkers
- file_receive.c : wire receive dispatch + deferred dir metadata

The shared receive_file_xattrs helper and MAX_FILE_DATA_SIZE are declared in
incremental_check.h.  file_save_to_disk_full_ex is decomposed into static
helpers (validation, special dispatch, dir/symlink creation, path resolution,
pre-write policies, data install) routed through one cleanup epilogue.

No behavior change.
refactor(delete): rename basis helper to delete_basis_relative
CI / lint (pull_request) Successful in 1m43s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-fast (pull_request) Successful in 19s
CI / build-and-test (pull_request) Successful in 57s
1167e7970b
Merge PR #307: structural refactor cycle (no behavior change)
CI / lint (push) Successful in 1m42s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 21s
CI / sanitizers (address) (push) Successful in 53s
CI / sanitizers (undefined) (push) Successful in 43s
CI / build-and-test (push) Successful in 1m16s
CI / fuzz-build (push) Successful in 48s
CI / coverage (push) Successful in 44s
CI / valgrind (push) Successful in 2m18s
8cc3dd993b
file_save_directory_to_disk() applied the exact source mode (fchmod)
inline for explicit --dirs/STATUS_MKDIR entries.  A restrictive source
mode (e.g. 0555) then made the directory read-only before its children
were written, so a non-root receiver failed each child with EACCES.  The
recursive -a path never hit this because it defers directory metadata.

Remove the inline fchmod and let the existing deferred
dir_metadata_list_apply() stamp the exact mode at end of transfer, as the
recursive path does.  Keep the inline ownership and xattrs (a direct
file_save_to_disk_full() caller has no deferred pass) and document the
resulting intentional ordering.  Capture errno before output_escape() in
the inline timestamp diagnostic so strerror() reports the real error, and
add the missing trailing newline to tests/test_xattr.c.
Merge branch 'fix/issues-f4' into fix/issues-triage
CI / lint (pull_request) Successful in 1m45s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-fast (pull_request) Successful in 17s
CI / build-and-test (pull_request) Successful in 54s
5d1303ffdf
Merge PR #308: close valid residuals of issues #286-#297
CI / lint (push) Successful in 1m45s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 21s
CI / sanitizers (address) (push) Successful in 52s
CI / sanitizers (undefined) (push) Successful in 42s
CI / build-and-test (push) Successful in 1m16s
CI / fuzz-build (push) Successful in 47s
CI / coverage (push) Successful in 44s
CI / valgrind (push) Successful in 2m18s
d780a4625e
# Conflicts:
#	RSYNC_COMPAT.md
# Conflicts:
#	RSYNC_COMPAT.md
docs: reconcile RSYNC_COMPAT for parity-next review follow-ups
CI / lint (pull_request) Successful in 1m46s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-fast (pull_request) Successful in 17s
CI / build-and-test (pull_request) Successful in 54s
4f945a8e39
Merge PR #309: parity burn-down
CI / lint (push) Successful in 1m46s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 22s
CI / sanitizers (address) (push) Failing after 54s
CI / sanitizers (undefined) (push) Successful in 43s
CI / build-and-test (push) Successful in 1m22s
CI / fuzz-build (push) Successful in 49s
CI / coverage (push) Successful in 44s
CI / valgrind (push) Successful in 2m19s
3ae7685655
test: restore dumpable flag after setuid drop so LSan can run under ASan
CI / lint (pull_request) Successful in 1m48s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-fast (pull_request) Successful in 18s
CI / build-and-test (pull_request) Successful in 55s
a07cc00bb0
Merge PR #310: restore dumpable flag for ASan/LSan
CI / lint (push) Successful in 1m46s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 21s
CI / sanitizers (address) (push) Successful in 54s
CI / sanitizers (undefined) (push) Successful in 43s
CI / build-and-test (push) Successful in 1m22s
CI / fuzz-build (push) Successful in 48s
CI / coverage (push) Successful in 44s
CI / valgrind (push) Successful in 2m17s
6db9827b87
file_send.c chose between sendfile() and the TLS-aware buffered path by
calling io_get_ssl(), which reads the thread-local io_ssl. A worker thread
that bound a TLS ProtocolSession via protocol_session_bind() never ran the
handshake in that thread, so io_ssl is NULL there and a TLS + --threads
transfer took the raw sendfile() path on an encrypted socket.

Add protocol_current_ssl(), which prefers the bound session's SSL and falls
back to io_ssl on the fd-shim path, and use it in file_send.c. Un-xfail
test_tls_with_multithreading.
Introduce ProtocolIoOps (send/recv/has_pending), selected once by
protocol_session_init() and protocol_session_set_ssl(), and dispatch the
send, receive and status-read loops through session->ops instead of
branching on session->ssl at runtime.

Each op performs one transfer attempt and classifies the result
(PROTOCOL_IO_RETRY/CLOSED/ERROR), preserving the WANT_READ/WANT_WRITE
wait_events switching, the SSL_ERROR_SYSCALL/EINTR retry, the
SSL_pending poll gating and the deadline handling. The raw read()/write()
fallback lives in the plaintext ops.

Add unit tests: a socketpair session with a counting ops wrapper proving
the loops dispatch through the vtable, and a worker-thread test that
protocol_current_ssl() resolves the bound session's SSL when io_ssl is NULL.
docs: bump release version to 2.29.0 and reconcile protocol docs
CI / lint (pull_request) Successful in 1m48s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-fast (pull_request) Successful in 20s
CI / build-and-test (pull_request) Successful in 57s
f3d7672694
Merge PR #311: transport I/O vtable + symlink-xattr wire block (2.29.0)
CI / lint (push) Successful in 1m48s
CI / parity-fast (push) Skipped
CI / parity-full (push) Successful in 20s
CI / sanitizers (address) (push) Successful in 54s
CI / sanitizers (undefined) (push) Successful in 45s
CI / build-and-test (push) Successful in 1m20s
CI / fuzz-build (push) Successful in 48s
CI / coverage (push) Successful in 46s
CI / valgrind (push) Successful in 2m35s
13b257d1dd
Release v2.29.0
CI / lint (push) Successful in 1m48s
CI / parity-fast (push) Skipped
CI / lint (pull_request) Successful in 1m48s
CI / parity-full (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped
CI / parity-full (push) Successful in 19s
CI / sanitizers (address) (push) Successful in 50s
CI / sanitizers (undefined) (push) Successful in 44s
CI / build-and-test (push) Successful in 1m19s
CI / coverage (push) Successful in 45s
CI / fuzz-build (push) Successful in 54s
CI / parity-fast (pull_request) Successful in 20s
CI / build-and-test (pull_request) Successful in 56s
CI / valgrind (push) Successful in 2m43s
00197102bf
- Transport I/O vtable over TCP/TLS; TLS multithreaded sendfile fixed
- Symlink-xattr wire block (protocol 2.29.0; config frame unchanged)
- No-wire parity burn-down: --inc-recursive, in-root --temp-dir,
  --delete-before phase-0, rsync-interoperable --fake-super, --devices
  per-entry failure, rsyncd.conf key subset (read-only default)
- Protocol version: 2.29.0
- Tested: unit, integration, ASan, UBSan, valgrind, differential parity
TapTap merged commit 7585f46eb4 into main 2026-09-23 02:05:14 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: TapTap/FastSync#312