The OpenBSD job runs inside a nested VM. At -j8 the --use-tcp run starts
many concurrent loopback daemons, and under that resource pressure the
daemon connection handshake occasionally loses a timing race and one test
hangs to the 300s runner timeout. It is an environment artifact, not an
rsync defect: the daemon handshake writes-then-reads with unbuffered early
I/O (no flush/mutual-wait deadlock), the indefinite wait is the documented
no-timeout daemon behaviour, and it does not reproduce off OpenBSD even with
the full suite pinned to a single CPU at -j8.
Drop just this job's --use-tcp parallelism to -j2 so the nested VM stops
over-subscribing; the pipe `make check` and every other platform are
unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Target previously-uncovered functions in the path/file-operation files the
resolver restructure touches, confirmed hit under coverage:
preallocate --preallocate (syscall.c do_fallocate) and sparse hole-punching
via --preallocate --sparse and --inplace --sparse (do_punch_hole),
on a file several levels deep.
fuzzy-basis --fuzzy basis selection with similar-named candidates and no
exact match, so the generator scores them (util1.c fuzzy_distance).
delete-deep add a --backup --delete case so removing an extraneous
backup-suffixed file consults delete.c is_backup_file.
preallocate probes --preallocate support up front and skips where it is
unavailable: macOS, the *BSDs and Solaris build without fallocate/posix_fallocate
(and FALLOC_FL_PUNCH_HOLE is Linux-only), and reject the option outright. It runs
on Linux and Cygwin. fuzzy-basis and delete-deep are plain local transfers with
no skips. All green on master and under --protocol=29/30.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The --expect-skipped check compared the skip list as an ordered string, so the
per-platform RSYNC_EXPECT_SKIPPED lists had to match runtests' collection order
(sorted filenames) exactly -- a subtle, easy-to-break ordering dependency.
Compare the skipped SET instead; which tests skipped is what matters.
Register the new require_tcp test daemon-access-ip in the per-platform
expected-skipped lists (it skips in the pipe-transport make check, like
daemon-chroot-acl and proxy-response-line-too-long).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
acls-depth skips where ACLs/setfacl are unavailable (macOS, Cygwin) like the
existing acls tests, and sparse skips on APFS (macOS), where a seek-written
hole isn't allocated sparsely. Add them to the per-platform RSYNC_EXPECT_SKIPPED
lists so the skip-set assertion stays accurate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Builds with --enable-coverage and runs the suite under both transports
(make coverage, then make coverage-tcp). gcovr's line/branch/decision totals
are printed to the step log and also written to the GitHub step summary, so the
coverage numbers are visible directly in the CI output; the HTML reports are
uploaded as an artifact. make coverage exits with the suite's status, so a test
regression fails the job.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Python rewrite had gated the xattr / fake-super tests (xattrs,
xattrs-hlink, chown-fake, devices-fake) to Linux because it used the
Linux-only os.*xattr. Restore them on macOS, FreeBSD, Cygwin and Solaris
via a per-OS xattr surface in rsyncfns.py (xattrs_supported / xattr_set /
xattr_dump):
* Linux -- os.*xattr
* macOS -- xattr
* FreeBSD -- setextattr / lsextattr / getextattr
* Cygwin -- getfattr / setfattr (from the `attr` package; CPython on
Cygwin has no os.*xattr)
* Solaris -- runat(1), with the script on stdin and the attr name/value
passed via the environment (the runat -c form mangles args)
Test attribute names are logical; the "user." namespace prefix is added
only on the Linux-style platforms (Linux, Cygwin). RSYNC_PREFIX/RUSR vary
per OS (macOS and Solaris use rsync.nonuser to avoid rsync's reserved
rsync.* space). The macOS and Cygwin workflows no longer skip these tests;
the FreeBSD/Solaris jobs use IGNORE skip-checking so need no change.
Verified on real Linux, macOS, FreeBSD, Cygwin and Solaris hosts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chmod-option: pin umask to the suite-wide 022 baseline (mirroring the
old rsync.fns) so rsync's --chmod `D+w` is computed and applied under
the same umask -- fixes failures under a different ambient umask (077).
* daemon module-list test: assert the `list = no` module does NOT leak
into the listing (the substring check alone missed regressions).
* claim_ports() lock file: open with O_NOFOLLOW and only fchmod a file we
O_EXCL-created, rejecting a symlink OR hard link planted at the
well-known /tmp path -- which, with the TCP tests running under sudo in
CI, could otherwise chmod an arbitrary root-owned target. Require a
pristine (regular, nlink==1) file.
* CI: extend the Linux/Cygwin expected-skip lists for the gated tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Daemon-mode tests default to the stdio-pipe transport (RSYNC_CONNECT_PROG),
which opens no listening socket -- so `make check` never exposes a network
service. Real TCP is opt-in via `runtests.py --use-tcp`, with the daemon
bound to loopback (127.0.0.1) on a claim_ports()-reserved port; CI runs the
suite both ways.
start_test_daemon() is the single seam every daemon test uses: the secure
pipe by default, a real rsyncd on a claimed loopback port under --use-tcp.
Tests with no pipe equivalent (the fake-proxy listener and the reverse-DNS
hostname-ACL daemon test) are gated behind require_tcp().
`make check` also now runs the suite in parallel by default (CHECK_J=8);
the claim_ports() byte-range locks make that safe across concurrent runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cross-compiles statically-linked rsync binaries with the Android NDK for
arm64-v8a (all modern phones) and armeabi-v7a (older 32-bit devices), and
uploads them as workflow artifacts for adb push / Termux use.
The build is self-contained (optional external libraries disabled; keeps
md5/md4 and the bundled zlib) and forces a few configure cache values
that can't be probed when cross-compiling: lchmod()/lutimes() off (Bionic
doesn't declare them until API 36 though the symbols link), and
socketpair / mknod-FIFO / mknod-socket on (Android runs a Linux kernel,
so these match the native result). IPv6 is enabled explicitly.
Since the binaries are cross-compiled the test suite can't run; the job
instead asserts each binary is static and the correct architecture, and
smoke-tests `--version` under qemu-user.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The chmod-symlink-race test was previously a no-op on Solaris,
OpenBSD, NetBSD, and Cygwin via a case 'uname -s' skip. The skip
was too broad: of the four scenarios the helper exercises, only
the 'legitimate within-tree dir-symlink' one actually needs
RESOLVE_BENEATH-equivalent kernel support. The other three
(attack rejection, plain relative path, top-level file) behave
identically on the per-component O_NOFOLLOW fallback and would
have caught the t_stub.c max_alloc=0 bug fixed in the previous
commit if the test had been allowed to run.
Make the helper probe the running kernel for either
openat2(RESOLVE_BENEATH) on Linux 5.6+ or openat(O_RESOLVE_BENEATH)
on FreeBSD 13+ / macOS 15+ by opening '.' under the requested
confinement. Honour the result:
- If RESOLVE_BENEATH-equivalent confinement is available, the
within-tree symlink scenario must succeed (status quo).
- If not, the per-component O_NOFOLLOW fallback rejects every
symlink including legitimate ones; expect the within-tree
symlink scenario to be rejected (rc != 0) and the file mode
to remain unchanged.
The attack-rejection, plain-path and top-level scenarios are
unchanged: they expect the same outcome on both code paths.
Drop the case-based skip from chmod-symlink-race.test so the test
runs everywhere and the per-component fallback gets the CI
coverage that the SunOS/OpenBSD/NetBSD/Cygwin runners can
provide. HPE NonStop -- which lacks RESOLVE_BENEATH but isn't in
the existing skip list -- is also covered by this change.
On an rsync daemon configured with "daemon chroot", the reverse-DNS
lookup of the connecting client was performed *after* the chroot
had been entered. If the chroot did not contain the files glibc
needs for resolution (/etc/resolv.conf, /etc/nsswitch.conf,
/etc/hosts, NSS service modules), the lookup failed and
client_name() returned "UNKNOWN". Hostname-based deny rules
("hosts deny = *.evil.example") therefore could not match, and
an attacker controlling their PTR record could connect from a
hostname the administrator had intended to deny. IP-based ACLs
were unaffected.
Do the reverse DNS lookup before chroot/setuid; client_name()
caches its result, so the post-chroot call uses the cached value
and hostname-based ACLs work even when DNS is unavailable
post-chroot.
Adds testsuite/daemon-chroot-acl.test as end-to-end regression
coverage. The test sets up an empty chroot directory, configures
"hosts deny = <localhost-resolved-name>" with daemon chroot, and
asserts the connection is refused with @ERROR access denied.
Uses unshare --user --map-root-user for non-root CAP_SYS_CHROOT;
skips cleanly on non-Linux or when user namespaces aren't
available.
Reporter: Joshua Rogers (MegaManSec).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cygwin lacks RESOLVE_BENEATH-equivalent kernel support and the
per-component O_NOFOLLOW fallback also can't be exercised meaningfully
under the cygwin runner's filesystem semantics, so every test that
asserts the secure_relative_open / do_*_at machinery actually blocks
the attack would skip. Make those skips expected in the workflow's
RSYNC_EXPECT_SKIPPED list:
- chdir-symlink-race
- chmod-symlink-race
- bare-do-open-symlink-race
- sender-flist-symlink-leak
- daemon-chroot-acl
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The default python3 on AlmaLinux 8 is 3.6, but runtests.py uses
subprocess.run(capture_output=...) and check_output(text=...) which
were introduced in 3.7. Install the python39 module stream and point
/usr/bin/python3 at it via alternatives so the existing shebang
resolves correctly.
Reproduced as: TypeError: __init__() got an unexpected keyword
argument 'capture_output' at runtests.py line 75.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The intent is to validate that future security fixes still build and
test cleanly on the oldest still-supported LTS releases of the two
mainstream Linux families, so backports can be developed against the
same CI surface as the trunk:
- ubuntu-22.04: oldest GitHub Actions runner image still available
(20.04 was retired in April 2025). Mirrors the existing
ubuntu-build.yml step list.
- almalinux-8: RHEL 8 rebuild, full support until 2029. Runs in an
almalinux:8 container on ubuntu-latest because GHA has no native
runner for the Fedora/RHEL family. Pulls libzstd/xxhash/lz4 dev
headers from PowerTools + EPEL; commonmark via pip for the man
page generator.
Both jobs follow the same paths-ignore convention as the other
workflows so a workflow-only change to one file won't fan out across
the whole CI matrix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test correctly skips on Cygwin (which lacks RESOLVE_BENEATH), but
the workflow's RSYNC_EXPECT_SKIPPED list still treats any change in
the skipped set as a CI failure. Add the new test name so the
skipped/got comparison matches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Solaris xls() function listed every entry in the file's xattr
directory, which on Solaris includes OS-managed SUNWattr_ro and
SUNWattr_rw pseudo-attributes. SUNWattr_rw embeds the file creation
time, so its bytes naturally differ between the source and destination
files, making the xattrs and xattrs-hlink tests fail with diffs that
have nothing to do with rsync.
Rsync's own listxattr wrapper already filters these out
(lib/sysxattrs.c), so the right fix is to filter them in the test
display too. Other platforms are unaffected because each has its own
xls() branch in the case statement.
With the test now actually passing on Solaris, drop the CI hack that
overwrote testsuite/xattrs.test with a skip stub.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the existing FreeBSD workflow for OpenBSD and NetBSD using
vmactions/openbsd-vm and vmactions/netbsd-vm so we get cross-BSD
coverage on push, PR, and the nightly schedule.
Also extend the FreeBSD and Solaris workflows to actually exercise the
test suite by running 'make check' after the build. The Linux, macOS,
and Cygwin jobs already did this.
The Solaris xattrs and xattrs-hlink tests are removed before 'make
check' because the Solaris SUNWattr_ro / SUNWattr_rw system attributes
leak into the test diff; that's a real rsync-on-Solaris issue to follow
up on, but skip the tests for now so the suite goes green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new simd-checksum test is skipped on platforms where SIMD
instructions are unavailable (macOS ARM, Cygwin). Add it to the
RSYNC_EXPECT_SKIPPED lists so CI doesn't fail on the mismatch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change the developer flow to not require updating the git-version repo
that the builds used to download a git-version.h file. The Actions now
do a full repo fetch so that the .h file can be generated via the git
history.
- Get rid of the gensend Makefile target that was used for the above.
- Get rid of the pre-push git hook file that called "Make gensend".
- Change the FreeBSD build to save an artifact with its built binaries.
[buildall]
The Linux fs.protected_regular sysctl setting could cause rsync to fail to write a file in-place with the O_CREAT flag set, so the code now tries an open without O_CREAT when it might help to avoid an EACCES error. A testsuite script is included (and slightly improved by Wayne to ensure that it outputs a SKIP when fs.protected_regular is turned off).
For a non-git build or for a git build w/o adequate git history, we now
allow the git-version.h file to be provided before the build. If the
file does not exist, we either create an empty file or put a define of
RSYNC_GITVER in it based on the output of git describe. The github
builds now snag the git-version.h file that was generated for the last
commit so that they all get the same version string, even with a shallow
checkout.