rsync re-implemented in Rust. The reference version is upstream rsync 3.5.1. oc-rsync speaks protocols 28 through 33 and aims to be a drop-in replacement. Known divergences are listed per test in the upstream testsuite manifests.
The binary is named oc-rsync, so it installs alongside the system rsync without conflict.
Release: 0.6.4 (2026-07-18). Changes merged since then are listed under Unreleased in the CHANGELOG. All transfer modes (local, SSH, daemon), the delta algorithm, metadata preservation and compression are implemented; the table below lists the gaps.
Upstream reference. Behaviour, source citations and the testsuite gate all track rsync 3.5.1 (upstream_version in Cargo.toml). 3.5.1 raised the protocol to 33. oc-rsync speaks 33, including MSG_BLOCK_STATS and the --stats touched-blocks line (#8003). It negotiates down to 28 for older peers and caps a newer peer at 33 instead of refusing it (#7916, #8209). The options and daemon directives added in 3.5.0 are implemented (see Path confinement below). The per-CVE audit is in SECURITY.md.
| Component | Status |
|---|---|
| Transfer | Local, SSH, daemon push/pull, daemon over remote shell (host::module) |
| Delta | Rolling + strong checksums, block matching |
| Metadata | Permissions, timestamps, ownership, ACLs (-A), xattrs (-X) |
| File handling | Sparse, hardlinks, symlinks, devices, FIFOs |
| Deletion | --delete (before/during/after/delay), --delete-excluded |
| Compression | zlib, zstd, lz4 with level control and negotiation |
| Checksums | MD4, MD5, XXH3/XXH128 with SIMD (AVX2, SSE2, NEON) |
| Incremental recursion | Negotiated whenever oc-rsync sends, when an oc-rsync server or daemon receives a push (#8169), and on client pulls by default (#8062, #8091, #8217). OC_RSYNC_PULL_INC_RECURSE=0 opts a pull out. --delete-before and --delete-after need the whole list and withhold it as in upstream. |
| Batch | --write-batch / --read-batch round trip |
| Daemon | Negotiation, auth, modules, chroot, syslog, pre/post-xfer exec; IPv4 and IPv6 listeners by default; one forked process per connection on Unix |
| Host names | Internationalized host names converted to IDNA A-labels, as upstream 3.5.1 does (#8104) |
| Filtering | --filter, --exclude, --include, .rsync-filter, --files-from |
| Reference dirs | --compare-dest, --link-dest, --copy-dest |
| Options | --delay-updates, --inplace, --partial, --iconv, fuzzy matching |
| Path confinement | --confine-root, --drop-D / --no-drop-D, --insecure-links / --no-insecure-links; daemon auth digest, insecure links, proxy protocol hosts |
| I/O | io_uring (Linux 5.6+), copy_file_range, clonefile (macOS), adaptive buffers |
Upstream's own 3.5.1 test suite runs against oc-rsync as $RSYNC on every pull request and nightly on master, in eight legs: platform {Linux, macOS} x daemon transport {stdio pipe, loopback TCP} x privilege {non-root, root}. The pipe legs run the whole 360-test corpus. The TCP legs add --daemon-tests-only and run the 161 tests that start a daemon. The legs run from upstream-testsuite-3.5.1.yml, behind the 3.5.1 badge at the top.
All eight legs are required status checks.
Outcomes, counted from each leg's committed manifest:
| leg | pass | fail | skip |
|---|---|---|---|
| Linux, non-root, pipe | 274 | 0 | 86 |
| Linux, root, pipe | 305 | 0 | 55 |
| Linux, non-root, tcp | 129 | 0 | 32 |
| Linux, root, tcp | 149 | 0 | 12 |
| macOS, non-root, pipe | 248 | 0 | 112 |
| macOS, root, pipe | 278 | 0 | 82 |
| macOS, non-root, tcp | 126 | 0 | 35 |
| macOS, root, tcp | 144 | 0 | 17 |
Re-derive any cell, and list any failing tests (the outcome is the second field; a fail row carries its cause and owner in a trailing comment):
awk '!/^#/ && NF {c[$2]++; t++} END {print t, c["pass"], c["fail"], c["skip"]}' \
tools/ci/upstream-3.5.1-expect.nonroot.txt
awk '!/^#/ && $2=="fail" {print $1}' tools/ci/upstream-3.5.1-expect.*.txt | sort -uNo 3.5.1 test fails on any leg. The last one, proto-subflist-freed, passes since the server receiver refuses a sub-list that arrives after the final file list was freed, as upstream flist.c does (#8193). Every remaining non-pass row is a skip with a reason. Only a change in outcome turns a leg red, including an unexpected pass, so a divergence cannot be re-baselined silently: the PR that fixes a cell must also flip its row to pass.
brew tap oferchen/rsync https://github.com/oferchen/rsync
brew install oferchen/rsync/oc-rsyncDownload from the Releases page:
| Platform | Formats |
|---|---|
| Linux (x86_64, aarch64) | .deb, .rpm, Alpine .apk, static musl .tar.gz |
| macOS (x86_64, aarch64) | .tar.gz |
| Windows (x86_64) | .tar.gz, .zip |
Linux static tarballs come in two checksum variants:
| Variant | Filename | Description |
|---|---|---|
| Pure Rust (recommended) | *-musl.tar.gz |
No system dependencies |
| OpenSSL | *-musl-openssl.tar.gz |
OpenSSL MD4/MD5, vendored and statically linked |
Each release also ships three toolchain builds: stable (no suffix, recommended), beta (-beta) and nightly (-nightly).
Requires Rust 1.89 or newer. The repository pins 1.89.0 in rust-toolchain.toml.
git clone https://github.com/oferchen/rsync.git
cd rsync
cargo build --workspace --release| Feature | Crate(s) | Default | Purpose | Status |
|---|---|---|---|---|
zstd |
workspace, cli, core, engine, transfer, compress, protocol, batch |
yes | zstd codec and negotiation. | stable |
zstdmt |
compress |
no | Multithreaded zstd, needed to honour --compress-threads=N above 0. |
stable |
lz4 |
workspace, cli, core, engine, transfer, compress, protocol |
yes | LZ4 codec. | stable |
zlib-ng |
workspace, core, engine, transfer, compress, protocol |
yes | SIMD zlib-ng C backend for zlib. compress and protocol enable it by default, so every build that links core uses it. |
stable |
zlib-rs |
compress, protocol |
no | Pure-Rust zlib-rs backend for zlib. |
stable |
xattr |
workspace, cli, core, engine, transfer, daemon, metadata |
yes | Extended attributes (-X); NTFS ADS on Windows. |
stable |
acl |
workspace, cli, core, engine, transfer, daemon, metadata |
yes | ACLs (-A) via exacl; NTFS ACLs via windows-rs. |
stable |
iconv |
workspace, cli, core, engine, transfer, daemon, protocol |
yes | Filename charset conversion (--iconv). |
stable |
idn |
workspace, core |
yes | IDNA A-label conversion of host names (upstream SUPPORT_IDN). Without it, hosts are used as typed and --version reports no IDN. |
stable |
parallel |
workspace, cli, engine, checksums, signature |
yes | Rayon-based parallel file and checksum work. | stable |
io_uring |
workspace, transfer, fast_io |
yes | Linux 5.6+ batched I/O with runtime fallback. | stable |
sqpoll-mlock-basis |
fast_io |
yes | Pins the mmap'd basis window with mlock(2) so an SQPOLL ring can stay on beside an mmap basis reader. |
stable |
iocp |
workspace, transfer, fast_io |
yes | Windows I/O Completion Ports. | stable |
copy_file_range |
workspace, fast_io |
yes | Compatibility alias; the syscall is always compiled with runtime detection. | stable |
async |
workspace, core, engine, daemon, bandwidth, protocol |
yes | tokio for async I/O paths. | stable |
embedded-ssh |
workspace, cli, core, rsync_io |
yes | Built-in SSH client for ssh:// operands. |
stable |
ssh-config-parse |
rsync_io |
yes | ssh_config reader for the embedded client and the SSH compression warning. |
stable |
openssl |
workspace, checksums |
no | MD4/MD5 through system OpenSSL. | stable |
openssl-vendored |
workspace, checksums |
no | As openssl, statically linked. |
stable |
sd-notify |
workspace, core, daemon |
no | systemd sd-notify for the daemon. |
stable |
quic |
workspace, cli, core, daemon, rsync_io, transfer, test-support |
yes | QUIC transport (see QUIC transport). | stable |
iouring-send-zc |
workspace, fast_io |
yes | io_uring SEND_ZC socket writes (Linux 6.0+). Built in, but used only when the local node asks for zero-copy (see Performance tuning). |
stable |
daemon-zero-copy |
daemon |
yes | Lets a daemon sender use SEND_ZC when zero-copy is requested. |
stable |
daemon-seccomp |
workspace, daemon |
no | seccomp allowlist for daemon workers. Once compiled in, it is on at runtime; opt out with OC_RSYNC_NO_SECCOMP=1. |
opt-in |
incremental-flist |
transfer |
yes | Gates tests only. The incremental receiver is chosen at runtime from the negotiated flags (#7968). | stable |
lazy-metadata |
engine |
yes | Defers stat() until metadata is needed. |
stable |
mmap-free-basis |
workspace, engine, fast_io |
no | Reads the basis without mmap, through io_uring where available or buffered I/O. | experimental |
multi-producer |
engine |
no | Relaxes the single-producer invariant on WorkQueueSender. |
experimental |
thread-slab-pool |
engine, transfer |
no | Per-thread slab in front of BufferPool. |
experimental |
parallel-delete-consumer |
engine |
no | Parallel consumer for delete operations that keeps the wire order. | experimental |
vmsplice |
fast_io, transfer |
no | Linux vmsplice(2) + splice(2) writer. |
experimental |
dontcache |
fast_io, transfer |
no | Linux 6.14+ RWF_DONTCACHE bulk writer that does not evict the page cache. |
experimental |
macos-gcd |
fast_io, transfer |
no | macOS Grand Central Dispatch dispatch_io disk writer. |
experimental |
macos-kqueue |
daemon |
no | macOS daemon accept loop on one kqueue(2) wait over all listeners. |
experimental |
transmitfile |
fast_io |
no | Windows TransmitFile() file-to-socket primitive. |
experimental |
iouring-data-reads / iouring-data-writes |
engine, fast_io |
no | Route large file reads or writes through io_uring. | experimental |
adaptive-basis-dispatch |
fast_io |
no | Per-file basis-read dispatch between mmap and io_uring by recent throughput; on Windows it also selects the IOCP file reader. | experimental |
async-ssh |
core, rsync_io |
no | Async SSH transport; enable at runtime with OC_RSYNC_ASYNC_SSH=1. |
experimental |
async-daemon |
cli, daemon |
no | tokio accept loop dispatching sync workers. | experimental |
concurrent-sessions |
daemon |
no | Shared session state for multi-session daemons. | experimental |
tokio-transfer |
protocol |
no | Async multiplex read leaf; not wired into a transfer path. | experimental |
tracing |
core, engine, transfer, daemon, protocol, filters, logging, matching, signature |
no | Structured tracing instrumentation. |
stable |
Not listed: landlock (fast_io), which cli and daemon always enable on Linux; syscall_batch (fast_io), an empty alias; block2 (fast_io), the macos-gcd dependency; and the internal serde, bench-internal and test-support features.
Works like rsync:
# Local sync
oc-rsync -av ./source/ ./dest/
# Remote pull / push over SSH
oc-rsync -av user@host:/remote/path/ ./local/
oc-rsync -av ./local/ user@host:/remote/path/
# Daemon pull / push
oc-rsync -av rsync://host/module/ ./local/
oc-rsync -av ./local/ rsync://host/module/
# Daemon over remote shell
oc-rsync -av host::module/path/ ./local/
# Run as a daemon
oc-rsync --daemon --config=/etc/oc-rsync/oc-rsyncd.conf
# Compression, checksum-based sync with deletion
oc-rsync -avz --compress-level=3 ./source/ ./dest/
oc-rsync -avc --delete ./source/ ./dest/
# Batch mode (record and replay)
oc-rsync -av --write-batch=changes ./source/ ./dest/
oc-rsync -av --read-batch=changes ./other-dest/For all options: oc-rsync --help.
host:pathanduser@host:pathspawn the systemssh, like upstream.-e/--rshandRSYNC_RSHchoose another remote shell. As in upstream, the child's stdin and stdout areAF_UNIXsocketpairs, with a fallback to pipes if the kernel refuses one (#8183). This path needs no cargo feature.ssh://[user@]host[:port]/pathis an oc-rsync extension. It uses an embedded client built onrusshand spawns no subprocess. It needs theembedded-sshfeature (on by default). Without the feature anssh://operand fails with a diagnostic. Combiningssh://with-e/--rshorRSYNC_RSHis rejected.
The embedded client supports key-based (RSA, ED25519, ECDSA) and password authentication, and SSH_AUTH_SOCK agents. It reads ~/.ssh/config and /etc/ssh/ssh_config with an in-house parser (ssh-config-parse feature, on by default): Host and Match blocks, Include, first-obtained-wins precedence, ProxyCommand / ProxyJump, and the host-key and authentication families. A differential harness checks it against ssh -G in CI.
Some SSH features (for example certificate-based auth) may be missing; please open an issue. The embedded client bridges a synchronous transfer pipeline to async russh through spawn_blocking; docs/design/russh-async-native-path.md describes the planned async-native path. See also the SSH TRANSPORT section of oc-rsync(1).
oc-rsync can carry the rsync daemon protocol over QUIC (TLS 1.3 over UDP) instead of a plain TCP socket. Upstream rsync has no QUIC transport, so both ends must be oc-rsync. The quic cargo feature is in the default build, and release artifacts for Linux, macOS and Windows carry it from the first release after v0.6.4. Being compiled in changes nothing at runtime: the client uses QUIC only for a quic:// operand or --quic, and the daemon listens on UDP only when quic cert file and quic key file are set. Windows builds run the QUIC client only: a Windows daemon with a QUIC directive refuses to start. To build without QUIC, pass --no-default-features plus the features you need:
cargo build --release --no-default-features --features zstd,lz4,acl,xattr,iconv,parallelThe daemon needs a certificate and key in the global section of oc-rsyncd.conf, and UDP open on its port (873 unless quic port says otherwise):
quic cert file = /etc/oc-rsync/server-cert.pem
quic key file = /etc/oc-rsync/server-key.pem
# Optional: require client certificates (mutual TLS)
# quic client ca file = /etc/oc-rsync/client-ca.pem
# Optional: QUIC on loopback only (default: the TCP addresses, dual-stack)
# quic address = 127.0.0.1 ::1Clients must name the CA that signed the daemon certificate. There is no default trust and no trust-on-first-use, and the system trust store is not consulted. A failed QUIC connection exits 5 and never falls back to TCP.
Under load, the daemon answers a new client with a stateless Retry before it holds any state for it. quic retry sets the policy: auto (the default), always or never.
oc-rsync -av --quic-ca ca-cert.pem quic://host/module/ dest/
oc-rsync -av --quic --quic-ca ca-cert.pem host::module/ dest/
oc-rsync -av --quic-ca ca-cert.pem --quic-cert client-cert.pem --quic-key client-key.pem quic://host/module/ dest/The ALPN identifier is rsync. The congestion controller defaults to BBR; --quic-cc selects cubic or newreno, and --quic-window sizes the flow-control window. The daemon leaf certificate must be CA:FALSE, which a plain openssl req -x509 does not produce. docs/quic-transport.md has the full guide: certificate generation, every directive, flag and environment variable, a walkthrough, error messages and limitations.
oc-rsync uses io_uring when the kernel and probed opcodes allow it, and otherwise falls back to standard read(2)/write(2). The kernel floor is Linux 5.6 (MIN_KERNEL_VERSION in crates/fast_io/src/io_uring/config.rs). Provided buffer rings need 5.19+. SEND_ZC needs Linux 6.0+; the iouring-send-zc feature is in the default set, but SEND_ZC runs only when zero-copy is requested. The per-opcode kernel table is in docs/audit/iouring-opcode-kernel-floor.md.
Three policies: auto (default; probe and fall back), --io-uring (require it), --no-io-uring (never use it). --version shows the active backend.
Avoid double compression over SSH. SSH compression (ssh -C, or Compression yes in ssh_config) and rsync's own -z compress the same bytes twice, costing CPU for little gain. Pick one, usually rsync's -z: oc-rsync negotiates zstd when both peers support it, falls back to zlib, and honours --skip-compress.
# Good: rsync compresses, SSH carries the bytes as-is.
oc-rsync -avz user@host:/src/ /dst/
# Bad: -C on ssh re-compresses what rsync already compressed.
oc-rsync -avz -e 'ssh -C' user@host:/src/ /dst/oc-rsync warns when it sees -C or -o Compression=yes in the SSH argv, and (with ssh-config-parse) when a Compression yes directive applies from ssh_config. It does not disable either layer.
--zero-copy and SEND_ZC. Zero-copy socket writes (io_uring SEND_ZC, Linux 6.0+) are off by default. On a loopback benchmark they made a 2 GiB daemon pull slower, so they stay opt-in. The choice is local to each node and is never forwarded to the peer (#8183). A client opts in with --zero-copy. A server opts in with oc-rsync --daemon --zero-copy, with --rsync-path='oc-rsync --zero-copy', or with OC_RSYNC_ZERO_COPY=on|off|auto. The flag wins over the variable, and the variable over the default. See docs/design/iouring-send-zc.md.
Parallel delta work. The sender scans each file's delta on one thread unless --parallel-delta-scan is given; it applies to local transfers of large, duplicate-free basis files. --checksum-threads=N sets the threads that hash basis signatures; the default uses the available cores above a size threshold, and 1 forces sequential hashing.
SSH stderr. The remote shell inherits oc-rsync's own stderr, as upstream piped_child() arranges (pipe.c:48-97): its messages reach the terminal byte for byte, as soon as they are written, and exactly once. Only stdin and stdout are socketpairs. See docs/ssh-transport.md.
- Two buffer pools.
OC_RSYNC_BUFFER_POOL_SIZE,OC_RSYNC_BUFFER_POOL_MEMORY_CAPandOC_BUFFER_POOL_BLOCK_SIZEtune the engineBufferPoolonly. The io_uring registered buffer pool is sized statically and does not adapt. Its cost has not been measured separately. - io_uring buffer-group IDs are a 16-bit namespace, and allocation returns
BgidAllocError::Exhaustedrather than wrapping. No transfer path allocates one today. Seedocs/audits/bgid-lifecycle.md. - Daemon traffic over TCP is plaintext, like upstream (authentication, no encryption). There is no TLS over TCP. Use SSH, QUIC between two oc-rsync peers, or a TLS proxy in front of the daemon (
stunnel, HAProxy, nginx) with a wrapper such asrsync-sslorstunnel. - Windows IOCP covers sockets and disk writes. The IOCP file reader is only selected under the experimental
adaptive-basis-dispatchfeature.
| Protocol | Upstream versions | oc-rsync status | Coverage |
|---|---|---|---|
| 33 | 3.5.1 | Full support (default) | Unit tests, interop matrix against 3.5.1, and the 3.5.1 testsuite legs |
| 32 | 3.4.x, 3.5.0 | Full support | Interop matrix against 3.4.4 and 3.5.0 |
| 31 | 3.1.x - 3.3.x | Full support | Interop matrix against 3.1.3 |
| 30 | 3.0.x | Full support | Interop matrix against 3.0.9 |
| 29 | 2.6.4 - 2.6.9 | Full support | Interop matrix against 2.6.9, plus golden-byte tests |
| 28 | 2.6.1 - 2.6.3 | Full support | Interop matrix against 2.6.3, plus golden-byte tests in crates/protocol/tests/ |
| <= 27 | <= 2.5.x | Not supported |
oc-rsync advertises protocol 33 and uses the lower of its own and the peer's version, so a 3.5.0 or 3.4.x peer runs at 32. A 3.5.1 peer runs at 33. Per-version behaviour is implemented as protocol_version gates in the wire codecs, for example zlib_codec.rs.
Interop scenarios run in CI against the upstream releases listed in tools/ci/run_interop.sh: versions= for the scenario matrix, extra_build_versions= for build-only peers, and extended_matrix_versions= for the extended matrix. Push and pull are both covered, across transfer modes, deletion, compression, metadata, reference dirs, file selection, batch round trip, path handling, device nodes and daemon auth. See the interop compatibility matrix for detail.
| Platform | Tier | Notes |
|---|---|---|
| Linux x86_64 / aarch64 | Tier 1 | io_uring, splice, vmsplice, Landlock. Required CI runs the full nextest workspace. A seccomp syscall allowlist for daemon workers is available with --features daemon-seccomp; it is off in default builds and in released binaries. |
| macOS x86_64 / aarch64 | Tier 1 | clonefile, fcopyfile, full metadata, ACL and xattr support including AppleDouble (._foo) resource forks. Required CI runs a crate-scoped subset (core, engine, cli, metadata, apple-fs, fast_io) plus the rsync_io QUIC tests. |
| Windows x86_64 | Tier 2 | IOCP file and socket I/O, CopyFileExW, ReFS reflink, NTFS DACLs (partial), xattrs via NTFS Alternate Data Streams. No POSIX device nodes or FIFOs. Required CI tests the core, engine, cli, metadata, fast_io and transfer crates, plus the rsync_io QUIC tests. |
Tier definitions and criteria: Platform support tiers. Windows detail: Windows support matrix and the Windows Tier 2 stub inventory.
| Feature | Linux | macOS | Windows | Notes |
|---|---|---|---|---|
Permissions (-p) |
✓ | ✓ | ⚠ | Windows preserves only the read-only flag. |
Times (-t) |
✓ | ✓ | ✓ | Nanosecond precision. |
Ownership (-o/-g) |
✓ | ✓ | ✗ | uid/gid mapping is Unix-only. |
ACLs (-A) |
✓ | ✓ | ⚠ | exacl on Linux/macOS. Windows round-trips NTFS DACLs; deny ACEs, inherited ACEs, the SACL, non-rwx bits and unresolvable SIDs are dropped with a warning. See docs/design/windows-ntfs-acl-support.md. |
Xattrs (-X) |
✓ | ✓ | ✓ | Windows stores xattrs as NTFS Alternate Data Streams. |
Hardlinks (-H) |
✓ | ✓ | ✓ | |
| Symlinks | ✓ | ✓ | ⚠ | Windows: directory links fall back to a junction when unprivileged; file links need Administrator or Developer Mode, otherwise they are skipped with a warning (exit 23). |
Devices/specials (-D) |
✓ | ✓ | ✗ | |
Sparse files (-S) |
✓ | ✓ | ⚠ | Windows does not set FSCTL_SET_SPARSE. |
| Async I/O | ✓ io_uring | ⚠ standard I/O | ⚠ IOCP | io_uring is detected at runtime on Linux 5.6+. Windows uses IOCP for disk writes and sockets; file reads use buffered I/O. |
| Reflink / clone | ✓ FICLONE |
✓ clonefile |
⚠ ReFS only | |
| Optimized copy | ✓ copy_file_range |
✓ fcopyfile |
✓ CopyFileExW |
Each falls back to standard I/O. |
Legend: ✓ supported, ⚠ partial, ✗ not implemented.
Each tagged release runs .github/workflows/benchmark.yml against upstream rsync 3.5.1, 3.5.0 and 3.4.4 across local, SSH and daemon modes (#8106). It reports elapsed time (median, with run-to-run spread), peak RSS and corpus rate. Results are attached to the GitHub release as benchmark.png, benchmark_report.md and benchmark_results.json.
Releases up to and including v0.6.4 predate this harness: their report compares against 3.4.4 only, without peak RSS, corpus rate or spread. The chart above comes from the latest release, so read that release's benchmark_report.md for what it measured.
A transfer runs its roles as threads where upstream forks, for performance, with the same protocol. The daemon forks one process per connection, as upstream does, to isolate sessions. I/O buffers are sized by file size.
Branch protection requires fifteen checks: fmt + clippy, nextest (stable), Windows (stable), macOS (stable), Linux musl (stable), interop / interop with upstream rsync, unsafe-safety-comment-audit, and the eight upstream testsuite legs (upstream-testsuite / upstream testsuite and its (root) variant, the same two under upstream-testsuite-tcp /, and upstream testsuite (macOS) and (macOS, root) under upstream-testsuite-macos / and upstream-testsuite-macos-tcp /). docs/contributing/TESTING.md has the command that re-derives the list from the ruleset.
The SSH throughput check (SSR-1) compares a release build of oc-rsync against upstream in its own SSH throughput (SSR-1, release) job. It runs on every pull request and reports its result, but it is not a required check. Benchmarks run only when a release is tagged (see Performance). Interop coverage is described under Protocol and interop.
Of the 33 CVEs fixed upstream in rsync 3.5.0, 27 are fixed in oc-rsync, 1 is partially fixed and 5 do not apply. The partial one is CVE-2026-53796: every non-daemon receiver pins its destination for data, but metadata calls after a destination swap are still path-based. The 2024 and rsync 3.4.3 CVEs are fixed, mitigated or do not affect oc-rsync. rsync 3.5.1 names no CVE; of its nine security-relevant fixes, seven are mirrored and two do not apply. Unsafe code is confined to a few crates that wrap platform FFI and SIMD.
SECURITY.md has the per-CVE evidence, the unsafe-code policy, fuzzing and how to report a vulnerability. docs/audits/cve-uts351-matrix.md maps each CVE to its upstream fix, its upstream test cells and the Rust tests that pin it.
- Fork and create a feature branch.
- Install Rust 1.89.0 (pinned in
rust-toolchain.toml) andcargo-nextest, then run the checks CI runs:cargo fmt --all -- --check cargo clippy --workspace --all-targets --all-features --no-deps -- -D warnings cargo nextest run --workspace --all-features
- Open a PR with a conventional-commit prefix describing behavioural changes and interop impact.
See CONTRIBUTING.md for the full workflow and docs/contributing/ONBOARDING.md for a guided start.
src/bin/oc-rsync.rs # Entry point
crates/cli/ # CLI flags, help, output formatting
crates/core/ # Orchestration facade, session management, config
crates/protocol/ # Wire protocol (v28-33), multiplex framing
crates/transfer/ # Generator, receiver, delta transfer pipeline
crates/engine/ # Local copy executor, sparse writes, temp-file commit
crates/daemon/ # Daemon mode, module access control, systemd
crates/checksums/ # Rolling and strong checksums (MD4, MD5, XXH3, SIMD)
crates/filters/ # Include/exclude pattern engine, .rsync-filter
crates/metadata/ # Permissions, uid/gid, mtime, ACLs, xattrs
crates/platform/ # Process, identity, environment and signal FFI
crates/rsync_io/ # SSH stdio, rsync:// TCP transport, handshake
crates/fast_io/ # Platform I/O (io_uring, copy_file_range, sendfile)
crates/compress/ # zstd, lz4, zlib compression codecs
crates/bandwidth/ # Bandwidth limiting and rate control
crates/signature/ # Signature layout and block-size calculations
crates/matching/ # Delta matching and block search
crates/logging/ # Logging macros and verbosity control
crates/logging-sink/ # Message sink and output formatting
crates/batch/ # Batch file read/write support
crates/branding/ # Binary naming and version metadata
crates/embedding/ # Programmatic entry points for library usage
crates/apple-fs/ # macOS filesystem operations (clonefile, FSEvents)
crates/windows-gnu-eh/ # Windows GNU exception handling shims
crates/test-support/ # Shared test utilities (dev-dependency only)
API documentation: cargo doc --workspace --no-deps --open.
cli -> core -> engine, daemon, rsync_io, logging
core -> protocol -> checksums, filters, compress, bandwidth -> metadata
-> platform
GNU GPL v3.0 or later. See LICENSE.
Inspired by rsync by Andrew Tridgell and the Samba team.
Internal matching-engine optimisations adapted from zsync by Colin Phipps. They change only in-memory lookups, so the default wire format stays pure rsync.
Thanks to Pieter for his heroic patience in enduring months of my rsync commentary. Thanks to Elad for his endless patience hearing rsync protocol commentary as I'm introduced to it.
