Compare commits

..

840 Commits

Author SHA1 Message Date
Zen Dodd
2d0ccacf3f ci: harden rsync-ssl socat fake helpers 2026-06-06 21:09:39 +10:00
Zen Dodd
f49fda1e4c ci: fix rsync-ssl socat test on OpenBSD 2026-06-06 21:00:51 +10:00
Zen Dodd
b76466bf4c ci: fix rsync-ssl socat test on BSD 2026-06-06 20:51:25 +10:00
Zen Dodd
1e8e388b3f rsync-ssl: add socat transport 2026-06-06 20:43:14 +10:00
Andrew Tridgell
6fad1d7d74 testsuite,ci: mark recv-discard-nullderef CI skip and tighten its check
The regression test honestly skips when it cannot force the receiver's
output mkstemp() to fail -- as root (root bypasses DAC) and on Cygwin
(chmod 0555 does not deny the owner a write). The ubuntu, ubuntu-22.04,
almalinux and macOS jobs run `make check` as root, and Cygwin can't
enforce the unwritable directory, so the test skips on all of them.
runtests.py fails a run on any skip-set mismatch, so add the test to
those jobs' RSYNC_EXPECT_SKIPPED lists; the BSD/Solaris jobs run as root
too but enforce no expected-skip set, so they need no change.

Also tighten the pass condition. The post-chmod writability probe already
guarantees the receiver discards (mkstemp must fail), so an exit 0 would
mean the file actually transferred and the discard path was never
exercised -- a silent false-pass. Require exactly exit 23 (the forced
discard leaves the file untransferred); 12 remains the pre-fix crash.
2026-06-06 18:56:51 +10:00
pterror
b8562dbf4a testsuite: regression for the receiver discard-path NULL deref
Drives a real sender<->receiver pair (client sender -> daemon receiver,
both the binary under test in the default pipe transport) so the receiver
actually takes the recv_files discard path -- a local `rsync a b` does
not. The basis and source share a leading block so the generator emits
real sums and the receiver gets a block MATCH; the destination directory
is made unwritable so the receiver's output mkstemp() fails and it
discards the delta. Pre-fix the receiver SIGSEGVs in full_fname(NULL),
which the client sees as a protocol-data-stream error (code 12); post-fix
it drains the delta and reports a benign code 23 (or 0).

Skips (exit 77) when run as root, since root bypasses DAC and the
unwritable destination would not make mkstemp() fail -- so the discard
path, and the bug, would never be reached.

Verified red-on-buggy / green-on-fixed against the 0d0399bb receiver.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 18:56:51 +10:00
pterror
d66846351d receiver: fix NULL deref on the delta discard path
receive_data() crashed a receiver that was merely DISCARDING a file's
delta stream. discard_receive_data() calls receive_data() with
fname == NULL and fd == -1, so size_r == 0 and mapbuf == NULL. A normal
block-MATCH token (against a block the basis and source share) then
reaches the !mapbuf branch added in 31fbb17d ("receiver: fix absolute
--partial-dir delta resume"), which calls full_fname(fname). full_fname()
dereferences its argument unconditionally (util1.c: `if (*fn == '/')`),
so fname == NULL faults there -> receiver SIGSEGV.

This is a normal-operation crash with a stock cooperating sender, not an
adversarial one. The generator hands the sender real block sums whenever
the basis is readable and we're in delta mode; the receiver only decides
to discard afterwards, when its output cannot be produced -- e.g. the
destination directory is not writable (mkstemp fails), the basis turns
out to be a directory, or a --partial-dir resume is skipped. A MATCH
token arriving during that discard hit the NULL deref.

The 31fbb17d branch is correct only for a REAL output transfer (fd != -1,
fname valid): there, a block match with no mapped basis is a genuine
protocol inconsistency (the generator promised a basis the receiver could
not open), and honoring it would silently omit those bytes from the
verification checksum or leave a hole, so hard-erroring -- and
full_fname(fname) -- is right. It conflated that with the discard path.

The discriminator is fd, not mapbuf: on the discard path fd == -1 always;
on the real-output inconsistency fd != -1. Scope the "no basis file"
protocol error to fd != -1 (where fname is non-NULL and full_fname is
safe) and, on the discard path (fd == -1), absorb the matched bytes
benignly (offset += len; continue) -- symmetric with the literal-token
handling just above, and restoring the pre-31fbb17d behavior. The
real-transfer inconsistency check is preserved unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 18:56:51 +10:00
Andrew Tridgell
bad790dd2e fleettest: add a per-target max_retry budget for flaky tests
A slow or heavily-loaded fleet box can occasionally flake a concurrency-
sensitive test (e.g. a daemon/lsh test under -j8 on a nested-VM Solaris box).
Rather than dropping the whole target to a lower -j, add a per-target
"max_retry" property: after a run, each failed test is re-run on its own up to
max_retry more times, and any that then pass are dropped from the failure list.
Recovered tests are listed in a new "RECOVERED" report section, so a flake is
surfaced, never silently hidden.

Applies to every pass for the target (pipe, tcp, protoNN, nonroot).  Default 0
keeps the current no-retry behaviour.
2026-06-06 16:41:51 +10:00
Zen Dodd
c67d1935d8 docs: fix option summary inconsistencies 2026-06-06 16:11:30 +10:00
Zen Dodd
1d6770edbc ci: test uninstall targets 2026-06-06 16:07:20 +10:00
Zen Dodd
135f2eca01 testsuite: correct files-from comment coverage 2026-06-06 14:54:55 +10:00
Zen Dodd
6bfb487155 testsuite: cover files-from comments 2026-06-06 14:54:55 +10:00
Zen Dodd
b1d68089e5 docs: describe files-from comments 2026-06-06 14:54:55 +10:00
Zen Dodd
a850e5d57e testsuite: cover groupmap empty source matching 2026-06-06 14:29:15 +10:00
Zen Dodd
6e3f17cea2 docs: clarify empty name groupmap matching 2026-06-06 14:29:15 +10:00
Zen Dodd
4a55da168c docs: clarify batch compression limits 2026-06-06 14:22:51 +10:00
Zen Dodd
7639ce4607 configure: avoid runtime IPv6 availability probe 2026-06-06 14:21:07 +10:00
Zen Dodd
0d31a20845 docs: mention systemd rsync daemon units 2026-06-06 14:17:41 +10:00
Zen Dodd
a5a7500707 build: fix rrsync manpage fallback 2026-06-06 14:17:00 +10:00
Andrew Tridgell
24b44290ab fleettest: add per-target protocol passes (check30/check29)
A target can list older "protocols" (e.g. [30, 29]) in the fleet config;
each runs as an extra stdio-pipe pass with runtests --protocol=N, the fleet
analogue of a workflow's check30/check29 steps. The passes reuse the same
parsed RSYNC_EXPECT_SKIPPED list as the default pipe run and appear as protoNN
columns in the report and --timing breakdown. Targets without the key run only
the default protocol and show "-" there.

The example config's ubuntu-2604 target (mirroring ubuntu-build.yml, which has
check30/check29 steps) now sets protocols: [30, 29].
2026-06-06 10:36:13 +10:00
SebMtn
0d0399bb14 rrsync: add -absolute argument to support calling rsync with absolute path
Signed-off-by: SebMtn <102696928+SebMtn@users.noreply.github.com>
2026-06-05 16:01:44 +10:00
Miao Wang
c1d7b5c6f9 receiver: try to chmod the target file when denied opening
When the target file exists but its permission modes prevent us from
opening it for writing, we can try first to chmod it and then open it.
2026-06-05 14:31:46 +10:00
Mike-Goutokuji
24e3d4d83c Always clear st out and validate nanoseconds before using it
Otherwise we get errors.
Fixes: https://github.com/RsyncProject/rsync/issues/927
2026-06-05 12:28:29 +10:00
Andrew Tridgell
9df00b6dc3 testsuite: regression for #880 --mkpath --dry-run file-to-file
Covers both halves: a --mkpath file-to-file --dry-run must succeed and
match the real run (the #880 abort), and a plain file-to-file --dry-run
onto an existing differing destination must still itemize the real change
rather than report it as brand new.  Both compare "--dry-run -i" output
against the real run.

Co-authored-by: Stiliyan Tonev (Bark) <stiliyan21@gmail.com>
2026-06-05 11:51:30 +10:00
Andrew Tridgell
3cd70a3761 main: fix --mkpath + --dry-run file-to-file copy (#880)
A single-file --mkpath copy whose destination parent does not exist
failed under --dry-run: make_path() only *reports* the directories it
would create in a dry run, so change_dir#3 then tried to chdir into a
parent that isn't there and aborted with "change_dir#3 ... failed".

When the parent is genuinely missing in a dry run, skip the chdir and
mark the destination as not-yet-present (dry_run++), exactly as the
multi-file/dir-creation path already does, so the generator doesn't
probe the missing tree.  Gating it on the missing-parent case keeps an
ordinary file-to-file dry run chdir'ing into and itemizing against an
existing destination.

Fixes: #880

Co-authored-by: Stiliyan Tonev (Bark) <stiliyan21@gmail.com>
2026-06-05 11:51:30 +10:00
Andrew Tridgell
981ba2a7b1 Drop stale "redo manual as SGML" TODO entries
The SGML manual idea is long dead (man pages are markdown now, and the
DocBook source was just removed). Remove both TODO mentions.
2026-06-05 11:09:36 +10:00
Andrew Tridgell
5de07c13c1 Remove obsolete DocBook manual
doc/rsync.sgml is a 1996-2002 DocBook user manual (with README-SGML
describing the docbook-utils build) that was long ago superseded by the
markdown man pages. It is unmaintained and referenced by nothing in the
build. This empties doc/.
2026-06-05 11:09:36 +10:00
Andrew Tridgell
a2ce82b35e Remove obsolete design notes
rsync3.txt and rsyncsh.txt are Martin Pool's 2001 design proposals
("notes towards a new version of rsync", an interactive rsync shell),
neither of which reflects the current implementation. doc/profile.txt is
stale profiling notes. None are referenced by the build, tests, or docs.
2026-06-05 11:09:36 +10:00
Andrew Tridgell
5e88945a3c Remove obsolete testhelp/maketree.py
This Python 2 test-tree generator (print statements, string.letters,
.next()) has been broken on modern Python for years and is referenced
nowhere in the build, tests, or any script. Drop it.
2026-06-05 11:09:36 +10:00
Zen Dodd
fb7daf02f6 fix: daemon upload delete stats 2026-06-05 11:06:48 +10:00
Andrew Tridgell
c5b7ea0bd2 token: drain the matched-block insert deflate (#951)
send_deflated_token() adds a matched block to the compressor history with
deflate(Z_INSERT_ONLY).  Our bundled zlib implements Z_INSERT_ONLY (it
produces no output and consumes the input in one call), but a build
against a system zlib lacks it and falls back to Z_SYNC_FLUSH (see the top
of the file), which emits a flush block into obuf.  For a large
incompressible matched token that block exceeds AVAIL_OUT_SIZE(CHUNK_SIZE),
so deflate returned with avail_in != 0 and the transfer aborted:

    "deflate on token returned 0 (N bytes left)"  at token.c

The insert output is never sent -- the receiver rebuilds the matching
history itself in see_deflate_token() -- so loop, resetting the output
buffer, and discard it.  Drain with the same condition as the data loop
above: until the input is consumed AND avail_out != 0.  Stopping at
avail_in == 0 alone can leave pending output in the deflate stream (a
full output buffer with bytes still buffered), which would then be emitted
by the next real deflate send and corrupt the stream.  A bundled-zlib
build still finishes in one iteration.

Fixes: #951
2026-06-05 10:38:03 +10:00
Zen Dodd
0b08fa4285 fix: install generated manpages out of tree 2026-06-05 09:39:21 +10:00
Zen Dodd
cb44fc5f1b fix: update skips different file type 2026-06-05 09:39:09 +10:00
Andrew Tridgell
eb3796a8c5 ci: add ubuntu-latest fleettest workflow against a localhost fleet
fleettest is a developer tool meant to run on a modern Ubuntu box, so a
bitrot check belongs in its own ubuntu-latest job rather than in the
testsuite (which runs on the BSD/Solaris/macOS/Cygwin matrix, whose
older Pythons may not even parse it).

The job sets up passwordless ssh to localhost, writes a two-target
fleet config that both ssh to localhost (distinct build dirs), and runs
a real fleettest pass. Two targets exercise the parallel multi-target
path and the per-run dir / port isolation; the run exits 0 only if
every cell is OK. Triggered on changes to fleettest.py or this
workflow, manually, and weekly.
2026-06-05 08:48:17 +10:00
Andrew Tridgell
571f87dd12 fleettest: add --timing to show per-target wall-clock
Records wall-clock per phase (push, build, each test transport, nonroot)
plus a total in TargetResult, and with --timing prints a breakdown after
the report, sorted slowest-target-first. Targets run in parallel, so the
run is gated by the slowest one; the phase columns show whether that
hold-up is the push, the build, or a test pass. A target that failed
early (no total) falls back to the sum of the phases it reached.
2026-06-05 08:48:17 +10:00
Andrew Tridgell
ea866650be fleettest: tighten --cleanup sweep scope and rm hardening
Address review findings on the cleanup paths:

- --cleanup no longer removes a bare <builddir>, only the suffixed
  <builddir>-* run dirs it created. This keeps the sweep within its
  documented scope and avoids clobbering an unrelated tree.

- Add _unsafe_builddir(): reject empty/root/$HOME and any absolute path
  directly under / (e.g. a misconfigured builddir of "/tmp") before
  building a destructive command, in both cleanup paths.

- Use `rm -rf --` so a path with a leading dash can't be read as options.

- Soften the docs: run-dir removal on Ctrl-C/kill is best-effort (a
  signal arriving mid-push can still leave a remnant for --cleanup).
2026-06-05 08:48:17 +10:00
Andrew Tridgell
c7c0109944 fleettest: isolate concurrent runs and add config/cleanup options
Each run now builds in its own randomly-named dir on every target
(<builddir>-<run_id>), so two or three fleettest runs can share the same
fleet without colliding on the pushed tree, the build, or the testtmp
scratch. Port collisions were already handled by claim_ports() locks.

The run dir is removed when the run ends -- on success, failure, or
Ctrl-C/kill (atexit + SIGINT/SIGTERM handlers); --keep retains it. A new
--cleanup mode sweeps stray <builddir>-* dirs left by a SIGKILL.

Incremental builds are dropped (every run is a fresh dir + full build):
--no-push removed, --clean removed.

Also look for the fleet config at ~/.fleettest.json first, then
testsuite/fleettest.json (still overridable with --fleet PATH).
2026-06-05 08:48:17 +10:00
Andrew Tridgell
ac282725cd testsuite: regression for the #829 daemon --chown/--groupmap wildcard
Maps every source group to a second group the test user belongs to via a
daemon upload (--groupmap='*:GID') and checks the wildcard took effect.
Runs both arg modes: the default path (the '*' is safe_arg-escaped and the
daemon must un-backslash it -- the regression) and --secluded-args (the '*'
is sent raw over the protected channel, a guard that the fix left that path
alone).  Needs no root -- a non-root receiver can chgrp to a member group --
and was verified RED on a pre-fix binary (the escaped '\*' is ignored, gid
unchanged) and GREEN after the fix.
2026-06-05 06:35:12 +10:00
Andrew Tridgell
6777170037 daemon: un-backslash escaped option args (#829)
Without --secluded-args, the client's safe_arg() backslash-escapes shell
and wildcard chars in option values before sending them to the server, so
--chown's --usermap=*:user is transmitted as --usermap=\*:user.  Over ssh a
remote shell removes the backslashes before rsync parses the args, but a
daemon has no shell and read_args() stored option args verbatim -- so the
receiver saw the literal "\*", the usermap/groupmap wildcard never matched,
and the module's configured uid/gid won instead.  A regression from the
secluded-args hardening; rsync 3.2.3 (protocol 31) worked.

Un-backslash option args in read_args() on the daemon's first
(non-protected) read, mirroring what the ssh-side shell does.  File args
after the dot are already handled by glob_expand(); the protected (NUL,
already-unescaped) re-read and the server's stdin read pass unescape=0 so
their raw args are left untouched.

Fixes: #829
2026-06-05 06:35:12 +10:00
Andrew Tridgell
b3107260a2 build: fall back to do_mknod() when mknodat() is unavailable (#896)
do_mknod_at() (the symlink-race-safe variant used by a non-chrooted
daemon receiver) calls mknodat()/mkfifoat(), but the at-variant was
gated only on AT_FDCWD.  Older Darwin declares AT_FDCWD without
mknodat(), so the build failed with "mknodat undeclared".

Probe mknodat()/mkfifoat() in configure and require HAVE_MKNODAT for the
at-variant; without it do_mknod_at() falls back to do_mknod(), exactly
as it already does where AT_FDCWD is missing.  Linux keeps the mknodat
path since HAVE_MKNODAT is defined there.

Fixes: #896
2026-06-05 06:35:12 +10:00
Andrew Tridgell
7db73ad9a1 alloc: revert "zero all new memory from allocations" (#959)
Commit d046525d made my_alloc() calloc every fresh allocation and made
expand_item_list() memset the freshly grown tail, to hand out predictably
zeroed memory.  But that forces the kernel to back pages callers never
touch: each per-directory file_list pre-allocates a FLIST_START-entry
(32768) pointer array -- 256KB -- and calloc now zeroes the whole array
even for an empty directory.  With incremental recursion over many
directories the resident set explodes; 80000 empty dirs went from ~336MB
to ~10.8GB.

Restore the pre-d046525d malloc/calloc split: fresh allocations use
malloc (so untouched tails stay lazy) and only explicit do_calloc
requests (new_array0) are zeroed.  Callers that need zeroed memory
already ask for it, and the full test suite passes.

Fixes: #959
2026-06-05 06:35:12 +10:00
Andrew Tridgell
3691b719fa testsuite: regression for short-checksum --append-verify s2length
Forces --checksum-choice=xxh64 (an 8-byte transfer checksum) with a
corrupted-prefix --append-verify so the full-checksum redo path runs.
Before the generator capped s2length at MIN(SUM_LENGTH, xfer_sum_len)
this died with "Invalid checksum length 16 [sender]"; the test is RED on
the prior generator and GREEN with the cap.  Reproduces on any build that
has xxhash, so it guards the fix without an old-libxxhash host; skips when
xxh64 is absent (a build without xxhash).
2026-06-04 14:33:20 +10:00
Andrew Tridgell
fe946581ba generator: cap block s2length at the negotiated checksum length
sum_sizes_sqroot() capped the strong-sum length at SUM_LENGTH (16), the
legacy MD4/MD5 digest size.  Since 0902b52f the sum2 array elements are
xfer_sum_len bytes and the sender rejects a sums header whose s2length
exceeds xfer_sum_len.  When the negotiated transfer checksum is shorter
than 16 bytes -- xxh64 (8), used when the build's libxxhash lacks
xxh128/xxh3 (e.g. Ubuntu 20.04) -- the generator still emitted s2length
up to 16, so --append-verify and other full-checksum (redo) transfers
died with "Invalid checksum length 16 [sender]" (protocol incompatibility).

Cap s2length at MIN(SUM_LENGTH, xfer_sum_len): unchanged for any checksum
>= 16 bytes (md5/xxh128/sha1), corrected for short ones.  Also closes a
latent over-read of the xfer_sum_len-sized digest buffer.
2026-06-04 14:33:20 +10:00
Andrew Tridgell
4634b0ada7 android: probe openat2 usability behind a SIGSYS handler
Android's seccomp sandbox traps openat2() with SECCOMP_RET_TRAP, which
raises SIGSYS and kills the process instead of returning ENOSYS, so the
secure resolver cannot simply try openat2() and inspect errno.  Add
openat2_usable() in a new android.c: it probes openat2() once behind a
temporary SIGSYS handler and caches the result.

Gate every SYS_openat2 call on openat2_usable(): in the resolver via an
openat2_beneath() wrapper, and in t_chmod_secure's kernel probe directly,
so a blocked openat2 reports ENOSYS and the caller falls back to the
portable O_NOFOLLOW resolver.  Only openat2 is gated -- a plain openat()
(e.g. opening an operator-trusted absolute basedir) is left free.

The probe body compiles only on Android -- __ANDROID__ is a Bionic target
macro, so it is set for NDK cross-builds and native Termux alike and unset
everywhere else, where openat2_usable() collapses to a constant 1.  Link
android.o into the secure-resolver test helpers too so their self-tests
survive on Termux.

Adapted from PR #909.
2026-06-04 13:41:07 +10:00
Andrew Tridgell
83a24c2117 configure: require <linux/openat2.h>, not just SYS_openat2
The openat2 secure resolver in syscall.c needs struct open_how and
RESOLVE_BENEATH from <linux/openat2.h>, not only the SYS_openat2 syscall
number.  Some setups expose the syscall number via glibc without the
kernel header present, so probing SYS_openat2 alone still left the build
broken (#905).  Exercise the header and struct in the configure check so
HAVE_OPENAT2 is defined only when both are actually usable.
2026-06-04 13:41:07 +10:00
Markus Mayer
39aa750b1c t_chmod_secure: use HAVE_OPENAT2 to check for openat2() support
To prevent using openat2() in situations where it is not supported, use
    #if defined(__linux__) && defined(HAVE_OPENAT2)
in t_chmod_secure.c, just like it was already being done in syscall.c.

Signed-off-by: Markus Mayer <mmayer@broadcom.com>
2026-06-04 13:41:07 +10:00
Markus Mayer
c73e0063b7 build: auto-detect the presence of the openat2() syscall
Let configure detect if the openat2() syscall is supported by the kernel
headers we are building against. Do not attempt to use openat2() if
support is not present.

Users can still disable using the openat2() syscall manually if so
desired.

Signed-off-by: Markus Mayer <mmayer@broadcom.com>
2026-06-04 13:41:07 +10:00
Andrew Tridgell
09656e19c1 testsuite: add fleettest.py fleet CI harness
fleettest.py builds the committed HEAD of a checkout on a fleet of remote machines over ssh and runs the test suite under both the stdio-pipe and --use-tcp transports in parallel, reporting only the unexpected results. Each target mirrors a .github/workflows/*.yml job: its configure flags, and the RSYNC_EXPECT_SKIPPED list parsed from the workflow.

The fleet is described by a JSON file (testsuite/fleettest.json, git-ignored); fleettest.json.example is a worked template. Use --fleet to point at another config and --repo to build a tree other than the current directory.

A target with nonroot:true reruns, as the unprivileged ssh user, the tests that declare a module-level fleet_nonroot=True (here ownership-depth and daemon). The set lives in the test files, so new privilege-sensitive tests join the non-root pass with no fleet-config change.

Also rename testsuite/README.testsuite to README.md and rewrite it as markdown documenting the current testsuite: runtests.py, the make check/check29/check30/installcheck/coverage targets, the result/exit-code conventions, and fleettest.py.
2026-06-04 13:00:04 +10:00
Andrew Tridgell
5972ebdaf8 syscall/receiver: honour a relative alt-basis dir on a daemon receiver (#915)
The symlink-race hardening routed the receiver's basis open through
secure_relative_open(), which rejects any '..' -- so a sibling
--link-dest=../01 on a use-chroot=no daemon was silently ignored and every file
re-transferred (#915/#928, a regression from 3.4.1).

Narrow the confinement to the sanitizing daemon (am_daemon && !am_chrooted) and
re-anchor it at the module root, the real trust boundary: secure_relative_open()
prefixes the cwd's module-relative path (from rsync's logical curr_dir[], a
guaranteed lexical prefix of module_dir) and resolves beneath module_dir, so
RESOLVE_BENEATH permits an in-module '..' climb while still rejecting one that
escapes the module.  secure_basis_open() opens with a bare do_open() in the
non-sanitizing cases.  t_stub.c gains weak curr_dir[]/curr_dir_len for the
helpers (via #pragma weak on non-GNU compilers, where rsync.h erases
__attribute__).

Two tests: link-dest-relative-basis asserts the in-module '..' is honoured;
link-dest-module-escape asserts a --link-dest=../../OUTSIDE climb that leaves
the module is refused (not hard-linked to an outside file).  See upstream
PR #930.
2026-06-04 07:41:41 +10:00
Andrew Tridgell
489f3e4521 sender: open a module-root-absolute path for a path = / module (#897)
A daemon module with path=/ makes F_PATHNAME absolute, so the secure_path built
for the content open starts with '/'.  secure_relative_open() rejects an
absolute relpath with EINVAL, so a use-chroot=no daemon with path=/ could not
send any file ('failed to open ...: Invalid argument (22)') -- a regression
from 3.4.2.  Strip leading slashes to a module-relative path; resolution stays
confined beneath module_dir.
2026-06-04 07:41:41 +10:00
Andrew Tridgell
ebfb3c0056 flist: accept the missing-args mode-0 entry in recv_file_entry (#910)
--delete-missing-args (missing_args==2) sends a missing --files-from arg as a
mode-0 entry (IS_MISSING_FILE), the generator's delete signal.  The mode-type
validation in recv_file_entry() rejected mode 0 as an invalid file type,
aborting the transfer with 'invalid file mode 00 ... code 2' before the
generator could act (a regression from 3.4.1).  Allow mode 0 through only when
missing_args==2 (the delete mode -- not --ignore-missing-args, which never
sends a mode-0 entry); all other modes are still rejected.
2026-06-04 07:41:41 +10:00
Andrew Tridgell
e16a001d39 testsuite/runtests: count XFAIL (exit 78) as expected, not a failure
The regression tests use test_xfail() (exit 78) to assert a known, documented
residual on platforms where the fix can't apply -- e.g. link-dest-relative-basis
XFAILs where the receiver has no openat2/O_RESOLVE_BENEATH and the portable
resolver rejects the '..' for safety.  runtests.py counted exit 78 in the
generic else->failed branch, so a bare XFAIL failed the whole suite; tally it
separately ('N xfailed (expected)') and exclude it from the failure exit code.
Also add --race-timeout plumbing (race_timeout env) for race tests.
2026-06-04 06:09:25 +10:00
Michael Mess
5cf7c50524 Corrected test case broken for locales that uses , instead of . for decimal numbers in human readable form. 2026-06-02 18:23:40 +10:00
Andrew Tridgell
ad3bfab05d ci: version-mixing workflow, expect manifests, check-progs target
Adds .github/workflows/ubuntu-version-mix.yml (ubuntu-latest) and a
per-release manifest testsuite/expect/rsync_<ver>.expect for each of the
nine peers. The workflow builds the current rsync, then runs the two-
sided suite against every old binary over both the pipe and --use-tcp
daemon transports. All peers run in a SINGLE looped job (not a matrix)
so the PR shows one check line; each peer/transport is a foldable log
group and a failure annotates which one broke.

A new phony `check-progs` target builds rsync plus the test helper
programs and check symlinks without running the suite -- the build half
of `make check` -- so the workflow's direct runtests.py invocation has
the helpers it needs.

Notable expected results encoded in the manifests:
 - The four May-2026 security tests xfail against every released peer:
   the suite demonstrates each release is vulnerable to those findings
   while current master is fixed.
 - symlink-dirlink-basis xfails on 3.4.0/3.4.1 (issue #715: their
   secure_relative_open O_NOFOLLOW-confines the basedir, breaking a -K
   dir-symlink update; current master fixes it with secure_basis_open).
 - Older peers carry more xfails for options/negotiation they lack;
   2.6.0 (protocol 27) fails most daemon tests. reverse-daemon-delta
   passes against all peers, confirming backward compat down to 2004.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:21:35 +10:00
Andrew Tridgell
e8d10dc2ad old_versions: commit static binaries of old rsync releases
Nine statically-linked, stripped binaries for the version-mixing test
suite (and ad-hoc cross-version behaviour checks): every x.y.0 release
from 2.6.0 (2004, protocol 27) through 3.4.0, plus the 3.1.3/3.2.7/3.4.1
point releases. 2.6.0 is the practical floor; older tags need more
porting to build on a current toolchain.

build_static.sh rebuilds any release from its git tag, applying the
minimal patches needed to compile old sources on a modern toolchain:
K&R lseek64 redecl, gettimeofday, -std=gnu11, --disable-openssl, and
_FORTIFY_SOURCE disabled (modern FORTIFY=3 turns latent benign over-reads
in old rsync into aborts when it runs as a server). Pre-3.0 trees ship
configure.in, so it regenerates configure (autoheader/autoconf) after
neutralizing the dead AC_LIBOBJ replacement fallbacks, generates proto.h,
and stubs the dropped vendored lib/addrinfo.h -- all guarded to no-op on
newer versions.

.gitattributes marks the binaries binary (so the text=auto rule can't
corrupt them) and export-ignore (kept out of the release tarball).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:21:35 +10:00
Andrew Tridgell
ad14569561 testsuite: reverse-direction smoke test (old client -> current daemon)
Every other two-sided test drives with the current binary, covering
new-client -> old-server. This adds the backward-compat direction that
matters most for a project shipping new servers to a world of old
clients: a current daemon must keep serving the installed base of old
rsync clients.

reverse-daemon-delta_test.py starts the daemon with the current build
(via start_test_daemon's rsync_cmd override) and drives it with the old
binary. It does a push and a pull, each with and without -z, with the
receiving side pre-seeded with an older version of the file so the delta
algorithm actually runs -- exercising delta encoding both ways (old->new
on push, new->old on pull) and compression negotiation both ways. It
asserts the bytes crossing the wire are far smaller than the file, so a
silent fallback to a whole-file copy is caught, and accepts both the
modern "sent/received" and the old "wrote/read" summary wording so an
old client's output parses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:21:35 +10:00
Andrew Tridgell
e21cdabd71 runtests: add --rsync-bin2 / --expect-result for version-mixing tests
Let the suite run with two rsync binaries so the current build can be
tested against the actual old code of a previous release, rather than
only forcing the current binary to speak an old protocol (check29/30).

  --rsync-bin2 PATH  exports RSYNC_PEER, the binary used for the SERVER
                     side of two-sided transfers (the daemon process and
                     the remote-shell --rsync-path target). Defaults to
                     RSYNC, so single-binary runs are byte-for-byte
                     unchanged.
  --expect-result F  the manifest's listed tests ARE the run set; each
                     test's actual outcome (pass/skip/fail/xfail) is
                     compared to its expected one and any mismatch --
                     including an unexpected pass (xpass) -- fails the
                     run. --expect-skipped and the default exit logic
                     are untouched.

rsyncfns gains the RSYNC_PEER global and launches the daemon with it
(start_rsyncd / start_test_daemon, the latter with an optional rsync_cmd
override used by the reverse-direction test); the remote-shell tests
pass --rsync-path={RSYNC_PEER}. All no-ops when no peer is selected.

Direction is fixed: the current binary always drives (only it
understands the new test scripts); the old binary is only ever the
server/daemon side.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:21:35 +10:00
Andrew Tridgell
c0219caf15 runtests: add --exclude / RSYNC_EXCLUDE to skip tests entirely
Some tests cannot run in certain build/CI environments. In particular the
protected-regular test self-re-execs under "unshare --map-users" to exercise
fs.protected_regular handling, and that user-namespace path hangs in a
restricted buildd chroot (e.g. Launchpad/sbuild), tripping the per-test
timeout and failing the whole "make check".

Add an --exclude option (comma-separated test names/globs), with an
RSYNC_EXCLUDE environment fallback so it can be set without touching the
make/check command line. Excluded tests are dropped before running -- they
are neither executed nor reported as skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 17:52:42 +10:00
Andrew Tridgell
68df17ae00 docs: document the rsync-latest snapshot PPA
Add the new ppa:rsyncproject/rsync-latest (development snapshots rebuilt
from git master) alongside the existing stable PPA in INSTALL.md and the
download page.  Notes that snapshot versions (3.5.0~git...) sort below the
matching stable release, so the two PPAs can coexist without a stable
release being silently replaced by a snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:37:10 +10:00
Andrew Tridgell
3748c3288d testsuite: added a test for symlinks to the same dir
when a symlink is to the same directory as the source then it can be
considered unsafe if it goes via a path outside the directory.

This came up on the mailing list, added a test to make the case clear
2026-05-31 18:42:37 +10:00
Andrew Tridgell
907505c004 ci: halve CI artifact retention from 90 to 45 days
GitHub Actions artifact storage is approaching our quota. Each `make`/build
job uploads its rsync binary + manpages, the coverage job uploads its full
HTML tree, and Android uploads its dist/ -- 11 jobs producing artifacts per
PR/push, all kept for the repo default of 90 days.

Set retention-days: 45 explicitly on every upload-artifact step so they
expire at half the previous lifetime; older artifacts can still be re-built
from the commit if needed. No other workflow behaviour changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 05:44:14 +10:00
Andrew Tridgell
8bbea98392 runtests.py: accept a relative --rsync-bin
Tests are launched with subprocess.run(..., cwd=TOOLDIR) so the
subprocess's argv[0] resolves against TOOLDIR, not the runner's
invocation cwd. A user-supplied --rsync-bin=../foo/rsync therefore
worked when invoked from inside TOOLDIR but silently failed (or
ENOENT'd inside individual tests) when invoked from a sibling
directory.

Fix: absolutize rsync_bin via os.path.abspath() at parse time, before
it propagates into build_rsync_cmd()/RSYNC. abspath() captures
os.getcwd() now, which is the operator's invocation cwd -- exactly
what the --rsync-bin=../path form expresses.

Regression check:

  cd /tmp/somewhere-else
  ln -s /path/to/rsync ./alt/rsync
  python3 /path/to/rsync-git/runtests.py \
      --rsync-bin=./alt/rsync \
      --srcdir=/path/to/rsync-git --tooldir=/path/to/rsync-git \
      00-hello

Before this commit the test failed at subprocess time with the relative
path being looked up under TOOLDIR; after, it passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:00:24 +10:00
Andrew Tridgell
f2eef1f0d2 ci: add actionlint workflow to lint GitHub Actions YAML
Adds .github/workflows/actionlint.yml which runs rhysd/actionlint over
.github/workflows/*.yml on push and PR to master.  Triggers only when
something in .github/workflows/ (or the actionlint config) changes, so
the rest of the platform matrix isn't billed when nothing here moves.

The job downloads a pinned actionlint binary (1.7.12) via the upstream
download script (which verifies a SHA256) -- no third-party Action
dependency, matching the inline-install style of the existing
ubuntu/macos/cygwin workflows.  Bump the pinned version deliberately.

actionlint catches a) GitHub Actions expression / type errors, b)
unsupported runner images, c) missing secrets / inputs, and d) the
embedded shellcheck class of issues in 'run:' scripts that the previous
commit cleaned up.  Keeping it in CI prevents regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 06:46:08 +10:00
Andrew Tridgell
d395d8df06 ci: clean up workflow shellcheck nits
actionlint (rhysd/actionlint) reported a handful of shellcheck-class issues
across the GitHub Actions workflows.  All are 1-line mechanical fixes:

  * Replace legacy backticks in --rsync-bin=`pwd`/rsync with
    --rsync-bin="$PWD/rsync" (SC2006 + SC2046; almalinux-8-build,
    macos-build, ubuntu-22.04-build, ubuntu-build).
  * Quote >>$GITHUB_PATH redirects as >>"$GITHUB_PATH"
    (SC2086; coverage, macos-build, ubuntu-22.04-build, ubuntu-build).

After this commit `actionlint .github/workflows/*.yml` exits 0.

(Also cleaned up 6 editor backup *.yml~ files from the local working
tree; those weren't tracked -- *~ is gitignored -- so the cleanup is
local-only and not part of this commit.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 06:46:08 +10:00
Andrew Tridgell
14d6c29d81 testsuite: close minor assertion gaps
symlink-dirlink-basis  assert the --backup file holds the pre-update content,
                         not merely that the backup file exists.
  acls-default           check that clearing the inherited default ACL actually
                         succeeded, so the no-default-ACL cases can't silently
                         test against the scratch dir's seeded default ACL.
  alt-dest               assert --copy-dest produces a distinct inode from the
                         alt-dir candidate (a copy, not a hard link) -- the
                         property that distinguishes it from --link-dest, which
                         checkit's tree comparison alone doesn't capture.

(crtimes' "independently pin the historical create time" gap is left as-is: the
touch-trick pinning is APFS-specific and not locally verifiable, and a mistuned
probe would make the test skip on macOS and break its expected-skip set.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
34f40b9ea7 testsuite: tighten metadata-precision and symlink-target assertions
Replace loose/partial oracles with exact ones:

  omit-times      under -O, require EVERY directory mtime to be omitted, not
                  just one (the old "at least one differs" missed partial bugs).
  dir-sgid        assert the created dirs' actual gid: a setgid parent makes
                  them inherit its group (set to a secondary group to be
                  discriminating), while the non-setgid case gets the process's.
  relative-implied pin a deterministic umask and assert the exact default mode
                  (0o755) for --no-implied-dirs, not merely "not the source's".
  safe-links /    compare the preserved symlink TARGET strings via readlink,
  unsafe-links    not just that a symlink exists.
  preallocate     verify do_punch_hole via st_blocks on the --inplace --sparse
                  case (guarded by a sparse-capability probe).

Note: --preallocate --sparse leaves the file fully allocated on a fresh write
(the zero run is not punched), so that case stays content-only rather than
asserting hole-punching -- see the test comment; rsync.1's claim that the
combination yields sparse blocks does not hold for the fresh-write path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
5b36673d0a testsuite: add content and return-code assertions
Several tests proved only that rsync exited cleanly (or that a file merely
exists), so a no-op/short transfer would pass:

  protected-regular  compare the dst bytes to the source after --inplace.
  00-hello           re-assert one/two were copied on the RSYNC_OLD_ARGS=1
                     env-var path (the explicit --old-args case already did).
  missing            check the dry-run's exit status in test 1.
  mkpath             compare transferred bytes (not just existence) and add a
                     negative control: a transfer WITHOUT --mkpath must fail
                     and create no intermediate path.
  size-filter        compare each kept file's content to its source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
1687230672 testsuite: verify destination content/listings in daemon tests
These daemon tests confirmed refusals/exclusions but accepted the allowed
transfers on exit status alone, so a transfer that exited cleanly while moving
nothing would pass:

  daemon-refuse  allowed() imported verify_dirs but never called it; now it
                 confirms the allowed push/pull actually populated the dest.
  daemon-filter  pull()/the incoming push ignored their exit status, and the
                 outgoing-chmod loop iterated only files that exist -- a
                 zero-file pull passed vacuously. Check the codes and require
                 at least one file to have been mode-checked.
  daemon         run_and_check's unused `expected` param is dropped; the
                 hidden-module and glob listings now compare the exact set of
                 listed paths (catching a leaked extra path), replacing the
                 per-path containment check and the dead normalise() helper
                 whose regex never matched the -r listing format anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
f196279c29 testsuite: add positive controls to the symlink-race security tests
The symlink-race tests only asserted that an outside sentinel was unchanged or
unlisted while ignoring rsync's exit status, so an attack transfer/listing that
failed before reaching the vulnerable receiver/sender path would pass without
the security property ever being exercised. Add a positive control to each --
an ordinary in-module write (bare-do-open, chdir) or an in-module listing
(sender-flist-leak) that must succeed -- so a globally broken/refusing daemon
can no longer make the sentinel checks vacuous, and assert the attack run did
not die from a signal.

clean-fname-underflow now also enforces a non-zero exit: clean_fname()
collapses "a/../test" to "test", whose merge file is absent, so rsync must
reject it; accepting it (rc 0) would mean the crafted name was mis-collapsed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
ed11852ed0 testsuite: harden output-options checks
Several subcases ran rsync without checking the exit status, so a silent
failure could pass as the expected (often empty) output -- most notably -q,
which only asserted empty stdout. Route every expected-success run through a
helper that asserts the exit status, and verify -q actually transferred the
tree. Replace the "-h/-8 didn't break the transfer" check with positive format
assertions: -h must render byte counts with a K/M/G suffix (and the default
must not), and -8 must leave a high-bit filename byte unescaped (\#371 absent)
where the default escapes it -- best-effort, self-skipping where the platform
can't store the raw byte.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
9f0afbea4f testsuite: verify the negotiated compressor/checksum selection
compress-options only checked that each requested algorithm yielded
byte-identical output, which proves parsing/non-corruption but not that the
advertised algorithm was actually used -- the test would pass if the choice
were silently ignored. Capture --debug=NSTR (compat.c / checksum.c) and assert
the selected compressor, compress level, and checksum match the request
(anchored so zlib != zlibx). --skip-compress / --checksum-seed stay content
checks: they have no comparable negotiation-string signal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
034a4f3b1e testsuite: verify --fuzzy actually selects a basis
Both fuzzy tests asserted only that the final file content matched, which a
full transfer that ignored --fuzzy would also satisfy -- so a broken fuzzy
basis selection would pass undetected. Drive rsync directly with --debug=FUZZY
and assert the generator reports the expected basis ("fuzzy basis selected
for <f>: <basis>", generator.c find_fuzzy): rsync2.c for fuzzy, and the
closest-named candidate archive-v1.tar for fuzzy-basis. fuzzy switches from
checkit() to a manual run plus verify_dirs() so the output can be captured.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00
Andrew Tridgell
4f5a5857ce Fix --preallocate --sparse to actually produce sparse files
rsync.1 says combining --preallocate with --sparse yields sparse blocks
wherever the filesystem can punch holes, but since 2019 (commit c2da3809,
"keep file-size 0 when possible") it has silently left the file fully
allocated. Two problems, both rooted in that commit switching --preallocate /
--inplace to fallocate(FALLOC_FL_KEEP_SIZE):

  * do_fallocate() then returned 0 instead of the reserved length, so the
    receiver's preallocated_len was 0 and write_sparse() always lseek'd over
    null runs instead of punching them (and the over-preallocation trim in
    receiver.c never fired either).

  * more fundamentally, KEEP_SIZE leaves the file size at 0 while data is
    written incrementally, so the FALLOC_FL_PUNCH_HOLE call lands on blocks
    beyond EOF and is a silent no-op -- the reserved blocks are never freed.

Fix both: don't request KEEP_SIZE when --sparse is also active, so the file is
preallocated at full size and the punch lands within it; and return the
reserved length from do_fallocate() so preallocated_len drives the punch
decision and the over-allocation trim. --preallocate without --sparse keeps
the KEEP_SIZE (file-size-0) behaviour. t_stub.c gains a sparse_files stub since
do_fallocate now references it and the test helpers link syscall.o.

preallocate_test.py now asserts via st_blocks (where the filesystem can punch
holes) that --preallocate --sparse ends up sparse, guarding the regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:03:58 +10:00
Andrew Tridgell
bc63ea82f2 ci: run the OpenBSD --use-tcp test step at -j2
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>
2026-05-25 07:44:12 +10:00
Andrew Tridgell
0d4fb1bc89 testsuite: cover more path/file-operation code (syscall.c, util1.c, delete.c)
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>
2026-05-25 07:44:12 +10:00
Andrew Tridgell
52480aaac2 runtests: compare expected-skipped order-insensitively; register daemon-access-ip
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>
2026-05-25 07:44:12 +10:00
Andrew Tridgell
702a8f61b7 testsuite: cover daemon access-control, config includes, --stop-at
Target the lowest-coverage rsync files identified from a merged (pipe + proto29/30
+ tcp) gcov report:

  daemon-access-ip  hosts allow / hosts deny with exact-IP and CIDR patterns over
                    --use-tcp, exercising access.c make_mask/match_address/
                    match_binary (19% -> 62% lines), plus client --address
                    (socket.c try_bind_local). require_tcp.
  daemon-config     the &include rsyncd.conf directive (params.c include_config/
                    parse_directives, 48% -> 60%) and a module with a missing path
                    (clientserver.c path_failure).
  stop-time         --stop-at future/past (options.c parse_time) and --stop-after
                    (options.c 59% -> 64%).

Merged scoped coverage: lines 67.3%->68.3%, functions 87.5%->88.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:44:12 +10:00
Andrew Tridgell
2928b2742e build: scope gcov report to rsync's own source; add coverage-all
The coverage report counted bundled third-party code (zlib/, popt/, and the
PostgreSQL/ISC lib/ imports getaddrinfo/getpass/inet_ntop/inet_pton) that rsync
ships but does not own, muddying the percentages. Add a COVERAGE_EXCLUDE gcovr
filter (shared by all coverage targets) so the report reflects rsync's own code:
on the same data, lines 63.9%->65.5%, functions 81.4%->85.0%, branches
55.0%->56.5% (rsync's own md5/mdfour/wildmatch/etc. stay in the report).

Add 'make coverage-all': run the suite under pipe + --protocol=30 + --protocol=29
+ --use-tcp, accumulating into the shared .gcda (not cleared between runs), then
one merged scoped report -- covers the daemon/TCP and protocol-compat paths a
single pipe run misses (lines 67.6%, functions 87.6%, branches 58.6%). Also add
'make coverage-fallback' for a separate --disable-openat2 build (different .gcno,
so it can't merge with the openat2 report). CI is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:44:12 +10:00
Andrew Tridgell
f1d5a3c815 ci: declare new metadata-coverage test skips for macOS and Cygwin
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>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
3086dbc0fd ci: add an Ubuntu gcov coverage job
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>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
63e599b921 build: add 'make coverage-tcp' and drop deprecated gcovr --branches
coverage-tcp reuses the coverage recipe with --use-tcp (daemon tests over a real
loopback rsyncd, which also runs the require_tcp-only tests) and a separate
report directory, via COVERAGE_RUNFLAGS / COVERAGE_DIR. Verified end to end:
pipe run reports 63.9% lines, the TCP run 64.5% (it exercises more code).

Also drop gcovr's --branches flag: it is deprecated in gcovr 8 and branch +
decision coverage still appear in --print-summary and the HTML without it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
340238421d testsuite: assert absolute --partial-dir delta resume now works
partial_test.py sub-test 5 deterministically asserts a delta (--no-whole-file)
resume from an absolute, outside-tree --partial-dir reproduces the source and
consumes the basis -- the regression guard for the receiver fix. Sub-test 4
keeps asserting the cross-directory partial WRITE on interrupt. Drop the
--whole-file workaround and the 'broken on master' notes in the docstring and
COVERAGE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
31fbb17d23 receiver: fix absolute --partial-dir delta resume (false verification)
A delta (--no-whole-file) resume whose basis is an absolute --partial-dir
looped forever on exit code 23 ("failed verification -- update put into
partial-dir"), stranding the correct data in the partial-dir and never
populating the destination.

Cause: an absolute --partial-dir makes the basis path absolute, but the
receiver opened it with secure_relative_open(NULL, fnamecmp, ...), which by
design rejects an absolute relpath (EINVAL). The basis fd was then -1, so
receive_data() mapped no basis and (because the matched-block sum_update() is
guarded by "if (mapbuf)") computed the whole-file verification checksum over
the literal data only -> a spurious mismatch every run. (The data itself was
correct, since the in-place update leaves the matched basis bytes in place.)
Under a non-chroot daemon the in-place write went through the same call and
failed outright.

Fix: add secure_basis_open(), which treats an operator-trusted absolute basis
path as (trusted directory + confined leaf) -- the same way secure_relative_open
already trusts an absolute basedir while keeping O_NOFOLLOW on the leaf -- and
use it for both the basis read and the inplace-partial write. The strict
"reject absolute relpath" contract of secure_relative_open is left intact.

Defense-in-depth: receive_data() now treats a block-match token with no mapped
basis as a protocol inconsistency (it can only arise from a basis that the
generator opened but the receiver could not), failing cleanly instead of
silently dropping those bytes from the verify checksum or the output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
edf298ace5 testsuite: add COVERAGE.md matrix and -u/--force coverage
COVERAGE.md is the living checklist mapping every CLI option (~142) and daemon
parameter (~54) to its test(s), with depth / cross-dir status and remaining
gaps, so the path-resolution restructure can see exactly what is guarded.

update_test.py closes two of the documented gaps: -u/--update (keep a newer
destination, update an older one) and --force (replace a non-empty destination
directory with a file), both at depth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
1d5b5ab83a build: add gcov coverage and --disable-openat2 knobs for the test suite
Two test-coverage build knobs (both behaviour-neutral by default):

  --enable-coverage  appends '--coverage -fprofile-update=atomic -O0' and adds
                     a 'make coverage' target (whole suite, run serially, then
                     gcovr HTML with branch + decision coverage). rsync forks
                     and its children exit without running the gcov atexit
                     flush -- the generator via its SIGUSR1 handler
                     (_exit_cleanup) and the receiver via the SIGUSR2 handler
                     -- so under GCOV_COVERAGE we call __gcov_dump() at both, or
                     receiver.c/generator.c record no coverage at all.

  --disable-openat2  gates the Linux openat2(RESOLVE_BENEATH) sites in syscall.c
                     on HAVE_OPENAT2 (defined by default), so disabling it forces
                     the portable per-component O_NOFOLLOW resolver to run as the
                     primary on ordinary Linux -- exercising and
                     coverage-counting that fallback tier without a pre-5.6
                     kernel. NOTE: coordinate with the parallel syscall.c
                     path-resolution restructure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
b0ba699031 testsuite: probe RESOLVE_BENEATH support functionally for the #715 test
Add resolve_beneath_supported() to rsyncfns: it functionally probes whether the
rsync binary can follow an in-tree directory symlink under its secure resolver
(an initial transfer plus a delta update through a dir-symlink, the operation
issue #715 is about). This tracks the actual binary instead of a platform name.

Use it in symlink-dirlink-basis_test.py in place of the SunOS/OpenBSD/NetBSD/
Cygwin name check: it skips on those platforms too, and additionally on
Linux < 5.6, a seccomp-blocked openat2, and the new --disable-openat2 build,
where the portable O_NOFOLLOW fallback rejects the in-tree symlink.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
e57c7f5d87 testsuite: output, comparison and algorithm-selection option coverage
Breadth pass for options not yet exercised:

  output-options    output shape of --version/--help/-i/-n/--stats/
                    --out-format/--list-only/-q/--progress/-h/-8 (these control
                    output, not path handling, so they're checked for shape).
  compare           -c and -I catch a stealth change (same size+mtime, new
                    content) deep in the tree; --size-only skips a same-size
                    change; --modify-window absorbs a 1s mtime difference.
  compress-options  --compress-choice for every advertised compressor,
                    --compress-level, --skip-compress, --checksum-choice for
                    every advertised checksum, and --checksum-seed -- each a
                    clean byte-identical transfer at depth.

Green on master and under --protocol=29/30.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
05f30c05c9 testsuite: daemon parameter coverage (loopback)
Drive a loopback daemon (secure stdio-pipe transport by default, also green
under --use-tcp) via the new write_daemon_conf helper and assert the behaviour
of the security-relevant rsyncd.conf parameters, transferring >=3-deep trees:

  daemon-access  path / read only / write only / list, incl. a deep sub-path
                 pull and that a list=no module is hidden yet usable by name.
  daemon-filter  daemon exclude hides matching files everywhere; incoming /
                 outgoing chmod rewrite modes of every transferred file.
  daemon-auth    auth users + secrets file accept the right password, reject a
                 wrong one and an unauthenticated request; strict modes rejects
                 a world-readable secrets file.
  daemon-exec    pre-/post-xfer exec run with RSYNC_MODULE_NAME /
                 RSYNC_EXIT_STATUS; a failing pre-xfer exec aborts the transfer
                 (marker files polled for, since post-xfer exec runs after the
                 client disconnects under TCP).
  daemon-munge   munge symlinks stores incoming links with the /rsyncd-munged/
                 prefix and strips it on the way out.
  daemon-refuse  refuse options: a named option, a wildcard, and the '* !a !v'
                 allow-list idiom.

Green on master under pipe and --use-tcp transports and under --protocol=29.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
922681e140 testsuite: filtering coverage at depth
Assert exactly which entries are/aren't transferred, deep in the tree:

  filter-depth      --exclude/--include precedence on files at every level, and
                    a -F per-directory .rsync-filter loaded from a deep dir that
                    applies to that subtree only (not above it).
  cvs-exclude       -C built-in cruft patterns (*.o, *~) at every level plus a
                    deep per-directory .cvsignore scoped to its subtree.
  size-filter       --max-size / --min-size select the right files all the way
                    down.
  files-from-depth  --files-from selects only the listed deep paths (implied
                    parents created); --from0 NUL-delimited; --exclude-from /
                    --include-from filter at depth.

(--existing / --ignore-existing are covered in delete-deep_test.py.)
Green on master and under --protocol=29/30.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
273b9f265f testsuite: metadata preservation coverage at depth
Set each attribute distinctively on a file AND a directory at every level of a
>=3-deep tree and verify it per entry after transfer (metadata is applied as a
single-component op on an entry whose parent chain the resolver restructure
rewrites):

  metadata-depth   -p preserves exact file/dir modes; -t preserves file
                   mtimes; --chmod=D710,F600 rewrites them.
  omit-times       -O omits directory times (files still preserved); -J omits
                   symlink times.
  sparse           -S preserves a deep file's hole (allocated << size);
                   --no-sparse fills it.
  xattrs-depth     -X reproduces a user xattr on every entry (gated on xattr
                   support).
  acls-depth       -A reproduces a POSIX ACL on every entry (gated on ACL
                   support + setfacl/getfacl).
  ownership-depth  --groupmap and --chown=:GROUP remap the group of every
                   entry (non-root, to a secondary group); -o/--usermap gated
                   on root.

All green on master and under --protocol=29/30.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
0d546ee3b4 testsuite: structure / recursion / link coverage at depth
Cover the structure and link options at >=3 levels and across directories,
asserting each option's specific effect:

  links            -l keeps a symlink, -L dereferences it, -k follows a
                   directory symlink -- all on a symlink several levels deep.
  dirs             -d copies the top layer (file + empty dir) without recursing.
  prune-empty-dirs -m drops empty chains and chains emptied by an exclude,
                   keeps populated ones.
  hardlinks-deep   -H preserves a hard link whose names live in different
                   directories at depth; without -H they become separate inodes.
  delete-deep      --delete removes a deep extraneous file/subtree; the four
                   delete-timing variants agree; --max-delete caps deletions;
                   --existing / --ignore-existing select/skip correctly.
  relative-implied -R mirrors an implied directory's mode at depth;
                   --no-implied-dirs does not (proto 30+).

Green on master and under --protocol=29/30 (the --no-implied-dirs sub-case is
gated to protocol >= 30, where multi-component sender paths are accepted).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
d6124a82a4 testsuite: cross-directory/temp/backup/dest coverage at depth
Fill the highest-restructure-risk gap: options that do two-directory / rename /
outside-tree work, asserted at >=3 levels deep with the aux tree kept outside
the main tree, and asserting the option's specific property rather than just
tree equality (which the ported tests already cover).

  alt-dest-deep  --link-dest hardlinks unchanged files (same inode), --copy-dest
                 copies (never links), --compare-dest omits unchanged files;
                 ref tree outside both src and dest.
  temp-dir       cross-dir temp->final rename at depth; temp dir left clean; a
                 missing --temp-dir fails (so the option is proven consulted).
  partial        --partial keeps the partial in the dest file; relative
                 --partial-dir stages per-directory at depth (pre-seed +
                 interrupt/resume); absolute --partial-dir writes the partial
                 outside the tree.
  inplace        --inplace keeps the destination inode across a delta update;
                 the default temp+rename path replaces it.
  append         --append completes truncated files tail-only; --append-verify
                 repairs a corrupted prefix (protocol >= 30).
  backup-deep    --suffix saves <name>S beside the new file; --backup-dir
                 relocates old files to a parallel deep tree outside the dest
                 and captures deletions under --delete.

All green on master and under --protocol=29/30.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
1d828f35ca testsuite: add depth/cross-dir/daemon coverage helpers to rsyncfns.py
Add helpers for the option-coverage expansion (the path-handling restructure
changes parent-component resolution, so options must be exercised at depth and
across directory boundaries):

  * make_tree() builds a tree with a regular file at every level so a property
    can be checked at the tree root and >=3 levels deep;
  * walk_files()/walk_dirs() iterate entries for per-level assertions;
  * assert_same/assert_mode/assert_mtime_close/assert_is_symlink/
    assert_hardlinked/assert_not_hardlinked/assert_exists/assert_not_exists
    assert the concrete property an option controls (not just dest == src);
  * write_daemon_conf() writes an arbitrary rsyncd.conf (globals + modules)
    for daemon-parameter tests, beyond build_rsyncd_conf's fixed four modules;
  * forced_protocol() lets protocol-sensitive tests gate sub-cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew Tridgell
7bba25e675 start on 3.5.0 2026-05-23 07:52:55 +10:00
Andrew Tridgell
6e3140d5ba testsuite: read xattrs natively instead of shelling out to getfattr
xattr_set() sets attributes with the native os.setxattr(), but
xattr_dump() read them back by running "getfattr -d". That asymmetry
breaks "make check" on any system where rsync is built with xattr
support (libattr headers present) but the attr package's CLI tools are
not installed -- common on Android/Termux and minimal CI images: setting
succeeds via os.setxattr, then xattr_dump's getfattr raises
FileNotFoundError, which crashes the test (reported FAIL) instead of
running or skipping it. That's why "make check" was failing here on
xattrs / xattrs-hlink.

Read the xattrs natively with os.listxattr()/os.getxattr() on Linux,
symmetric with xattr_set(), so the suite needs no external getfattr; the
output still mimics "getfattr -d" and only has to be self-consistent
between the source and destination dumps. Cygwin keeps the CLI path
(Python there lacks os.*xattr). make check now passes with no attr
package installed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:15:22 +10:00
Andrew Tridgell
1d8f47cc71 testsuite: generate predictable fixture files instead of reading /etc, /bin, /
The Python rewrite of the suite carried over the shell habit of
populating the test tree by capturing "ls -l /etc" / "ls -l /bin"
(falling back to "ls /"): hands_setup() built etc-ltr-list / bin-lt-list
that way, and longdir_test.py did the same for its leaf files. That ties
the fixtures to the host filesystem layout -- those directories are
absent or unreadable on Android/Termux and other minimal environments,
where "ls /" fails outright -- and the captured content was never
reproducible from run to run.

Add a deterministic make_text_file() helper to rsyncfns.py and use it for
hands_setup()'s two fixture files and longdir's leaf files. The names
etc-ltr-list / bin-lt-list are unchanged (chmod, chmod-temp-dir and
alt-dest reference them by name); only the content source changes, so the
fixtures are now self-contained and identical on every platform. This
also drops longdir_test.py's date(1) and ls(1) subprocess calls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:15:22 +10:00
Andrew Tridgell
743d715d43 docs: add rsync Discord server link
Add a link to the rsync Discord server (https://discord.gg/Avfvy9zhdp)
below the mailing lists section in README.md and on the lists.html web
page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:06:21 +10:00
Andrew Tridgell
4b862306e5 testsuite: restore non-Linux xattr/fake-super coverage
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>
2026-05-22 14:34:52 +10:00
Andrew Tridgell
70948a9dc3 testsuite: post-review fixes and lock-file hardening
* 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>
2026-05-22 14:34:52 +10:00
Andrew Tridgell
951bf0a446 socket: enforce socketpair_tcp()'s anti-hijack guarantee
socketpair_tcp() fakes a connected socket pair via a loopback TCP
self-connect (socket -> bind 127.0.0.1:0 -> listen -> connect ->
accept), used by sock_exec() for RSYNC_CONNECT_PROG. Its comment has
long promised that "nobody else can attach to the socket, or if they
do that this function fails", but nothing actually verified it: the
code accept()ed whatever connection arrived first without checking it
was the one our own connect() made.

Between listen() and accept() the ephemeral loopback port is
connectable by any local user. With backlog 1 a same-host attacker who
races a connection in before our connect() lands could have their
socket returned by accept(), handing them one end of the rsync
protocol stream. The exposure is small (loopback only, random
ephemeral port, sub-millisecond window, local users only), but the
promised guarantee was simply not enforced.

Enforce it: after the connection is established, require that the peer
address of the accepted end (fd[0]) equals the local address of our
connecting end (fd[1]), and that both are 127.0.0.1. A hijacked
connection has a different source port and is rejected (errno EPERM,
fail closed). The legitimate self-connect always matches, so there is
no behaviour change for the normal path.

Verified: rebuilds clean with -Wall -W; the full testsuite still
passes in both transports (pipe `make check` 57/3, `runtests.py
--use-tcp` 59/1) -- the pipe transport exercises this code path on
every daemon test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:34:52 +10:00
Andrew Tridgell
bea8a3a16f testsuite: secure stdio-pipe daemon transport by default, opt-in TCP
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>
2026-05-22 14:34:52 +10:00
Andrew Tridgell
bf8aab51e8 testsuite: add claim_ports() for parallel-safe TCP-port coordination
rsyncfns.claim_ports(*ports) takes exclusive POSIX byte-range locks on
/tmp/rsync_test.lck (offset = port number) so any number of test
processes can run concurrently without colliding on a TCP port: a test
asking for a port already held blocks until the holder exits. The
kernel drops the locks automatically when the holding process dies, so
a crashed test releases its ports with no manual cleanup.

Ports are claimed in sorted order so two callers requesting the same
set in different orders can't deadlock. The lock file is forced to
mode 0o666 after creation (the umask would otherwise trim it and lock
out a second user on a shared CI runner; EPERM when we're not the
owner is fine).

proxy-response-line-too-long is the first user: it switches from an
ephemeral port to a claimed fixed port (12873).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:34:52 +10:00
Andrew Tridgell
1f689ec0c2 testsuite: rewrite the shell testsuite in Python
Replace the entire shell-based testsuite with Python. runtests.py
already drove the suite (it had replaced runtests.sh earlier); this
converts all 60 test scripts from *.test shell to *_test.py and adds
testsuite/rsyncfns.py as the shared helper module -- the Python
counterpart of the now-removed rsync.fns.

runtests.py:
  * Discovers and runs both *.test and *_test.py; dispatches the
    Python tests via the same python3 that runs the harness.
  * Extends PYTHONPATH so tests can `import rsyncfns`.

testsuite/rsyncfns.py provides everything the ports need:
  * environment wiring (scratchdir / srcdir / TOOLDIR / RSYNC /
    TLS_ARGS, and HOME pointed at the per-test scratch dir);
  * result reporting -- test_fail / test_skipped / test_xfail mapping
    to the 0 / 1 / 77 / 78 exit-code convention;
  * the transfer-and-verify helpers checkit, checkdiff, verify_dirs,
    rsync_ls_lR, check_perms and the v_filt output filter;
  * fixture builders hands_setup, build_symlinks, build_rsyncd_conf,
    make_data_file, cp_p / cp_touch, makepath / rmtree.

All 60 tests are converted, including the four split-variant tests
that share one source via a Makefile-built symlink (chown/chown-fake,
devices/devices-fake, xattrs/xattrs-hlink, exclude/exclude-lsh);
Makefile.in's CHECK_SYMLINKS now points at the *_test.py names.

The dead rsync.fns shell library is removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:34:52 +10:00
Andrew Tridgell
8839314025 ci: add static Android NDK build workflow
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>
2026-05-22 13:09:47 +10:00
Andrew Tridgell
47e087d8eb testsuite: portable make_data_file helper; drop hard /dev/urandom dependency
symlink-dirlink-basis.test and chdir-symlink-race.test both
require a multi-kilobyte non-trivial-content source file for the
rsync delta algorithm to exercise.  Both used dd / head against
/dev/urandom directly, which fails on platforms that don't ship
/dev/urandom (e.g. HPE NonStop).  The dd error gets swallowed by
'2>/dev/null' and the test then fails with a misleading 'failed
to create test file' that hides the real cause.

Add make_data_file <path> <size> to testsuite/rsync.fns.  Prefers
/dev/urandom when readable (kernel-provided randomness, fast),
falling back to a deterministic awk LCG seeded from PID and a
POSIX cksum of the destination path.  Output is constrained to
printable ASCII (33..126) so the helper survives two awk-portability
quirks:

  - printf '%c', 0 terminates the string in some awks, emitting
    fewer than sz bytes;
  - gawk in UTF-8 locales encodes printf '%c', N for N > 127 as
    a 2-byte UTF-8 sequence, emitting more than sz bytes.

The tests don't need 8-bit binary entropy -- they just need
non-trivial bytes for rsync's block-matching algorithm.

Update both call sites to use the helper.  Linux/FreeBSD/macOS
still take the /dev/urandom fast path; NonStop and any other
platform missing the device get the awk fallback transparently.
Both paths verified locally with the symlink-dirlink-basis test.
2026-05-21 07:40:30 +10:00
Andrew Tridgell
e1c5f0e93a t_chmod_secure: probe kernel RESOLVE_BENEATH at runtime; drop test skip
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.
2026-05-21 07:40:30 +10:00
Andrew Tridgell
cfdc27c613 t_stub.c: raise max_alloc default so test helpers can allocate
The t_stub.c shim defined max_alloc = 0 as a placeholder to satisfy
the link against util2.o.  This was harmless when the test helpers
made no allocations, but the secure_relative_open() implementation
in 3.4.0+ calls my_strdup() in its per-component O_NOFOLLOW
fallback (syscall.c around line 1857), and the 3.4.3 do_*_at()
hardening series added more such calls.  With max_alloc=0, every
allocation in that path trips the 'exceeded --max-alloc=0' check in
util2.c's my_alloc(), and t_chmod_secure (which exercises
do_chmod_at via secure_relative_open) fails on the very first
my_strdup.

The failure is invisible on Linux 5.6+ / FreeBSD 13+ / macOS 15+ /
recent Cygwin because those platforms take the kernel-enforced
openat2(RESOLVE_BENEATH) or openat(O_RESOLVE_BENEATH) branch and
never reach the per-component fallback.  It also goes unobserved
on the SunOS/OpenBSD/NetBSD/CYGWIN* CI runners because the
chmod-symlink-race.test script case-skips on those platforms (the
legitimate dir-symlink scenario the test exercises can't pass on
the per-component fallback).  HPE NonStop is the first platform
that lacks RESOLVE_BENEATH support AND isn't in the skip list AND
has someone actually running the test suite, so it surfaced the
latent bug.

Raise max_alloc to SIZE_MAX so the helpers can allocate freely.
A follow-up patch makes t_chmod_secure adapt at runtime so the
skip list can be removed and the per-component fallback gets real
CI coverage.
2026-05-21 07:40:30 +10:00
Andrew Tridgell
7e7372a0c5 packaging: add ftp.filt, the FTP mirror filter file
The .filt file in /home/ftp/pub/rsync on samba.org controls which
subtrees release.py's FTP mirror excludes (currently /binaries/
and /generated-files/).  Without it, step-10-push-ftp's
'rsync --del' would propagate local deletions to the server even
for those archive subtrees.

Until now the only copy of this two-line file lived on the server.
Bundle it in source at packaging/ftp.filt so it survives a disaster
on samba.org, and have step_1_fetch seed FTP_DIR/.filt from the
bundled copy on every run (with --exclude=/.filt on the rsync pull,
so the server's copy can't silently drift the bundled one).
step-10-push-ftp then propagates any in-source updates to the
filter back to the server.
2026-05-20 15:36:44 +10:00
Andrew Tridgell
8cad2097e9 packaging: remove obsolete samba-rsync and send-news scripts
Both scripts were pre-release.py legacy helpers:

  * samba-rsync rsync'd ~/samba-rsync-{ftp,html}/ to the samba.org
    server.  release.py step-10-push-ftp and step-11-push-html now
    do exactly this, using ../release/rsync-{ftp,html}/ as the
    local mirrors.

  * send-news copied README/INSTALL/NEWS .md + .html files into
    ~/samba-rsync-ftp/ and rsync'd them to samba.org.
    release.py step-8-update-ftp already does this
    (./md-convert --dest=FTP_DIR README.md NEWS.md INSTALL.md and
    the surrounding rsync of html files into FTP_DIR), and
    step-10-push-ftp pushes the result.

Update the trailing instructions printed at the end of
step-12-push-git to drop the now-obsolete 'run packaging/send-news'
suggestion, and tighten the comment in step_1_fetch that referred
to samba-rsync as a current sibling tool.
2026-05-20 15:36:44 +10:00
Andrew Tridgell
d039cfa829 packaging/release.py: rsync-web is now an in-tree subdirectory
Track the move of rsync-web from sibling git checkout to a regular
subdirectory of the rsync source tree:

  * HTML_SRC: '../rsync-web' -> 'rsync-web'.
  * step_1_fetch: drop the .git-presence probe and the 'make sure
    it's up to date' reminder.  Both made sense when rsync-web was
    a separate repo the maintainer had to clone and pull, but the
    directory is now part of the same checkout as this script.
  * rsync invocation no longer needs --exclude=/.git: there is no
    .git inside rsync-web/ (it is just a subdir of the parent
    rsync-git checkout).
  * Header comment block and step-1 help text rewritten to describe
    the new layout.
2026-05-20 15:36:44 +10:00
Andrew Tridgell
0af88421dc import rsync-web website content as a subdirectory
Fold the standalone rsync-web repo into the rsync source tree as
rsync-web/, eliminating the sibling-checkout convention and the
drift it causes between the release-time HTML snapshot in
../release/rsync-html and the source of truth in ../rsync-web.

Flat-copy import (no git history merge).  The standalone repo at
github.com/RsyncProject/rsync-web is retained for historical
reference and will be archived once the in-tree copy proves itself.

Add /rsync-web/ to .gitattributes with export-ignore so the
website content does not bloat the release source tarball
produced by 'git archive' in packaging/release.py step_7_tarball.

A follow-up commit repoints HTML_SRC in packaging/release.py at
the new in-tree location.
2026-05-20 15:36:44 +10:00
Andrew Tridgell
9d014670df INSTALL.md: point Ubuntu users at the ppa:rsyncproject/rsync PPA
Most Ubuntu users landing on INSTALL.md want to install rsync, not
build it.  Add a short section near the top that offers the
Launchpad PPA as the one-line path for the four currently supported
series (jammy 22.04 LTS, noble 24.04 LTS, questing 25.10,
resolute 26.04 LTS), and clarify that the rest of the file is about
building from source.
2026-05-20 15:36:44 +10:00
Andrew Tridgell
647a00a278 start on 3.4.4 2026-05-20 11:50:33 +10:00
Andrew Tridgell
2c7777aaa6 Preparing for release of 3.4.3 [buildall] 2026-05-20 10:07:26 +10:00
Andrew Tridgell
6af41d2357 version.h: bump to 3.4.3 for the release
Drops the "dev" suffix on RSYNC_VERSION ahead of the
2026-05-20 00:00 UTC public release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
a0b9a8e989 NEWS: prepare 3.4.3 release entry with six CVEs
Set the date to 20 May 2026, add a SECURITY FIXES section listing
all six May 2026 CVEs (CVE-2026-29518, -43617, -43618, -43619,
-43620, -45232) with reach, root cause, fix and reporter for each,
plus a note on the defence-in-depth hardening that goes with them.
Also list the new symlink-race regression tests under DEVELOPER
RELATED.
2026-05-20 10:01:22 +10:00
Andrew Tridgell
ac692b199c util1: handle out-of-range times in timestring 2026-05-20 10:01:22 +10:00
Andrew Tridgell
147e9bea8c main: reject hyphen-prefixed remote-shell hostnames 2026-05-20 10:01:22 +10:00
Andrew Tridgell
a5fc5ebe7a socket: reject over-long proxy response line
fixes a one byte stack overflow when using RSYNC_PROXY with a
malicious proxy.

Reach: only when RSYNC_PROXY is set and a malicious or MITM'd
proxy returns the pathological response.  The byte written is
always '\0' and the attacker doesn't choose the offset, so impact
is corruption of one adjacent stack byte and possible later
misbehaviour or crash -- no information disclosure beyond the
existing rprintf of buffer contents.

Reported by Aisle Research via Michal Ruprich
2026-05-20 10:01:22 +10:00
Andrew Tridgell
c79cb81a4f rsync.h: lower MAX_WIRE_DEL_STAT to avoid signed-int overflow in read_del_stats
read_del_stats() in main.c accumulates 5 wire-supplied counts into
the int32 stats.deleted_files field:

    stats.deleted_files  = read_varint_bounded(..., MAX_WIRE_DEL_STAT, ...);
    stats.deleted_files += stats.deleted_dirs     = ...;
    stats.deleted_files += stats.deleted_symlinks = ...;
    stats.deleted_files += stats.deleted_devices  = ...;
    stats.deleted_files += stats.deleted_specials = ...;

With the previous MAX_WIRE_DEL_STAT = 2^30 (1.07 GB) the worst-case
sum is 5 * 2^30 = 5.37 GB; three maximal values already exceed
INT32_MAX = 2.15 GB on the third "+=", triggering signed integer
overflow (C99 6.5/5 -- undefined behaviour, the compiler may assume
it cannot happen and elide subsequent checks).

The bound was introduced in f0155902 ("defence-in-depth: bound
wire-supplied counts and lengths") with a commit message claiming
"per-summand cap so the total can't overflow", but 2^30 * 5 does
overflow.  Lower the per-summand cap to 2^28 (= 268M) so the worst
case is 5 * 2^28 = 1.34 GB < INT32_MAX with margin.  2^28 deletions
per category is still vastly above any plausible real transfer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
650643109e defence-in-depth: receiver block-index bounds + read_delay_line null check
Two assorted audit findings:

  - receive_data() never bounds-checked the block index returned
    by recv_token() against sum.count before computing offset2
    and feeding it to map_ptr(). An out-of-bounds index from a
    hostile sender produces invalid memory access. Add a
    sum.count bounds check.

  - read_delay_line()'s strchr() call could return NULL when no
    space was found, but the code unconditionally added 1 to the
    result before dereferencing. Low impact (just a disconnect on
    exit of the client-specific forked process) but the NULL
    deref is real. Guard the NULL.

Both reported by Joshua Rogers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
4cf08983e8 defence-in-depth: guard cumulative snprintf against length underflow
Two cumulative-snprintf patterns in log.c (rsyserr) and main.c
(output_itemized_counts) had the shape

    len = snprintf(buf, sizeof buf, ...);
    len += snprintf(buf+len, sizeof buf - len, ...);

with no guard between calls. snprintf returns the would-have-been
length on truncation, so a truncated first call leaves
"sizeof buf - len" as a negative-then-promoted-to-size_t value,
underflowing into a huge size_t and writing past buf.

Realistic exposure is small in both cases (log header well under
buffer, only ~5 itemized iterations writing ~25 chars each into a
1024-byte buffer) but the defect class matches bb0a8118 and the
fix is cheap. Guard before each subsequent call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
8112445318 defence-in-depth: bound wire-supplied counts and lengths
Multiple receiver-side fields read from the wire were trusted
without upper-bound checks. A hostile peer could either request
extreme allocations (DoS via --max-alloc) or, on platforms where
read_varint returned a negative value, push ~SIZE_MAX through the
size_t conversion to wrap downstream length checks.

Introduce read_int_bounded(), read_varint_bounded() and
read_varint_size() in io.c so wire-derived integer ranges are
checked at the read site rather than scattered across each
caller, with RERR_PROTOCOL on out-of-range input.

Apply the bounded primitives to:
  - sum->count (checksum count -- previously could overflow
    (size_t)count * xfer_sum_len on 32-bit with raised max-alloc)
  - xattrs: count, name_len, datum_len, plus rel_pos overflow
    detect to stop chain wrapping the num accumulator
  - acls: ida-entry count
  - flist: file mode S_IFMT validation, modtime_nsec range check
  - delete-stat counters in main: per-summand cap so the total
    can't overflow a signed 32-bit accumulator

Reporters include Joshua Rogers (checksum-count overflow finding).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
c38f20c5ff clientserver: fix hostname ACL bypass when using daemon chroot
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>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
0cf200ecbb receiver: add parent_ndx<0 guard, mirroring 797e17f
Commit 797e17f ("fixed an invalid access to files array") added a
parent_ndx < 0 guard to send_files() in sender.c, but the visually-
identical block in recv_files() in receiver.c was not updated. A
malicious rsync:// server can therefore drive any connecting client
into the same out-of-bounds dir_flist->files[-1] read followed by a
file_struct dereference in f_name() one line later.

Reach: protocol-30+ default (inc_recurse) makes flist.c:2745 set
parent_ndx = -1 on the first received flist when the sender omits a
leading "." entry; rsync.c flist_for_ndx() does not reject ndx == 0
in that state because the range check evaluates 0 < 0 = false; and
read_ndx_and_attrs() only validates ndx with the ITEM_TRANSFER bit
set, so iflags=ITEM_IS_NEW (or any other non-transfer iflag word)
bypasses the check.

Apply the same guard receiver-side. Confirmed: the same PoC (a
minimal Python rsyncd that handshakes with CF_INC_RECURSE, sends a
no-leading-"." flist, and emits ndx=0 with ITEM_IS_NEW) crashes
unpatched 3.4.2 with SEGV_MAPERR si_addr=0x4101a-class in the
receiver child; with this guard it exits cleanly with code 2
(RERR_PROTOCOL).

The attack surface delta over the sender variant is large:
the original was malicious-client -> daemon, this is
malicious-server -> any rsync client doing a normal rsync://
or remote-shell pull.

Reported by Pratham Gupta (alchemy1729).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
e4c681fefd testsuite: cover 'refuse options = compress' for the daemon
Add a daemon-refuse-compress test that builds a module configured with
'refuse options = compress' and asserts that:
  1. an attempted -z transfer to that module fails with an error
     mentioning --compress, and
  2. the same transfer without -z still succeeds.

This pins down the documented way to disable all compression on a
daemon, which previously had no automated coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
c44c90e946 token: harden compressed-token decoding against integer overflow
The receiver's three compressed-token decoders --
recv_deflated_token (zlib), recv_zstd_token, and
recv_compressed_token (lz4) -- accumulated rx_token (a 32-bit
signed counter) without overflow checking. A malicious sender
could craft a compressed-token stream that walked rx_token past
INT32_MAX, with careful manipulation leaking process memory
contents to the wire (environment variables, passwords, heap
pointers, library pointers -- significantly weakening ASLR
and facilitating further exploitation).

Cap rx_token at MAX_TOKEN_INDEX = 0x7ffffffe. Fold the
bookkeeping into recv_compressed_token_num() and
recv_compressed_token_run() shared by all three decoders. Reject
negative or out-of-range token values explicitly. Also cap the
simple_recv_token literal-block length at the source: any
wire-supplied length > CHUNK_SIZE is ill-formed (the matching
simple_send_token never writes a chunk larger than CHUNK_SIZE),
so reject before looping on attacker-controlled bytes.

Reach: an authenticated daemon connection with compression
enabled (the default for protocols >= 30 when both peers
advertise it). Disabling compression on the daemon
("refuse options = compress" in rsyncd.conf) is the available
workaround.

Reporter: Omar Elsayed (seks99x).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
fc592a8e25 ci(cygwin): mark all symlink-race regression tests as expected-skipped
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>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
40a6e13071 testsuite: end-to-end regression test for chdir-symlink-race
testsuite/chdir-symlink-race.test runs an actual rsync daemon
(via RSYNC_CONNECT_PROG to avoid the network) configured with
"use chroot = no", plants a symlink at module/subdir -> ../outside,
and runs four flavours of attacker-shaped transfer (single-file
poc_chmod, -r push into the symlinked subdir with --size-only and
without, -r push into the module root). All four must leave the
outside-the-module sentinel file's mode AND content unchanged.

Portability:
  - file_mode() helper falls back to BSD stat -f %Lp when GNU
    stat -c %a is unavailable (macOS, FreeBSD).
  - Pre-saved pristine copy + cmp(1) replaces sha1sum, which
    differs across platforms (sha1sum / shasum / sha1).

Tests are kept running as root in the user-namespace re-exec
wrapper used by symlink-race tests so the daemon's setuid path
doesn't drop into the test user's identity (which on Linux
would mean the chmod-escape code path can't trigger because
the test user doesn't have CAP_FOWNER over the outside file).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
3cc6a9e8cd util1+syscall: secure copy_file source/dest opens; bare-path defence-in-depth
Three related codex audit findings:

  Finding 3a: copy_file()'s source open in util1.c used
  do_open_nofollow(), which only rejects a final-component
  symlink. A parent-component symlink (e.g. --copy-dest=cd where
  cd -> /outside) follows freely and reads outside the module.
  Route through secure_relative_open() with O_NOFOLLOW.

  Finding 3b: generator.c's in-place backup-file create still
  used a bare do_open with O_CREAT, leaving a tiny but reachable
  parent-symlink window between the secure unlink (already
  through do_unlink_at) and the create. Add do_open_at() that
  goes through a secure parent dirfd, and route the call site
  through it.

  Finding 3c: copy_file()'s destination open in
  unlink_and_reopen() had the same bare-do_open pattern; route
  through do_open_at as well.

Adds testsuite/copy-dest-source-symlink.test and
testsuite/bare-do-open-symlink-race.test as regression coverage
for both attack shapes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
30656c5e35 syscall: add symlink-race-safe do_*_at() wrappers and harden secure_relative_open
Add the rest of the path-based syscall wrappers and migrate every
receiver-side caller:
  - do_lchown_at, do_rename_at, do_mkdir_at, do_symlink_at,
    do_mknod_at, do_link_at, do_unlink_at, do_rmdir_at,
    do_utimensat_at, do_stat_at, do_lstat_at

Same shape as do_chmod_at: open each parent under
secure_relative_open(), call the *at() variant against the dirfd,
fall through to the bare path-based syscall in non-daemon /
chrooted / absolute-path / no-parent cases. macOS's
setattrlist-based set_times tier is also routed through the
utimensat_at path on daemon-no-chroot.

Hardenings to secure_relative_open() itself:
  - confine basedir resolution under the same kernel mechanism
    used for relpath (basedirs from --copy-dest / --link-dest are
    sender-controllable in daemon mode)
  - reject any '..' component (bare '..', 'foo/..', 'subdir/..')
    so the per-component O_NOFOLLOW fallback can't escape
  - return the dirfd we built up from the per-component fallback
    when the caller passed O_DIRECTORY (otherwise every do_*_at
    failed with EINVAL on platforms without RESOLVE_BENEATH)

Adds testsuite/alt-dest-symlink-race.test and
testsuite/secure-relpath-validation.test (with t_secure_relpath
helper) as regression coverage for the new hardenings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
15d2964256 util1: secure change_dir() against symlink-race chdir-escape
The receiver's chdir(2) into a destination subdirectory followed
attacker-planted symlinks at every path component. Once CWD
escaped the module, every subsequent path-relative syscall (open,
chmod, lchown, ...) inherited the escape -- defeating
secure_relative_open's RESOLVE_BENEATH anchor against AT_FDCWD,
since the anchor itself was now outside the module.

Route change_dir's relative target through secure_relative_open()
and fchdir() to the resulting dirfd in am_daemon && !am_chrooted
mode, so the chdir step itself can no longer follow a parent-
symlink. Same treatment applied to the CD_SKIP_CHDIR /
set_path_only path so it also can't follow attacker symlinks
during path tracking.

Adds testsuite/sender-flist-symlink-leak.test covering the
sender-side flist resolution variant of the same primitive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
862fe4eeaf syscall+receiver: secure receiver-side do_chmod against symlink-race TOCTOU
CVE-2026-29518's fix routed the receiver's open() through
secure_relative_open(), but every other path-based syscall the
receiver runs on sender-controllable paths is vulnerable to the
same TOCTOU primitive. This commit closes the chmod variant.

Add do_chmod_at() that opens the parent of fname under
secure_relative_open() and uses fchmodat() against the resulting
dirfd. Gate the secure path on am_daemon && !am_chrooted (the same
gate use_secure_symlinks already uses for the receiver basis-file
open), so non-daemon callers and chrooted daemons keep the original
do_chmod() fast path.

Migrate the receiver-side do_chmod() call sites in delete.c,
generator.c, rsync.c, and xattrs.c.

Adds testsuite/chmod-symlink-race.test (with t_chmod_secure helper)
as regression coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
859d44fa4f sender: fix read-path TOCTOU by opening from module root (CVE-2026-29518)
The sender's file open was vulnerable to the same TOCTOU symlink
race as the receiver-side basis-file open. change_pathname() calls
chdir() into subdirectories, which follows symlinks; an attacker
could race to swap a directory for a symlink between the chdir and
the file open, allowing reads of privileged files through the
daemon.

Reconstruct the full relative path (F_PATHNAME + fname) and open
via secure_relative_open() from the trusted module_dir, which
walks each path component without following symlinks. This is
independent of CWD, so the chdir race is neutralised.

CVE-2026-29518.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
f1c24ab03b syscall+clientserver: am_chrooted and use_secure_symlinks for daemon-no-chroot (CVE-2026-29518)
CVE-2026-29518: an rsync daemon configured with "use chroot = no"
is exposed to a TOCTOU race on parent path components. A local
attacker with write access to a module can replace a parent
directory component with a symlink between the receiver's check
and its open(), redirecting reads (basis-file disclosure) and
writes (file overwrite) outside the module. Under elevated daemon
privilege this allows privilege escalation. Default
"use chroot = yes" is not exposed.

Add secure_relative_open() in syscall.c. It walks the parent
components under RESOLVE_BENEATH (Linux 5.6+) /
O_RESOLVE_BENEATH (FreeBSD 13+, macOS 15+) / per-component
O_NOFOLLOW elsewhere, anchored at a trusted dirfd, so a parent-
symlink swap is rejected by the kernel. Route the receiver's
basis-file open in receiver.c through it when use_secure_symlinks
is set in clientserver.c rsync_module().

Reporters: Nullx3D (Batuhan SANCAK); Damien Neil; Michael Stapelberg.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:01:22 +10:00
Andrew Tridgell
b9cc0c6176 ci(almalinux-8): use python39 module for runtests.py
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>
2026-05-07 05:47:29 +10:00
Andrew Tridgell
c60550bff9 ci: add Ubuntu 22.04 and AlmaLinux 8 workflows for backporting
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>
2026-05-07 05:47:29 +10:00
Andrew Tridgell
67f1dcf604 testsuite: run protected-regular test as non-root using unshare
Use unshare with user namespace UID mapping to run the
protected-regular test without real root privileges. Falls back
to skipping if unshare or uidmap is not available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 09:27:12 +10:00
Andrew Tridgell
79fd7d5885 Start 3.4.3dev going.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:43:14 +10:00
Andrew Tridgell
dfdcd8f851 ci: add symlink-dirlink-basis to Cygwin's expected-skipped list
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>
2026-04-30 09:30:31 +10:00
Andrew Tridgell
04e2fc2c76 testsuite: skip symlink-dirlink-basis on platforms without RESOLVE_BENEATH
secure_relative_open() has a kernel-enforced "stay below dirfd" path
on Linux 5.6+ (openat2 RESOLVE_BENEATH) and FreeBSD 13+ (openat
O_RESOLVE_BENEATH). On Solaris, OpenBSD, NetBSD, and Cygwin the code
falls back to the per-component O_NOFOLLOW walk, which by design
rejects every directory symlink in the path -- the very case this
test exercises. Mark the test skipped there rather than have it
fail with a known regression that's tracked separately.

macOS is intentionally not in the skip list: although it does not
have O_RESOLVE_BENEATH either, the test passes there in practice;
investigation of the underlying reason is left as follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:30:31 +10:00
Andrew Tridgell
7f60ec001a syscall: also use O_RESOLVE_BENEATH on FreeBSD and MacOS
FreeBSD and MacOS have O_RESOLVE_BENEATH as an openat() flag with the same
"must not escape dirfd" semantics as Linux's RESOLVE_BENEATH. The
kernel rejects ".." escapes, absolute symlinks, and symlinks whose
target lies outside dirfd, while still following symlinks that
resolve within it -- the same trade-off that fixes issue #715 on
Linux.

Add a parallel BSD path in secure_relative_open(), gated on
declared. Unlike Linux, BSD doesn't have the header/runtime split
where the symbol can exist without kernel support, so no runtime
fallback is needed: if the flag compiles in, the kernel honours it.

OpenBSD and NetBSD have no equivalent kernel primitive and continue
to use the existing per-component O_NOFOLLOW walk; issue #715
remains visible on those platforms (a userland resolver or
unveil(2)-based fence would be follow-up work).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:30:31 +10:00
Andrew Tridgell
4fa7156ccd syscall: use openat2(RESOLVE_BENEATH) on Linux for secure_relative_open
The CVE fix in commit c35e283 made secure_relative_open() walk every
component of relpath with O_NOFOLLOW. That blocks every symlink in the
path, which is stricter than the threat model required: legitimate
directory symlinks within the destination tree (e.g. when using -K /
--copy-dirlinks) are also rejected, breaking delta transfers with
"failed verification -- update discarded".  See issue #715.

On Linux 5.6+, openat2(RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS) gives
us exactly what we want: the kernel rejects any resolution that would
escape the starting directory (via "..", absolute paths, or symlinks
pointing outside dirfd) while still following symlinks that resolve
within it. /proc magic-links are blocked too.

Use openat2 first; fall back to the existing per-component O_NOFOLLOW
walk on ENOSYS (kernel < 5.6). The lexical "../" checks at the head
of the function are kept as defense in depth. The Linux gate is
plain #ifdef __linux__: the runtime ENOSYS fallback covers the only
case that actually matters (header present + old kernel), and any
Linux build environment without linux/openat2.h will fail with a
clear "no such file" error rather than silently disabling the
protection.

Verified manually that openat2(RESOLVE_BENEATH) blocks all four
escape patterns (absolute symlink, ../ symlink, lexical .., absolute
path) while allowing direct and within-tree symlinks. The new
testsuite/symlink-dirlink-basis.test (taken from PR #864 by Samuel
Henrique) exercises the issue #715 regression and passes; full
make check passes 47/47.

Test: testsuite/symlink-dirlink-basis.test (8 scenarios)
Fixes: https://github.com/RsyncProject/rsync/issues/715

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:30:31 +10:00
Andrew Tridgell
dcf364dac5 testsuite/xattrs: ignore SUNWattr_* in the Solaris xls helper
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>
2026-04-30 09:25:58 +10:00
Andrew Tridgell
d1eff8f0dc ci: add OpenBSD and NetBSD build jobs, run 'make check' on the BSDs
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>
2026-04-30 08:15:37 +10:00
Andrew Tridgell
8f727166d9 runtests.py: error early when test helper programs are missing
When invoked directly (rather than via 'make check'), runtests.py
previously left the user with a wall of confusing "not found" errors
from inside individual test scripts if the CHECK_PROGS helpers had not
been built. Detect this up front and point the user at the make
target that builds them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:00:55 +10:00
Andrew Tridgell
5bcb3deb2f packaging: remove old release system 2026-04-28 15:08:25 +10:00
Andrew Tridgell
de3cc03b03 Preparing for release of 3.4.2 [buildall] 2026-04-28 14:29:48 +10:00
Andrew Tridgell
006ee327d6 packaging: new release script 2026-04-28 14:27:41 +10:00
Andrew Tridgell
9b6363fa10 update NEWS.md ready for 3.4.2 2026-04-28 12:55:38 +10:00
Andrew Tridgell
9e2f0fe9ae packaging: remove support for rsync-patches 2026-04-28 12:55:38 +10:00
Michal Ruprich
4f6e4ea64a Do not clean DISPLAY unconditionally 2026-04-22 13:05:35 +10:00
Andrew Tridgell
567c40935f call tzset() before chroot to cache timezone data
localtime/localtime_r need /etc/localtime for timezone info.
After chroot this file is inaccessible, causing log timestamps
to fall back to UTC. Calling tzset() before chroot ensures the
timezone data is cached by glibc for subsequent calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 13:02:10 +10:00
Michal Ruprich
8e11f0c169 Using a correct time in log file 2026-04-22 13:02:10 +10:00
Andrew Tridgell
e9dbc8d66d rsyncd.conf: document the temp dir parameter
The temp dir parameter was functional but undocumented in the man page.

Fixes: https://github.com/RsyncProject/rsync/issues/820

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 12:34:58 +10:00
Andrew Tridgell
bd2dbd2f32 runtests.py: preserve test-execution order in skipped list
The sorted() call reordered skipped test names alphabetically,
causing CI expected-skipped mismatches (e.g. acls,acls-default
instead of acls-default,acls). Sort by original test order instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 12:34:39 +10:00
Andrew Tridgell
350e295d1c runtests.py: add -j/--parallel option for parallel test execution
Add parallel test execution using concurrent.futures. With -j8 the
test suite completes in ~4s vs ~29s sequential (~7x speedup).

Also fix two issues that caused failures under parallel execution:
- rsync_ls_lR now prunes testtmp/ so parallel tests don't see each
  other's temp files when scanning the source tree
- clean-fname-underflow.test now uses $scratchdir instead of /tmp

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 12:34:39 +10:00
Andrew Tridgell
066156fcd9 replace runtests.sh with runtests.py
Rewrite the test runner in Python with proper command-line options
including --valgrind which directs valgrind output to per-process
log files so it doesn't interfere with test output comparisons.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 12:34:39 +10:00
Holger Hoffstätte
a5bbe859db Fix glibc-2.43 constness warnings
Glibc 2.43 added C23 const-preserving overloads to various string functions,
which change the return type depending on the constness of the argument(s).
Currently this leads to warnings from calls to strtok() or strchr().
Fix this by properly declaring the respective variable types.

Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com>
2026-04-22 12:10:08 +10:00
Andrew Tridgell
d046525de3 zero all new memory from allocations
Change my_alloc() to use calloc instead of malloc so all fresh
allocations return zeroed memory. Also zero the expanded portion
in expand_item_list() after realloc, since it knows both old and
new sizes. This gives more predictable behaviour in case of bugs
where uninitialised or stale memory is accidentally accessed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 11:44:10 +10:00
Andrew Tridgell
bb0a8118c2 xattrs: fixed count in qsort
this fixes the count passed to the sort of the xattr list. This issue
was reported here:

https://www.openwall.com/lists/oss-security/2026/04/16/2

the bug is not exploitable due to the fork-per-connection design of
rsync, the attack is the equivalent of the user closing the socket
themselves.
2026-04-22 10:38:14 +10:00
Andrew Tridgell
d1df0aaf70 fix signed integer overflow in proxy protocol v2 header parsing
The len field in the proxy v2 header was declared as signed char,
allowing a negative size to bypass the validation check and cause
a stack buffer overflow when passed to read_buf() as size_t.

This bug was reported by John Walker from ZeroPath, many thanks for
the clear report!

With the current code this bug does not represent a security issue as
it only results in the exit of the forked process that is specific to
the attached client, so it is equivalent to the client closing the
socket, so no CVE for this, but it is good to fix it to prevent a
future issue.
2026-04-16 13:59:52 +10:00
Andrew Tridgell
15d8e49a64 zlib: convert K&R function definitions to ANSI style
The bundled zlib 1.2.8 used K&R-style function definitions which are
rejected by clang 16+ as hard errors. Convert all 90 functions across
9 files to ANSI-style prototypes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:49:30 +10:00
Andrew Tridgell
b905ab23af CI: add simd-checksum to expected-skipped on macOS and Cygwin
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>
2026-03-02 09:52:01 +11:00
Andrew Tridgell
aa142f08ef fix uninitialized mul_one in AVX2 checksum and add SIMD checksum test
The AVX2 get_checksum1_avx2_64() read mul_one before initializing it,
which is undefined behavior. Replace the cmpeq/abs trick with
_mm256_set1_epi8(1) to match the SSSE3 and SSE2 versions.

Add a TEST_SIMD_CHECKSUM1 test mode that verifies all SIMD paths
(SSE2, SSSE3, AVX2, and the full dispatch chain) produce identical
results to the C reference, across multiple buffer sizes with both
aligned and unaligned buffers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:52:01 +11:00
Andrew Tridgell
236417cf35 acl: fixed ACL ID mapping for non-root
closes issue #618
2026-01-19 11:32:13 +11:00
Andrew Tridgell
2a97d81e99 CI: fixed MacOS test
fixed multiple MacOS issues
2025-12-31 11:37:27 +11:00
Andrew Tridgell
359e539a72 reject negative token values in compressed stream receivers
Validate that token numbers read from compressed streams are
non-negative. A negative token value would cause the return value
of recv_*_token() to become positive, which callers interpret as
literal data length, but no data pointer is set on this code path.

While this only causes the receiver to crash (which is process-isolated
and only affects the attacker's own connection), it's still undefined
behavior.

Reported-by: Will Sergeant <wlsergeant@gmail.com>
2025-12-31 09:31:52 +11:00
Andrew Tridgell
9e0898460d util: fixed issue in clean_fname()
fixes buffer underflow (not exploitable) in clean_fname
2025-12-30 17:49:35 +11:00
Andrew Tridgell
185520a141 testsuite: added clean-fname-underflow test 2025-12-30 17:49:35 +11:00
Andrew Tridgell
c98f9d1f68 fix uninitialized buf1 in get_checksum2() MD4 path
The static buf1 pointer was only allocated when len > len1, but on
first call with len == 0, this condition is false (0 > 0), leaving
buf1 NULL when passed to memcpy().

Fixes #673
2025-12-30 16:51:43 +11:00
Nebojša Cvetković
1f9ce2fcbe rsync: Add missing dirs long option 2025-12-30 16:48:34 +11:00
Andrew Tridgell
797e17fc4a fixed an invalid access to files array
this was found by Calum Hutton from Rapid7. It is a real bug, but
analysis shows it can't be leverged into an exploit. Worth fixing
though.

Many thanks to Calum and Rapid7 for finding and reporting this
2025-08-23 17:49:19 +10:00
Ronnie Sahlberg
c2db921890 options.c: Fix segv if poptGetContext returns NULL
If poptGetContext returns NULL, perhaps due to OOM,
a NULL pointer is passed into poptReadDefaultConfig()
which in turns SEGVs when trying to dereference it.

This was found using https://github.com/sahlberg/malloc-fail-tester.git
$ ./test_malloc_failure.sh rsync -Pav crash crosh

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
2025-08-23 17:49:03 +10:00
Silent
77be09aaed syscall: fix a Y2038 bug by replacing Int32x32To64 with multiplication
Int32x32To64 macro internally truncates the arguments to int32,
while time_t is 64-bit on most/all modern platforms.
Therefore, usage of this macro creates a Year 2038 bug.
2025-08-23 17:32:11 +10:00
Jeremy Norris
0d0f615240 Ignore directory has vanished errors. 2025-08-23 17:31:52 +10:00
Max Kellermann
b6457bbc83 make lots of global variables const
This way, they can live in `.rodata` and the compiler is allowed to do
certain optimizations.
2025-08-23 17:31:40 +10:00
Peter Eriksson
1807ce485a Fix handling of objects with many xattrs on FreeBSD 2025-08-23 17:31:28 +10:00
Rahul Mehta
9c175ac9ef chore: gitignore MacOS debug symbols 2025-08-23 17:31:12 +10:00
Emily
a84b79ea58 Allow ls(1) to fail in test setup
This can happen when the tests are unable to `stat(2)` some files in
`/etc`, `/bin`, or `/`, due to Unix permissions or other sandboxing. We
still guard against serious errors, which use exit code 2.
2025-08-23 17:30:59 +10:00
fbuescher
d4c4f6754e fixed remove multiple leading slashes 2025-08-23 17:14:43 +10:00
Michal Ruprich
a4b926dcdc bool is a keyword in C23 2025-08-23 17:14:26 +10:00
Eli Schwartz
0973d0e380 configure.ac: check for xattr support both in libc and in -lattr
In 2015, the attr/xattr.h header was fully removed from upstream attr.

In 2020, rsync started preferring the standard header, if it exists:
https://github.com/RsyncProject/rsync/pull/22

But the fix was incomplete. We still looked for the getxattr function in
-lattr, and used it if -lattr exists. This was the case even if the
system libc was sufficient to provide the needed functions. Result:
overlinking to -lattr, if it happened to be installed for any other
reason.

```
checking whether to support extended attributes... Using Linux xattrs
checking for getxattr in -lattr... yes
```

Instead, use a different autoconf macro that first checks if the
function is available for use without any libraries (e.g. it is in
libc).

Result:

```
checking whether to support extended attributes... Using Linux xattrs
checking for library containing getxattr... none required
```

Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
2025-08-23 17:14:06 +10:00
Ethan Halsall
e405cfc073 feat: add compress threads to man page 2025-08-23 17:13:49 +10:00
Ethan Halsall
b78a841bb0 feat: validate compress threads option 2025-08-23 17:13:49 +10:00
Ethan Halsall
f7a2b8a3fa feat: add threads to zstd compression 2025-08-23 17:13:49 +10:00
Arnaud Rebillout
d941807915 Fix flaky hardlinks test
The test was added in dc34990, it turns out that it's flaky. It failed
once on the Debian build infra, cf. [1].

The problem is that the command `rsync -aH '$fromdir/sym' '$todir'`
updates the mod time of `$todir`, so there might be a diff between the
output of `rsync_ls_lR $fromdir` and `rsync_ls_lR $todir`, if ever rsync
runs 1 second (or more) after the directories were created.

To clarify: it's easy to make the test fails 100% of the times with this
change:

```
 makepath "$fromdir/sym" "$todir"
+sleep 5
 checkit "$RSYNC -aH '$fromdir/sym' '$todir'" "$fromdir" "$todir"
```

With the fix proposed here, we don't use `checkit` anymore, instead we
just run the rsync command, then a simple `diff` to compare the two
directories. This is exactly what the other `-H` test just above does.

In case there's some doubts, `diff` fails if `sym` is missing:

```
$ mkdir -p foo/sym bar
$ diff foo bar || echo KO!
Only in foo: sym
KO!
```

I tested that, after this commit, the test still catches the `-H`
regression in rsync 3.4.0.

Fixes: https://github.com/RsyncProject/rsync/issues/735

[1]: https://buildd.debian.org/status/fetch.php?pkg=rsync&arch=ppc64el&ver=3.4.1%2Bds1-1&stamp=1741147156&raw=0
2025-08-23 17:13:28 +10:00
Krzysztof Płocharz
992e10efaf Fix --open-noatime option not working on files
atime of source files could sometimes be overwritten
even though --open-noatime option was used.

To fix that, optional O_NOATIME flag was added
to do_open_nofollow which is also used to open regular
files since fix:
  "fixed symlink race condition in sender"
Previously optional O_NOATIME flag was only in do_open.
2025-08-23 17:13:09 +10:00
Chris Lamb
1c5ebdc4e5 Make the build reproducible
From https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1093201:
Whilst working on the Reproducible Builds effort [0], we noticed that
rsync could not be built reproducibly.

This is because the date in the manual page can vary depending on
whether there is a .git directory and the modification time of version.h
and Mafile, which might get modified when patching via quilt.

A patch is attached that makes this use SOURCE_DATE_EPOCH, which
will always be reliable.
2025-08-23 16:40:34 +10:00
Wayne Davison
9994933c8c Test on ubuntu-latest. 2025-02-11 13:37:12 -08:00
Alan Coopersmith
23d9ead5af popt: remove obsolete findme.c & findme.h
popt 1.14 merged these into popt.c but the import into rsync
missed removing them.

Fixes: https://github.com/RsyncProject/rsync/issues/710

Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
2025-01-17 08:31:36 +11:00
Wayne Davison
fcfdd36054 Update MAINTAINER_TZ_OFFSET on release.
This also fixes a string with \s that wasn't a r'...' string.
2025-01-15 23:27:27 -08:00
Wayne Davison
89b847393f Fix python deprecation warning. 2025-01-15 22:36:29 -08:00
Wayne Davison
788ecbe5ea Don't edit copyright year values anymore. 2025-01-15 22:30:32 -08:00
Wayne Davison
353506bc51 Improve interior dashes in long options.
Improve the backslash-adding code in md-convert to affect dashes in the
interior of long options.  Perhaps fixes #686.
2025-01-15 22:23:30 -08:00
Wayne Davison
7cff121ec8 Start 3.4.2dev going. 2025-01-15 22:01:42 -08:00
Andrew Tridgell
14f33837dc fixed build error on ia64 NonStop
it treats missing prototype as an error, not warning
2025-01-16 15:27:21 +11:00
Andrew Tridgell
3305a7a063 Preparing for release of 3.4.1 [buildall] 2025-01-16 07:49:23 +11:00
Andrew Tridgell
494879b819 update NEWS.md for 3.4.1 2025-01-16 07:47:07 +11:00
Andrew Tridgell
8d6da040e5 popt: remove dependency on alloca 2025-01-16 07:27:46 +11:00
Natanael Copa
68e9add76a Fix build on ancient glibc without openat(AT_FDCWD
Fixes: https://github.com/RsyncProject/rsync/issues/701
2025-01-16 06:43:57 +11:00
Rodrigo OSORIO
dc34990b2e Test send a single directory with -H enabled
Ensure this still working after 3.4.0 breakage

https://github.com/RsyncProject/rsync/issues/702
2025-01-16 06:32:17 +11:00
Natanael Copa
81ead9e70c Fix use-after-free in generator
full_fname() will free the return value in the next call so we need to
duplicate it before passing it to rsyserr.

Fixes: https://github.com/RsyncProject/rsync/issues/704
2025-01-16 06:27:26 +11:00
Natanael Copa
996af4a79f Fix FLAG_GOT_DIR_FLIST collission with FLAG_HLINKED
fixes commit 688f5c379a (Refuse a duplicate dirlist.)

Fixes: https://github.com/RsyncProject/rsync/issues/702
Fixes: https://github.com/RsyncProject/rsync/issues/697
2025-01-16 06:21:54 +11:00
Andrew Tridgell
dacadd53a9 update maintainer address
use rsync.project@gmail.com
2025-01-15 12:13:41 +11:00
Wayne Davison
a6312e60c9 Force rsync group when uploading files. 2025-01-14 13:09:33 -08:00
Andrew Tridgell
e3ee0e7319 Preparing for release of 3.4.0 [buildall] 2025-01-15 05:53:23 +11:00
Andrew Tridgell
0fd29b6bcb packaging: adjust release script
remove auto-edit of NEWS.md
2025-01-15 05:50:22 +11:00
Andrew Tridgell
7f79682732 NEWS: update protocol version table 2025-01-15 05:50:05 +11:00
Andrew Tridgell
870b7d96dc update NEWS for 3.4.0 2025-01-15 05:30:32 +11:00
Andrew Tridgell
9dc31473ba change version to 3.4.0 2025-01-15 05:30:32 +11:00
Andrew Tridgell
536ae3f4ef raise protocol version to 32
make it easier to spot unpatched servers
2025-01-15 05:30:32 +11:00
Andrew Tridgell
0590b09d9a fixed symlink race condition in sender
when we open a file that we don't expect to be a symlink use
O_NOFOLLOW to prevent a race condition where an attacker could change
a file between being a normal file and a symlink
2025-01-15 05:30:32 +11:00
Andrew Tridgell
407c71c7ce make --safe-links stricter
when --safe-links is used also reject links where a '../' component is
included in the destination as other than the leading part of the
filename
2025-01-15 05:30:32 +11:00
Andrew Tridgell
344327385f range check dir_ndx before use 2025-01-15 05:30:32 +11:00
Wayne Davison
688f5c379a Refuse a duplicate dirlist. 2025-01-15 05:30:32 +11:00
Andrew Tridgell
9f86ddc965 disallow ../ elements in relpath for secure_relative_open 2025-01-15 05:30:32 +11:00
Andrew Tridgell
c35e28331f receiver: use secure_relative_open() for basis file
this prevents attacks where the basis file is manipulated by a
malicious sender to gain information about files outside the
destination tree
2025-01-15 05:30:32 +11:00
Andrew Tridgell
b4a27ca25d added secure_relative_open()
this is an open that enforces no symlink following for all path
components in a relative path
2025-01-15 05:30:32 +11:00
Andrew Tridgell
8ad4b5d912 refuse fuzzy options when fuzzy not selected
this prevents a malicious server providing a file to compare to when
the user has not given the fuzzy option
2025-01-15 05:30:32 +11:00
Andrew Tridgell
589b0691e5 prevent information leak off the stack
prevent leak of uninitialised stack data in hash_search
2025-01-15 05:30:32 +11:00
Charalampos Mitrodimas
36212021f0 hlink: Fix function pointer cast in qsort()
Replace unsafe generic function pointer cast with proper type cast for
qsort() comparison function. This fixes a potential type mismatch
warning without changing the behavior.

Signed-off-by: Charalampos Mitrodimas <charmitro@posteo.net>
2024-12-18 08:56:27 +11:00
Andrew Tridgell
2b38542e0d added security email address to README.md 2024-12-18 08:55:45 +11:00
Frederic Grabowski
321dd78f8c fix typo in manual page 2024-11-19 21:45:50 -08:00
Romain Geissler
6f10f12577 When not using the builtin zlib, link it before linking libcrypto, as libcrypto depends on zlib.
This prevents "undefined symbol" errors which might arise from libcrypto.a if linking openssl statically.
2024-11-19 21:40:14 -08:00
Colin Watson
1a95869dfc Allow basic connectivity check via rrsync
rsbackup (https://github.com/ewxrjk/rsbackup) uses "ssh <host> true" to
check that the host in question is reachable.  I like to configure my
backed-up hosts to force the backup system to go via `rrsync`, but I
always have to add a local tweak to allow `SSH_ORIGINAL_COMMAND=true` to
work.  I think this would be safe enough to include in rrsync.
2024-11-19 21:35:49 -08:00
Rose
c9fe6ca304 Introduce PTR_SUB
This is more intuitive than adding a negative number.
2024-11-19 21:33:30 -08:00
Samuel Henrique
990fa5c1e1 rrsync: fix wrong parameter name in manpage SYNOPSIS
Replace ¨rw¨ with ¨ro¨.

Reported on Debian by Adriano Rafael Gomes <adrianorg@debian.org>
2024-11-19 21:32:18 -08:00
Holger Hoffstätte
07069880a2 Fix warning about conflicting lseek/lseek64 prototypes
Clang rightfully complains about conflicting prototypes, as both lseek() variants
are redefined:

  syscall.c:394:10: warning: a function declaration without a prototype is deprecated
  in all versions of C and is treated as a zero-parameter prototype in C2x, conflicting
  with a previous declaration [-Wdeprecated-non-prototype]
        off64_t lseek64();
                ^
/usr/include/unistd.h:350:18: note: conflicting prototype is here
extern __off64_t lseek64 (int __fd, __off64_t __offset, int __whence)
                 ^
1 warning generated.

The point of the #ifdef is to build for the configured OFF_T; there is
no reason to redefine lseek/lseek64, which should have been found
via configure.

Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com>
2024-11-19 21:28:39 -08:00
Holger Hoffstätte
e55b190f4a Fix warning about missing bomb(..) prototype
Clang rightfully complains about invoking bomb(..) without a proper prototype:
  lib/pool_alloc.c:171:16: warning: passing arguments to a function without a prototype
  is deprecated in all versions of C and is not supported in C2x [-Wdeprecated-non-prototype]
                (*pool->bomb)(bomb_msg, __FILE__, __LINE__);
                             ^
1 warning generated.

Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com>
2024-11-19 21:28:39 -08:00
Holger Hoffstätte
48d51a1370 Fix __m128i_u / __m256i_u alignment
Building with clang-16 complains with:
./simd-checksum-x86_64.cpp:204:25: warning: passing 1-byte aligned argument to
  16-byte aligned parameter 1 of '_mm_store_si128' may result in an unaligned pointer
  access [-Walign-mismatch]

Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com>
2024-11-19 21:28:39 -08:00
Wayne Davison
f654e47691 Mention latest NEWS. 2024-11-14 11:59:12 -08:00
Wayne Davison
83ad3533d4 Always check old==new, even for missing array size. 2024-11-14 11:53:40 -08:00
Wayne Davison
fa28c5d693 Improve packaging/var-checker.
Make var-checker compare the variable type of the extern vars to ensure
that they are all consistent. Fix the remaining issues.
2024-11-14 11:42:24 -08:00
Carlo Marcelo Arenas Belón
62bb9bba02 acls: correct type/size for orig_umask
Since 05278935 (- Call mkdir_defmode() instead of do_mkdir(). - Define
orig_umask in this file, not options.c. - Made orig_umask a mode_t, not an
int., 2006-02-24), the type for the global was changed, and therefore on
systems where sizeof(mode_t) != sizeof(int), writes or reads to them will
overflow to adjacent bytes.

Change the type to the one used everywhere else and avoid this problem.

While at it, silence again a warning that is being triggered by
Apple's clang 15.
2024-11-14 07:15:14 +11:00
Wayne Davison
6601510425 Mention more NEWS. 2024-11-09 11:05:16 -08:00
Wayne Davison
f7ac7ffd16 Some minor option/prompt tweaks. 2024-11-05 17:50:16 -08:00
Wayne Davison
4320c25fcc More helper script improvements. 2024-11-05 13:44:17 -08:00
Wayne Davison
4490fb8660 Add some info for making a release. 2024-11-05 13:03:04 -08:00
Wayne Davison
475ca7d43c Add helper script for updating samba files. 2024-11-05 12:42:42 -08:00
Wayne Davison
7c3c54b132 Don't force zsh use. 2024-11-05 11:20:28 -08:00
Wayne Davison
bcf0738f98 Indentation tweak. 2024-11-05 11:20:17 -08:00
Wayne Davison
8749ec6436 Update to newer artifact version. 2024-11-05 11:14:46 -08:00
Wayne Davison
42e2b56c4e Another cast when multiplying integers. 2024-11-05 11:01:03 -08:00
Wayne Davison
0902b52f66 Some checksum buffer fixes.
- Put sum2_array into sum_struct to hold an array of sum2 checksums
  that are each xfer_sum_len bytes.
- Remove sum2 buf from sum_buf.
- Add macro sum2_at() to access each sum2 array element.
- Throw an error if a sums header has an s2length larger than
  xfer_sum_len.
2024-10-29 23:06:34 -07:00
vincent sgherzi
9615a2492b added apple silicon path details 2024-05-29 11:19:19 +10:00
Wayne Davison
4592aa770d More tweaks for Actions.
- When a .github/workflows/*.yml file changes, skip running unaffected
  builds.
- We need git to be installed for git-version.h generation.
2024-04-10 13:24:09 -07:00
Wayne Davison
8bc363cc9f Separate the builds and make Cygwin always run. 2024-04-10 13:02:34 -07:00
Wayne Davison
a9a3155756 Work around pkg install issue.
The xxhash, lz4, and zstd libraries aren't getting installed on FreeBSD.
[buildall]
2024-04-10 12:45:26 -07:00
Wayne Davison
fcc79836b8 Get fetch-depth:0 right. 2024-04-10 12:30:05 -07:00
Wayne Davison
804411b7fd Get rid of gensend target & cached git version.
- 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]
2024-04-10 12:23:58 -07:00
Wayne Davison
0b1b2a3ff4 Get the "dev" suffix right. 2024-04-10 11:53:17 -07:00
Wayne Davison
50bdf9685d Remove duplicate paragraph. 2024-04-10 11:51:59 -07:00
Charalampos Mitrodimas
3f2a38b011 CI: added Solaris build
Signed-off-by: Charalampos Mitrodimas <charmitro@posteo.net>
2024-04-09 07:34:26 +10:00
Wayne Davison
5510255f12 Tweak maintainer messaging. 2024-04-08 13:16:12 -07:00
Wayne Davison
56a039b04a Changes for 3.3.1dev. 2024-04-08 13:15:16 -07:00
Andrew Tridgell
7bc3be2b9e CI: fixed rules for when to trigger 2024-04-08 15:50:47 +10:00
Andrew Tridgell
411c4789df support: added install_deps_ubuntu.sh
convenient way to bootstrap quickly
2024-04-08 15:32:16 +10:00
Andrew Tridgell
231b239f30 check for stpcpy
needed for popt on macos
2024-04-08 15:31:36 +10:00
Andrew Tridgell
4c8683c875 update to popt 1.19 2024-04-08 15:31:36 +10:00
Rose
85c906f964 Silence unused var warning
recv_ida_entries still needs to be called regardless, so we cannot take that out. Let's just quiet the compiler instead.
2024-04-07 09:28:03 +10:00
Christian Hesse
35f5a21a16 hint that a proxy can handle plain and ssl stream at the same time 2024-04-07 09:25:46 +10:00
Andrew Tridgell
99673f937f CI: added FreeBSD build 2024-04-07 08:07:50 +10:00
Andrew Tridgell
9505ac5945 removed old cirrus CI 2024-04-07 08:07:50 +10:00
Ivan Babrou
0dd25d4752 configure.ac: fix failing IPv6 check due to missing return type
Fixing this warning escalated to an error, resuting in no IPv6 support:

```
configure.sh:7679: checking whether to enable ipv6
configure.sh:7718: clang -o conftest -g -O2 -DHAVE_CONFIG_H -Wall -W   conftest.c  >&5
conftest.c:73:1: error: type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int [-Wimplicit-int]
main()
^
int
1 error generated.
configure.sh:7718: $? = 1
configure.sh: program exited with status 1
```
2024-04-07 07:46:47 +10:00
Wayne Davison
ae3e13ba99 Update github links. 2024-04-06 10:33:42 -07:00
Wayne Davison
6c8ca91c73 Preparing for release of 3.3.0 [buildall] 2024-04-06 09:30:21 -07:00
Wayne Davison
079e74a30f Some year updates. 2024-04-06 09:22:29 -07:00
Wayne Davison
abc3c74652 Mention latest changes in NEWS. 2024-04-06 09:22:29 -07:00
Jiri Slaby
99ab59464b exclude: fix crashes with fortified strlcpy()
Fortified (-D_FORTIFY_SOURCE=2 for gcc) builds make strlcpy() crash when
its third parameter (size) is larger than the buffer:
  $ rsync -FFXHav '--filter=merge global-rsync-filter' Align-37-43/ xxx
  sending incremental file list
  *** buffer overflow detected ***: terminated

It's in the exclude code in setup_merge_file():
  strlcpy(y, save, MAXPATHLEN);

Note the 'y' pointer was incremented, so it no longer points to memory
with MAXPATHLEN "owned" bytes.

Fix it by remembering the number of copied bytes into the 'save' buffer
and use that instead of MAXPATHLEN which is clearly incorrect.

Fixes #511.
2024-04-06 08:41:41 -07:00
Grant Gardner
a47ae6fad9 typo in rsyncd.conf.5.md 2024-04-06 09:53:47 +11:00
Wayne Davison
2f9b963aba Make --max-alloc=0 safer.
Always do size checking in my_alloc(), even for `--max-alloc=0`.
2023-06-27 09:01:25 -07:00
Wayne Davison
3476caea3e Convert mnt-excl into python. 2023-05-22 08:29:15 -07:00
Wayne Davison
6f3c5eccee Fix old stats bug that counted devices as symlinks. 2023-05-16 22:44:54 -07:00
Wayne Davison
79fda35342 A couple more NEWS improvements. 2023-05-08 08:15:42 -07:00
Wayne Davison
cd76993461 Mention updated config files. 2023-05-04 08:45:42 -07:00
zhangwenlong
05a683900f update config.guess config.sub (#478)
- curl -sL -o config.guess 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'
- curl -sL -o config.sub 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'

Signed-off-by: Wenlong Zhang <zhangwenlong@loongson.cn>
2023-05-04 08:41:52 -07:00
Wayne Davison
86f41650fb A couple spelling tweaks; tweak order. 2023-04-30 17:28:50 -07:00
Wayne Davison
9a06b2edb0 Preparing for release of 3.3.0pre1 [buildall] 2023-04-29 09:01:43 -07:00
Wayne Davison
273dced284 Update the NEWS. 2023-04-29 09:01:09 -07:00
Wayne Davison
b6e2321973 Mention that --crtimes support is spotty. 2023-04-29 08:21:19 -07:00
Wayne Davison
fe95a9369a Fix issue with trailing --sparse --inplace blocks.
Fixes #450.
2023-04-29 07:56:27 -07:00
Wayne Davison
6ae7f4085a Add --force-link-text to md-convert. 2023-04-23 08:26:32 -07:00
Wayne Davison
0f599d3641 Fix overflow of sum2 buffer for sha1 rolling checksums.
Fixed #353.
2023-04-22 08:49:50 -07:00
Wayne Davison
c3d3b49d72 Make use of .UR & .UE for links. 2023-04-22 08:40:27 -07:00
Wayne Davison
c69dc7a5ab Tweak shell protection news to mention a few more characters. 2023-03-30 12:56:49 -07:00
dogvisor
2c82006b1f add rrsync option to enforce --ignore-existing (#461)
The `-no-overwrite` rrsync option disallows the updating of existing files for incoming rrsync copies.
2023-03-30 12:55:56 -07:00
Wayne Davison
0698ea9aeb Fix flist string comparison issue in tr_TR.utf-8 locale. 2023-02-05 19:46:45 -08:00
Wayne Davison
90df93e446 Don't call memcmp() on an empty lastdir. 2023-01-08 21:35:39 -08:00
Wayne Davison
5c93dedf45 Add backtick to SHELL_CHARS. 2023-01-04 21:52:48 -08:00
Wayne Davison
f1e3434b59 Trust the sender on a local transfer. 2022-12-01 20:24:17 -08:00
Wayne Davison
48252c3c2b A couple manpage links. 2022-11-23 07:59:12 -08:00
Wayne Davison
5b67ff2a86 Improve [global] module documentation. 2022-11-22 22:55:52 -08:00
Wayne Davison
8990ad96de Duplicate argv data before poptFreeContext(). 2022-11-22 22:21:15 -08:00
Wayne Davison
0f44e864d4 Another python conversion. 2022-11-20 09:38:12 -08:00
Wayne Davison
ab0d5021ed Convert a few more scripts to python3. 2022-11-16 00:10:09 -08:00
Wayne Davison
7402896523 Tweak an older NEWS item to be a bit clearer. 2022-11-09 16:04:02 -08:00
Wayne Davison
5374994089 Avoid quoting of tilde when it's a destination arg. 2022-11-05 09:22:10 -07:00
Wayne Davison
526366129a Upgrade verion of actions. 2022-11-02 23:54:41 -07:00
Wayne Davison
556a2c5bc2 Check for EVP_MD_CTX_copy in crypto lib instead of MD5_Init. 2022-10-25 21:55:53 -07:00
Wayne Davison
27feda0436 Call OpenSSL_add_all_algorithms() on older openssl versions. 2022-10-25 09:04:45 -07:00
Wayne Davison
bf96cd314c Init the checksum choices before the daemon auth. 2022-10-25 09:04:45 -07:00
Wayne Davison
1b2688807d Fix protocol <= 29 daemon auth if openssl is handling md4. 2022-10-24 08:38:00 -07:00
Wayne Davison
08ec80ac65 Cygwin needs stdout flushed. [buildall] 2022-10-22 12:04:32 -07:00
Wayne Davison
6b5ae825db Preparing for release of 3.2.7 [buildall] 2022-10-20 17:57:22 -07:00
Wayne Davison
3b719d1d6e Improve JSON output a bit more. 2022-10-20 17:50:06 -07:00
Wayne Davison
ebe1af749c Make use of -VV when checking rsync capabilities. 2022-10-20 09:09:26 -07:00
Wayne Davison
de6848ed97 Re-run the exclude test using lsh.sh pull.
The exclude.test file continues to run local copies (which are a special
kind of "push") while the exclude-lsh.test symlink runs a a "pull" using
the lsh.sh script as the "remote" shell.
2022-10-19 20:58:29 -07:00
Wayne Davison
42f8386823 Improve --mkpath a bit more. 2022-10-16 12:27:30 -07:00
Wayne Davison
ad6245f394 Include "buildall" flag in the release commit. 2022-10-16 12:14:46 -07:00
Wayne Davison
ca980b5863 Yet another manpage tweak. 2022-10-16 12:10:05 -07:00
Wayne Davison
677aa0dc91 Fix version verification when "\|" doesn't work in sed. 2022-10-16 11:14:15 -07:00
Wayne Davison
025596757c Silence autoconf warnings. 2022-10-16 10:28:58 -07:00
Wayne Davison
449d9bf950 Make the new manpage section better. 2022-10-16 10:26:39 -07:00
Wayne Davison
35ecec972a A few more manpage clarifications. 2022-10-15 17:02:18 -07:00
Alexponomarev7
d76cabe54f Fix autoconf help strings (#389) 2022-10-15 16:54:27 -07:00
Wayne Davison
b5544a95b1 Add info on single-file copying; tweak --mkpath. 2022-10-12 10:16:47 -07:00
Wayne Davison
11bd2a4fd6 Tweak NEWS. 2022-10-10 08:55:09 -07:00
Wayne Davison
6ba434de5c Change fgrep to grep. 2022-10-06 22:18:48 -07:00
Wayne Davison
3296351442 Fix validation of "preN" git tags for git-version.h. 2022-10-02 11:43:46 -07:00
Wayne Davison
0088a85aeb Mention smart-make in a comment. 2022-10-02 11:26:44 -07:00
Wayne Davison
4923c4dc0c Mention the --list-only output format. 2022-10-02 10:35:23 -07:00
Wayne Davison
76c4fa8b54 Mention latest changes. 2022-10-02 10:03:00 -07:00
Wayne Davison
25efa10802 Complain if the destination arg is empty. 2022-10-02 09:54:59 -07:00
Wayne Davison
fdf5e577f5 Read a 4-byte mtime as unsigned (old-protocol).
When conversing with a protocol 29 or earlier rsync, the modtime values
are arriving as 4-byte integers.  This change interprets these short
values as unsigned integers, allowing the time that can be conveyed to
range from 1-Jan-1970 to 7-Feb-2106 instead of the signed range of
13-Dec-1901 to 19-Jan-2038.  Given that we are fast approaching 2038,
any old-protocol transfers will be better served using the unsigned
range rather than the signed.

It is important to keep in mind that protocol 30 & 31 convey the full
8-byte mtime value (plus nanoseconds), allowing for a huge span of time
that is not affected by this change.
2022-10-02 09:54:54 -07:00
Wayne Davison
19bd0dd340 Use newer protocol to avoid mtime corruption. 2022-10-01 08:04:00 -07:00
Wayne Davison
ed4b3448be Preparing for release of 3.2.7pre1 2022-09-30 12:36:21 -07:00
Wayne Davison
4d44bf122d A few more doc tweaks & comment tweaks. 2022-09-30 12:34:58 -07:00
Wayne Davison
6af27a538e Explicitly ignore snprintf() return value. 2022-09-30 11:50:20 -07:00
Wayne Davison
f9e29dfb09 More NEWS updates. 2022-09-25 13:20:06 -07:00
Wayne Davison
591de7ce5c Fix compile w/o openssl; disable sha256 & sha512 for --checksum. 2022-09-25 12:42:09 -07:00
Wayne Davison
c8c627756a Avoid test -e. 2022-09-20 21:50:07 -07:00
Wayne Davison
46884e4ff6 Fix a link. 2022-09-20 00:12:49 -07:00
Wayne Davison
97e02bf21a Some "use chroot" improvements.
- The sanitize_paths variable was set too often. It only needs to be set
  when the "inner" path is not "/".  This change avoids sanitizing &
  munging things for a path=/ module just because chroot is off.
- The default for "use chroot" is now "unset" instead of "true".  When
  unset it checks if chrooting works, and if not, it proceeds with a
  sanitized copy instead of totally failing to work.  This makes it
  easier to setup a non-root rsync daemon, for instance.  It will have
  no effect on a typical Linux root-run daemon where the default will
  continue to use chroot (because chrooting works).  A config file can
  explicitly set "use chroot = true | false" to force the choice.
- Try to improve the "use chroot" manpage.
2022-09-20 00:08:16 -07:00
Wayne Davison
77d762ced8 Stop importing "re". 2022-09-19 22:36:49 -07:00
Wayne Davison
5b27d2e6f3 Pre-compute FILE_SUM_EXTRA_CNT. 2022-09-15 10:25:32 -07:00
Wayne Davison
7e634f5355 We always add a slash now that path is cleaned. 2022-09-15 10:13:20 -07:00
Kenneth Finnegan
8fe8cfd60a Use string length diff heuristic to skip Levenshtein Algo (#369)
When using the --fuzzy option to try and find close matches locally,
the edit distance algorithm used is O(N^2), which can get painful on
CPU constrained systems when working in folders with tens of thousands
of files in it.

The lower bound on the calculated Levenshtein distance is the difference
of the two strings being compared, so if that difference is larger than
the current best match, the calculation of the exact edit distance between
the two strings can be skipped.

Testing on the OpenSUSE package repo has shown a 50% reduction in the CPU time
required to plan the rsync transaction.
2022-09-15 10:12:02 -07:00
Wayne Davison
7a2dbf7177 Make the implied-arg adding for --relative more efficient. 2022-09-14 08:20:41 -07:00
Wayne Davison
8449539a0f More NEWS updates. 2022-09-14 07:57:44 -07:00
Wayne Davison
71c2b5d0e3 Fix exclusion of /. with --relative. 2022-09-14 07:14:13 -07:00
Wayne Davison
f3f5d8420f Tweak a define. 2022-09-14 07:13:24 -07:00
Wayne Davison
8b1b81e054 Use UNSUPPORTED instead of PROTOCOL for various validation checks. 2022-09-13 23:38:01 -07:00
Wayne Davison
e8161304f7 Use hashlittle2() for xattr hashing
- The non-zero key code is now in hashtable.c
- The hashtable_create() code already checks for OOM
2022-09-13 22:43:01 -07:00
Wayne Davison
b012cde1ed Add hashlittle2() and ensure the hash is never 0
It's probably time for a faster hash algorithm, but this gives us
the free 64-bit hashing that things like the xattr code can use.
2022-09-13 22:37:39 -07:00
Wayne Davison
464555ea92 Fix really silly bug with --relative rules. 2022-09-13 20:56:32 -07:00
Wayne Davison
df904f590e Improve var ref. 2022-09-13 20:55:58 -07:00
Wayne Davison
208d6ad1cd NEWS tweak. 2022-09-13 20:54:35 -07:00
Wayne Davison
51dae12c92 Update NEWS. 2022-09-12 22:04:33 -07:00
Wayne Davison
950730313d Fix bug with validing remote filter rules. 2022-09-12 22:02:00 -07:00
Wayne Davison
81c5c81381 Mention the filename when unpack_smb_acl() returns an error. 2022-09-11 10:04:26 -07:00
Wayne Davison
a6a0d2f77c Require a newer protocol to specify the digest list. 2022-09-10 22:12:24 -07:00
Wayne Davison
418e38a878 Talk about the new daemon greeting line. 2022-09-10 22:12:23 -07:00
Wayne Davison
b2dcabdbb9 Improve output of "N-bit" items in json data. 2022-09-10 21:10:10 -07:00
Wayne Davison
ad53a9b5a0 Also change dashes in the dict var names to make jq use easier. 2022-09-10 17:30:54 -07:00
Wayne Davison
1750288660 A few more tweaks. 2022-09-10 16:35:20 -07:00
Wayne Davison
087fffaa2b Unify older protect-args capability to secluded-args name. 2022-09-10 16:17:32 -07:00
Wayne Davison
5c1fa2a21d Use dict for capabilities & optimizations in json output. 2022-09-10 16:01:53 -07:00
Wayne Davison
0efa63f2e6 Use JSON output if --version (-V) is repeated (client side only). 2022-09-10 13:14:42 -07:00
Wayne Davison
ae16850dc5 Add support for various SHA checksum digests
The main purpose of the SHA checksums are to allow the daemon auth code
to pick a stonger digest method when negotiating the auth digest to use.
However, the SHA digests are also available for use in file checksums,
should someon really want to use one of them.

The new digests are listed from strongest to weakest at the start of the
daemon auth list, giving them the highest priority.

The new digests are listed from weakest to strongest near the end of the
checksum list, giving them the lowest priority of use for file
checksums.
2022-09-10 11:48:44 -07:00
Wayne Davison
7e2711bb2b Improve various things in the checksum code
- Size flist checksum data to hold the active size, not the max.
- Add a negotiated hash method to the daemon auth code.
- Use EVP for all openssl digests. This makes it easy to add more
  openssl digest methods and avoids deprecation warnings.
- Support a way to re-enable deprecated digests via openssl conf
  file and allow a default file to be configured.
- Supply a simple openssl-rsync.cnf file to enable legacy digests.
2022-09-10 11:39:37 -07:00
Wayne Davison
b8c2fde3a5 Try freebsd-13-1 to fix weird wget issue. 2022-09-09 13:16:27 -07:00
Wayne Davison
1f12b196fd When deleting a tag, del in the patches dir too. 2022-09-09 12:59:22 -07:00
Wayne Davison
bafe73dd5c Start 3.2.7dev going. 2022-09-09 12:59:17 -07:00
Wayne Davison
db5bfe67a5 Preparing for release of 3.2.6 2022-09-09 12:23:37 -07:00
Wayne Davison
5447d038c6 Mention a potential bash security issue with openssh forced commands. 2022-09-09 10:48:52 -07:00
Wayne Davison
711773631b A few more minor tweaks. 2022-09-01 22:07:54 -07:00
Wayne Davison
bf3e49b453 Improve the daemon info a bit. 2022-09-01 22:01:18 -07:00
Wayne Davison
034d5e8770 Tweak a couple links. 2022-08-23 21:12:26 -07:00
Wayne Davison
ad8917437a Mention that copying to a case-ignoring filesystem can be problematical. 2022-08-23 21:02:41 -07:00
Wayne Davison
1b664d30e4 Fix an unreleased bug handling a leading dot. 2022-08-23 19:38:41 -07:00
Wayne Davison
ea38f34d02 Another spelling fix. 2022-08-23 15:44:48 -07:00
Wayne Davison
44d4727664 Fix a link. 2022-08-23 15:30:37 -07:00
Wayne Davison
1f2f413167 Fix split limits. 2022-08-23 15:30:32 -07:00
Wayne Davison
0a09df2c5e Rename --protect-args to --secluded-args. 2022-08-23 14:56:23 -07:00
Wayne Davison
cc861cf8c0 More NEWS tweaks. 2022-08-22 08:15:35 -07:00
Wayne Davison
5183c0d6f0 Add safety check for local --remove-source-files.
A local_server copy now includes the dev+ino info from the destination
file so that the sender can make sure that it is not going to delete
the destination file.  Fixes mistakes such as:

  rsync -aiv --remove-source-files dir .
2022-08-21 10:19:23 -07:00
Wayne Davison
706bff9176 Mention the latest changes. 2022-08-20 08:30:22 -07:00
Wayne Davison
2c1204032b Make sure that the configure.sh script is up-to-date in a release. 2022-08-19 09:49:52 -07:00
Wayne Davison
8adc2240e0 Mention copy-devices. 2022-08-19 08:56:49 -07:00
Wayne Davison
84ad83525b Remove unneeded var. 2022-08-19 08:56:04 -07:00
Wayne Davison
9a3449a398 Stop enabling -pedantic-errors. 2022-08-18 17:33:54 -07:00
Wayne Davison
3258534e99 Change name_num_obj struct to use a name_num_item pointer. 2022-08-18 17:33:25 -07:00
Samuel Henrique
b94bba4036 Fix typos on manpage (#358) 2022-08-17 21:50:43 -07:00
Wayne Davison
a182507bef Fix issue when the files-from list isn't nl terminated. 2022-08-17 16:57:39 -07:00
Wayne Davison
2895b65f53 Another mkgitver tweak & mention it in NEWS. 2022-08-16 08:56:36 -07:00
Wayne Davison
def595c559 Remove useless comment. 2022-08-15 21:56:37 -07:00
Wayne Davison
68b1ce1dc3 Only run git describe if .git exists in the $srcdir. 2022-08-15 21:52:13 -07:00
Wayne Davison
5a4116e553 Start 3.2.6dev going. 2022-08-15 19:01:56 -07:00
Wayne Davison
024bf1d831 Do more path cleaning in add_implied_include(); make u.slash_cnt more accurate. 2022-08-15 18:55:54 -07:00
Wayne Davison
db4f919ebe Allow ~/remote/./path with -R if the path has /./ in it. 2022-08-15 18:55:05 -07:00
Wayne Davison
6ac2c7b682 We must use the CSUM_CHUNK size in the non-openssl MD4 code. 2022-08-14 14:03:02 -07:00
Wayne Davison
0e10163a9d Fix another dot-dir implied arg issue. 2022-08-14 12:27:25 -07:00
Wayne Davison
5fcf20ee9d Preparing for release of 3.2.5 2022-08-14 10:15:08 -07:00
Wayne Davison
fc72d2b771 Update the NEWS. 2022-08-14 10:12:06 -07:00
Wayne Davison
b7ea3fcd19 Ensure a dynamically linked xxhash lib is >= 0.8 for XX3. 2022-08-14 10:09:40 -07:00
Wayne Davison
9cb7529ba6 Remove some trailing whitespace. 2022-08-13 10:53:53 -07:00
Wayne Davison
55ad8757ec Make a --trust-sender a bit clearer. 2022-08-10 16:34:26 -07:00
Wayne Davison
3e4b01173a One more doc tweak. 2022-08-10 08:48:27 -07:00
Wayne Davison
2f1d1d5cac Add packaging note. 2022-08-10 08:42:22 -07:00
Wayne Davison
4c0a4067df Fix handling of a character class with an escaped closing bracket. 2022-08-09 17:55:03 -07:00
Wayne Davison
8550142804 Be a little paranoid. 2022-08-09 17:55:03 -07:00
Wayne Davison
97f40754ba A couple manpage tweaks. 2022-08-09 17:55:01 -07:00
Wayne Davison
cff8f04477 Add --trust-sender option. 2022-08-09 11:45:56 -07:00
Wayne Davison
db8034f12e Escape leading tilde char when "~" or with -R. 2022-08-09 11:42:32 -07:00
Wayne Davison
c86763dc38 Fix handling of daemon module names in file-list verification; convert some while loops to for loops. 2022-08-09 11:37:47 -07:00
Wayne Davison
5ce575b157 Preparing for release of 3.2.5pre2 2022-08-08 22:50:31 -07:00
Wayne Davison
fabef23bea Fix --relative when copying an absolute path. 2022-08-08 21:30:43 -07:00
Wayne Davison
685bf58046 Handle files-from args that span 2 buffers. 2022-08-08 21:18:10 -07:00
Wayne Davison
9e2921fce8 A fix for the zlib fix. 2022-08-08 20:05:10 -07:00
Wayne Davison
80d8f7c7cb Handle a "[foo]" arg matching the literal wildcards. 2022-08-08 19:57:28 -07:00
Wayne Davison
38e1b075b4 Fix some issues with backslashed wildcards in args. 2022-08-08 19:26:05 -07:00
Wayne Davison
d659610afc Handle a trailing "/." at the end of a source arg. 2022-08-08 17:36:36 -07:00
Wayne Davison
6cafc1f8bf Update the NEWS. 2022-08-07 09:59:43 -07:00
Wayne Davison
788f11ea6a Fix zlib bug with a large gzip header extra field
From zlib commit eff308af425b67093bab25f80f1ae950166bece1.
Fixes CVE-2022-37434.
2022-08-07 09:34:26 -07:00
Wayne Davison
b7fdc9ef0e Make sure that --read-batch doesn't try to check args. 2022-08-07 08:56:39 -07:00
Wayne Davison
0d8cc26044 Some md-convert doc tweaks. 2022-08-03 09:55:51 -07:00
Jakub Wilk
2955888468 Fix typos in NEWS (#339) 2022-08-02 11:31:04 -07:00
Wayne Davison
0773cecc1f Preparing for release of 3.2.5pre1 2022-08-01 18:51:07 -07:00
Wayne Davison
8e33586359 Tweaks to allow for a release. 2022-08-01 18:50:28 -07:00
Wayne Davison
da5c72da4b More NEWS. 2022-08-01 18:36:22 -07:00
Wayne Davison
2f7c583143 A few more minor tweaks. 2022-08-01 18:36:21 -07:00
Wayne Davison
51fd4993ba Avoid the getgroups program when cross-compiliing. 2022-08-01 09:00:34 -07:00
Wayne Davison
e37bfdb445 Make sure sign is consistend in 2 gid comparisons. 2022-08-01 08:29:15 -07:00
Wayne Davison
3d7015afa2 A few more minor changes. 2022-08-01 07:45:57 -07:00
Wayne Davison
7e5424b806 More improvements to file-list checking
- Avoid implied rules on generator and (with extra certainty) on server
- Add -R implied-directory path elements as directory includes
- Log about extra file-list checking using a new --debug=FILTER3 level
2022-08-01 07:00:51 -07:00
Wayne Davison
43f70b961e The latest NEWS. 2022-07-31 17:47:45 -07:00
Wayne Davison
b7231c7d02 Some extra file-list safety checks. 2022-07-31 17:46:34 -07:00
Wayne Davison
15c34f0a8c A few more minor doc tweaks. 2022-07-11 13:54:59 -07:00
Wayne Davison
d1e42ffa16 A few minor fixes. 2022-06-19 17:35:18 -07:00
Wayne Davison
36f489c211 Link to rsyncd.conf page server-setup details. 2022-06-19 16:55:18 -07:00
Wayne Davison
defe2287aa Improve the filter intro. 2022-06-19 16:45:43 -07:00
Wayne Davison
112bef11ad Improve filter discussion. 2022-06-19 16:28:45 -07:00
Wayne Davison
b38780f3fd Some proxy improvements (mainly). 2022-06-19 11:42:25 -07:00
Wayne Davison
5f33238f06 Some clarifications about transfer rules. 2022-06-19 11:42:24 -07:00
Wayne Davison
3592ac3c02 Include bsd/strings.h if it exists
Some systems apparently put strlcpy() into a separate bsd/strings.h file
without putting the function into a separate library. Thus, configure
finds that the function exists for linking but the build does not have
the declaration (which rsync only supplies if it is also supplying its
own version of the function).
2022-06-19 10:11:28 -07:00
Yuri Chornoivan
c897b16f32 Fix minor typos (#327) 2022-06-19 09:14:36 -07:00
Wayne Davison
4f741addbd Fix configure's "signed char" check
When pedantic errors are enabled, SIGNED_CHAR_OK was no longer
being set correctly. This would cause the checksum code to use
"char" instead of "signed char", and if the default for a "char"
was unsigned, the checksum code would fail to compute the right
hash values.  Fixes bug #317.
2022-06-18 10:23:32 -07:00
Wayne Davison
355b81d8bc Avoid -pedantic-errors on non-x86 for the moment. 2022-06-18 09:42:16 -07:00
Wayne Davison
6f35553372 Fix grabbing version value in configure. 2022-06-01 17:41:28 -07:00
Wayne Davison
71090b7e2c Improve discussion of old-args in advanced usage. 2022-05-14 16:41:44 -07:00
Wayne Davison
2ab2ee166e Make md-convert --test work again. 2022-05-06 19:37:40 -07:00
Wayne Davison
1e858e39e6 Manpage improvements. 2022-05-06 17:42:55 -07:00
Wayne Davison
664639e349 Use the maintainer's timezone for translating the manpage date. 2022-05-06 17:42:54 -07:00
Wayne Davison
517b9d91fc Setup for 3.2.5dev. 2022-05-06 17:24:54 -07:00
Wayne Davison
0ac7ebceef Preparing for release of 3.2.4 2022-04-15 13:31:16 -07:00
Wayne Davison
85c56b2603 The latest news. 2022-04-11 09:50:58 -07:00
Wayne Davison
10aeb75cea Add debugging comment about read_buf_(). 2022-04-11 09:50:31 -07:00
Simon Deziel
d41bb98c09 systemd: restart daemon on-failure (#302)
man 5 systemd.service:
> Setting this to on-failure is the recommended choice for long-running services

Partial fix for https://bugzilla.samba.org/show_bug.cgi?id=13463

Signed-off-by: Simon Deziel <simon@sdeziel.info>
2022-04-11 09:08:11 -07:00
Yoichi NAKAYAMA
2fda51692b Specify log format to avoid malfunctions and unexpected errors. (#305)
Solve the following problems:
* mishandling of commit message lines similar to committer lines
* UnicodeDecodeError with commit messages that cannot be interpreted
  as utf-8
2022-04-11 08:57:19 -07:00
Michal Ruprich
1de71e8a78 Fix for CVE-2018-25032 in zlib (#306) 2022-04-11 08:50:50 -07:00
Wayne Davison
60dd42be60 Handle linking with a zlib with external read_buf. 2022-04-11 08:29:54 -07:00
Wayne Davison
d821e4cbfb Preparing for release of 3.2.4pre4 2022-03-27 14:59:57 -07:00
Wayne Davison
8aa465117f Add new & improved --copy-devices option. 2022-03-27 14:04:59 -07:00
Wayne Davison
8977815f5d Some --write-device fixes. 2022-03-27 12:52:26 -07:00
Wayne Davison
a48c20c97c Combine some alt-dest tests. 2022-03-26 10:01:12 -07:00
Wayne Davison
601f47436f Rename compare-dest test. 2022-03-26 10:01:10 -07:00
Sam Mikes
ef76d6cfa5 Extract unlink_and_reopen from copy_file (#294)
* add tests to exercise copy_file

* Extract new function unlink_and_reopen from copy_file

The argument `ofd` to copy_file is always set to -1 unless
`open_tmpfile()` is called at generator.c:909

This change
 * removes assignment to a function argument
 * renames argument `ofd` to `tmpfilefd` in line with existing uses
 * extracts a new function `unlink_and_reopen` which is static to util1.c
 * rewrites header comments for copy_file
2022-03-26 09:14:10 -07:00
Wayne Davison
96ed4b47b9 Some word fixes. 2022-03-26 08:58:51 -07:00
Wayne Davison
13c4019e94 Also ignore a root-level rrsync file. 2022-03-13 10:45:09 -07:00
Wayne Davison
b7b387b1f7 Add FALLTHROUGH comment. 2022-03-13 09:31:44 -07:00
Wayne Davison
7569edfaef Use ac_includes_default in largefile support test. 2022-03-09 18:38:03 -08:00
Wayne Davison
55b2a06812 Test newer FreeBSD. 2022-03-03 17:26:47 -08:00
Wayne Davison
b81a509556 Make asm use more selectable
- Make the SIMD ASM code off by default. Use configure --enable-simd-asm
  to enable.
- Allow MD5 ASM code to be requested even when OpenSSL is handling MD4
  checksums. Use configure --enable-md5-asm to enable.
2022-03-03 17:00:57 -08:00
Wayne Davison
26f4dbe12c Change usage (--version) output to note when ASM isn't really being used. 2022-02-21 16:41:50 -08:00
Sam Mikes
b3f1970f18 Fix wording in RSYNC_PORT section (#293)
Fix wording from 'does is not read' -> 'is not read'
2022-02-21 14:00:45 -08:00
Wayne Davison
c51da9174f Build Cygwin on windows-2022 with newer python. [buildall] 2022-02-09 14:00:13 -08:00
Wayne Davison
81f71f6f29 Add a CAUTION message to --debug=FILTER for trailing whitespace. 2022-01-27 08:53:41 -08:00
Wayne Davison
48e7005554 Add a couple more --rsync-path opts to the test. [buildall] 2022-01-20 10:51:13 -08:00
Wayne Davison
2b3e68814b Specify the rsync that lsh.sh should run. [buildall] 2022-01-20 09:02:02 -08:00
Wayne Davison
cc83294316 Preparing for release of 3.2.4pre3 2022-01-18 23:47:45 -08:00
Wayne Davison
08c8375acb Tweak rrsync rules in the Makefile. 2022-01-18 23:13:22 -08:00
Wayne Davison
824a057935 Add some arg-escaping tests. 2022-01-18 22:47:05 -08:00
Wayne Davison
d91ddb97d1 Don't backslash-escape args for a local transfer. 2022-01-18 22:47:05 -08:00
Wayne Davison
5bb637ca04 Add missing ">". 2022-01-18 22:47:03 -08:00
Wayne Davison
142aba00d5 Silence some symlink mode-change failures. 2022-01-17 22:23:31 -08:00
Wayne Davison
8687e44d10 Fix a broken link & make a tweak. 2022-01-17 20:44:16 -08:00
Wayne Davison
0bd8e85185 Facilitate the next release. 2022-01-17 19:43:43 -08:00
Wayne Davison
00a5ab2364 Tweak some rrsync rules for cleanup & release. 2022-01-17 18:52:49 -08:00
Wayne Davison
f44e76b65c Handle html link targets in a better way. 2022-01-17 18:11:45 -08:00
Wayne Davison
1174d97072 Fix --old-args interaction with a daemon
Ensure that a remote rsync daemon will not split a filename arg unless
the user asked for `--old-args`.
2022-01-17 18:11:03 -08:00
Wayne Davison
d9eaffe564 Complain about --old-args with --protect-args. 2022-01-17 18:09:36 -08:00
Wayne Davison
6197385d1f More man & NEWS enhancements, including linking to env vars. 2022-01-17 18:09:34 -08:00
Wayne Davison
d07272d631 More man page and NEWS improvements.
- Add link targets for all option choices, not just the first one.
- Tweak cross-link arg format.
- Add more links, including some in the latest NEWS.
- Split out a few numbered lists.
2022-01-16 10:47:36 -08:00
Wayne Davison
e2a011d9d0 Fix some typos mentioned in the fossies report. 2022-01-16 10:33:22 -08:00
Wayne Davison
76dc7d0a76 It's OK to capitalize rsync at the start of a sentence. 2022-01-15 21:44:26 -08:00
Wayne Davison
7e94e52144 Some NEWS.html improvements.
- Improve NEWS heading's link targets using version info.
- Optimize regex compilation.
- Make sure every link target is unique.
- Allow link targets to start with a number.
2022-01-15 21:07:34 -08:00
Wayne Davison
5ef7e3c9c5 Remove <a name=...> tags. 2022-01-15 20:55:54 -08:00
Wayne Davison
d2cc1149b3 Get md-convert to output the release html files in the right dir. 2022-01-15 19:12:03 -08:00
Wayne Davison
c3b553a93f Preparing for release of 3.2.4pre2 2022-01-15 17:21:01 -08:00
Wayne Davison
eb0b41587c Use standard "git diff" for full diff highlighting support. 2022-01-15 17:20:11 -08:00
Wayne Davison
3c0bb7ff51 Even more man page improvements. 2022-01-15 17:13:31 -08:00
Wayne Davison
995ce7198b Man page improvments, including html cross-links. 2022-01-15 16:31:54 -08:00
Wayne Davison
38ffa522f6 A few more man page format tweaks. 2022-01-14 14:27:31 -08:00
Wayne Davison
8898aecb21 Make it easier to get section links. 2022-01-14 13:55:22 -08:00
Wayne Davison
f08505e92b Add more link targets to html man pages. 2022-01-13 23:44:30 -08:00
Wayne Davison
c1e8809a8f Tweak a caveat. 2022-01-13 23:31:43 -08:00
Wayne Davison
6130c4fa3c Display ??:??:?? when a time estimate gets too big. 2022-01-13 08:22:25 -08:00
Wayne Davison
8c4ceb3b86 Avoid a -8 in the progress output's remaining time
If the double "remain" value is so large that it overflows an int, make
the estimated seconds output as :00 instead of :-8.  Similar for the
estimated remaining minutes.  Support larger hours values.
2022-01-12 20:04:32 -08:00
Wayne Davison
30a5909544 Some symlink improvements to the man page. 2022-01-12 16:43:40 -08:00
Wayne Davison
e841944b47 Change manpage headings in html to use h2 tags with an id target. 2022-01-12 16:43:22 -08:00
Wayne Davison
635d8c0632 A repeated --old-args does more escape disabling. 2022-01-09 18:20:23 -08:00
Wayne Davison
6b8db0f644 Add an arg-protection idiom using backslash-escapes
The new default is to protect args and options from unintended shell
interpretation using backslash escapes.  See the new `--old-args` option
for a way to get the old-style splitting.  This idiom was chosen over
making `--protect-args` enabled by default because it is more backward
compatible (e.g. it works with rrsync). Fixes #272.
2022-01-09 17:47:24 -08:00
Wayne Davison
3b2804c815 Tweak a comment. 2022-01-09 14:03:31 -08:00
Wayne Davison
ff1792edf1 Improve --copy-links description. 2022-01-09 12:38:36 -08:00
Wayne Davison
b985123d2e Allow someone to specify scratchbase=FOO for runtests.sh. 2022-01-09 11:40:41 -08:00
Wayne Davison
c983279020 Improve rrsync usage and some more NEWS tweaks. 2022-01-03 00:47:19 -08:00
Wayne Davison
ee9199b542 More NEWS improvements. 2022-01-03 00:18:59 -08:00
Wayne Davison
f1a6998df2 Only send the --no-W kluge to a receiver. 2022-01-02 23:51:35 -08:00
Wayne Davison
3e44bbd313 Preparing for release of 3.2.4pre1 2022-01-02 15:13:19 -08:00
Wayne Davison
4adfdaaf12 Tweak stderr handling for older BackupPC versions
This makes the default for a protocol-28 server process be --stderr=client
instead of --stderr=errors.  See rsync's github issue #95.
2022-01-02 14:48:04 -08:00
Wayne Davison
4a7ba3cfaf A couple man page improvements. 2022-01-02 14:43:30 -08:00
Rodrigo Osorio
ffbca80ca2 Time-limit options are not being checked enough (#179)
The `--stop-at`, `--stop-after`, and `--time-limit`` options should have their
limit checked when receiving and sending data, not just when receiving.
Fixes #177.
2022-01-02 14:37:27 -08:00
Wayne Davison
c11467af36 Some compression improvements.
The compression level of the first file in the transfer no longer sets
the level for all files that follow it.  Document that per-file level
switching has no current effect (except for a global "dont compress = *"
rule in the daemon).
2021-12-31 12:21:13 -08:00
Wayne Davison
13cfe6406f Add error-code ignoring options to atomic-rsync. 2021-12-30 12:29:14 -08:00
Wayne Davison
8e77ece0ee Tweak the rrsync man page. 2021-12-30 12:29:09 -08:00
Marco Nenciarini
ffec7fe109 Fix rrsync directory normalization (#268)
Fix an off-by-one in the `args.dir_slash_len` variable that leads to base every absolute path on `/`
2021-12-30 08:59:17 -08:00
Wayne Davison
e07f8fb863 Add a default single-access lock. 2021-12-27 17:57:53 -08:00
Wayne Davison
8cf9dbb742 Change args to maybe-make-man. 2021-12-27 17:40:31 -08:00
Wayne Davison
3008e7c226 Include "rrsync" in "all" target when --with-rrsync was used. 2021-12-27 15:52:11 -08:00
Wayne Davison
a2b630c0bb Unify md parsing scripts & improve non-man html conversions. 2021-12-27 14:24:05 -08:00
Wayne Davison
5b1baa7a2e Rename md2man. 2021-12-27 13:42:19 -08:00
Wayne Davison
7f8cf771b7 Add more backticks. 2021-12-27 13:11:23 -08:00
Wayne Davison
b00e99c529 Ignore the built rrsync man-page files. 2021-12-27 12:10:31 -08:00
Wayne Davison
a76e32f949 Test --with-rrsync configure option & put rrsync into the artifacts. 2021-12-26 14:58:16 -08:00
Wayne Davison
512acd125e Use mallinfo2, when available, and use %zd for size_t values on C99.
An exhanced version of pull request #265.
2021-12-26 14:25:53 -08:00
Wayne Davison
72adf49ba8 rrsync improvements
- Convert rrsync to python.
- Enhance security of arg & option checking.
- Reject `-L` (`--copy-links`) by default.
- Add `-munge` and `-no-del` options.
- Tweak the logfile line format.
- Created an rrsync man page.
- Use `configure --with-rrsync` if you want `make install` to install
  rrsync and its man page.
- Give lsh more rrsync testing support.
2021-12-26 12:29:00 -08:00
Wayne Davison
73ceea6ad2 Convert atomic-rsync to python. 2021-12-20 17:36:33 -08:00
Wayne Davison
39c3ae0ea3 Convert munge-symlinks to python. 2021-12-20 16:41:16 -08:00
Wayne Davison
ed19ea05fe Make rrsync default to munged symlinks. 2021-12-20 15:16:30 -08:00
Wayne Davison
dc1b9febf1 Add options to assist in localhost rrsync testing. 2021-12-20 15:08:20 -08:00
Wayne Davison
d9015da151 Add --munge-links rsync option; convert to python. 2021-12-20 13:51:50 -08:00
Wayne Davison
1f0e62f139 Improve a couple support scripts:
- rsync-no-vanished now avoids joining stdout & stderr, avoids affecting
  a non-client run, and gets the rsync status code correctly.
- rsync-slash-strip now avoids affecting a non-client run.
2021-11-13 10:39:09 -08:00
Andrew Aladjev
7d830ff52f improved cross compilation detection (#252) 2021-11-07 11:45:49 -08:00
Issam Maghni
8f383e8987 shell: test -a|o is not POSIX (#250) 2021-11-07 10:23:01 -08:00
Wayne Davison
ca538965d8 Add closing backticks that Itzoke pointed out. 2021-11-07 10:11:12 -08:00
Wayne Davison
e4669b81ae Add the --info=NONREG setting. 2021-11-03 09:35:50 -07:00
Wayne Davison
1b9308b727 More NEWS changes. 2021-10-30 15:58:01 -07:00
Wayne Davison
80c64dc3b3 Fix the ability to read the user's numeric locale. 2021-10-29 20:06:06 -07:00
Wayne Davison
be3d6c0fbb Update the NEWS. 2021-10-19 21:10:59 -07:00
Wayne Davison
7956070f2b Make --chown|--usermap|--groupmap imply -o|-g (as appropriate). 2021-10-19 21:10:12 -07:00
Wayne Davison
d0f34b5a76 Allow a "%scope" suffix on the client's ipv6 addr.
Hopefully fixes bug #239.
2021-10-17 13:58:57 -07:00
Achim Leitner
84498104bf Linux: Handle protected_regular in inplace writes (#241)
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).
2021-10-17 13:00:24 -07:00
Wayne Davison
378a0a634f Add more skipped verifications. [buildall] 2021-10-17 12:45:45 -07:00
Wayne Davison
ac08fa74f3 Tweak output about skipped tests. 2021-10-17 12:07:03 -07:00
Wayne Davison
d5d4ae51ee Change RSYNX_MAX_SKIPPED to RSYNC_EXPECT_SKIPPED. 2021-10-17 11:34:07 -07:00
Wayne Davison
0f87eafa2f A couple minor tweaks. 2021-10-13 10:39:44 -07:00
Wayne Davison
3af00277ee We need stat memcpy. 2021-10-10 14:01:59 -07:00
Wayne Davison
b774dbc1c0 Improve --omit-dir-times & --omit-link-times
The code now better handles skipping time setting on dirs and/or links
when --atimes and/or --crtimes is specified without --times.
2021-10-10 13:39:09 -07:00
Wayne Davison
296352ecb0 Tweak atime/crtime code a bit more. 2021-10-10 12:43:11 -07:00
Wayne Davison
11a9b62322 Avoid spurious warning about "code" var not being initialized. 2021-10-10 10:05:26 -07:00
Wayne Davison
452ef78517 Unify on "path" vs "fname" arg naming. 2021-10-10 09:53:35 -07:00
Wayne Davison
0d1b48893a Change do_lchmod() back to a swtich with some better ENOTSUP & ENOSYS logic. 2021-10-10 09:32:43 -07:00
Wayne Davison
ec8a05f653 Some packaging improvements. 2021-10-10 09:28:24 -07:00
Wayne Davison
78b5bc6629 Enable --atimes on macOS. 2021-10-02 15:23:30 -07:00
Wayne Davison
f41cdc75a1 Check ro in set_create_time() for Cygwin too. 2021-10-02 11:39:41 -07:00
Wayne Davison
c8e7c4b352 Avoid an issue where the size of st_dev != dev_t. 2021-10-01 14:21:26 -07:00
Wayne Davison
bff084c10a Always run mkgitver prior to a build
Some hosts were not running `mkgitver` when they should, so tweak the
script to only update the timestamp when the file's data changes and
then always run the script when performing a build.
2021-10-01 14:17:53 -07:00
Wayne Davison
16c8b05f11 Add more NEWS updates. 2021-10-01 13:40:07 -07:00
Wayne Davison
15dd2058fd Change do_chmod to always try lchmod() first (when possible). 2021-10-01 13:28:57 -07:00
Wayne Davison
c27180c044 Add a couple more options to rrsync. 2021-10-01 13:24:51 -07:00
Wayne Davison
050fdd4126 Allow the script to be run from inside the packaging dir. 2021-10-01 13:23:30 -07:00
Jindřich Makovička
ae1f002999 Reduce memory usage (#235)
In 2004, an allocation optimization has been added to the file
list handling code, that preallocates 32k of file_struct pointers
in a file_list. This optimization predates the incremental
recursion feature, for which it is not appropriate anymore. When
copying a tree containing a large number of small directories,
using the incremental recursion, rsync allocates many short
file_lists. Suddenly, the unused file_struct pointers can easily
take 90-95% of the memory allocated by rsync.
2021-10-01 12:04:59 -07:00
Wayne Davison
3911c23866 Tweak SIMD & ASM option defaults. 2021-09-27 11:09:43 -07:00
Wayne Davison
3814dbb0f4 Make cygwin's curl grab the gist file. [buildall] 2021-09-27 10:34:22 -07:00
Wayne Davison
82f023d7e3 Add --fsync option (promoted from patches). 2021-09-27 10:30:00 -07:00
Wayne Davison
ec57c57baf Help avoid a --sparse --inplace bug in older rsyncs. 2021-09-27 10:16:15 -07:00
Wayne Davison
354fa581c1 Fix typo. 2021-09-26 19:27:46 -07:00
Wayne Davison
d881814a35 Don't allow a broken samba host to cause gensend to fail. 2021-09-26 19:11:24 -07:00
Wayne Davison
ad048d78ac Allow $host_cpu to be amd64 in addition to x86_64. 2021-09-26 19:11:20 -07:00
Wayne Davison
109dbc0b75 Rename cmdormsg -> cmd-or-msg. 2021-09-26 18:55:46 -07:00
Wayne Davison
745ecf2825 Fix a couple variable typos. 2021-09-26 18:50:32 -07:00
Shark64
265785b7b9 x86-64 AVX2 assemby implemenation of get_checksum1() (#174) 2021-09-26 18:16:55 -07:00
Wayne Davison
97f4d48a07 configure improvements
- Make SIMD & ASM only default to enabled on linux for now (due to
  FreeBSD & MacOS issues).
- Improve the enable/disable help messages so that they don't look
  wrong when the opposite --enable-X/--disable-X arg is specified.
2021-09-26 18:11:06 -07:00
Wayne Davison
3cc7f0ba43 Tweak a comment. 2021-09-26 17:23:33 -07:00
a1346054
dde4695136 Minor cleanup (#214)
- use `grep -E` and `grep -F` (`egrep` and `fgrep` are non-standard)
- use same hashbang style for all test scripts
- use explicit comparisons in test scripts
- remove redundant ; from test scripts
- make test script not executable, just like all the other scripts
- unify codestyle across all test scripts
- make openssl license exception clearer by having it at the top
- use modern links in COPYING. The text now matches:
  https://www.gnu.org/licenses/gpl-3.0.txt
- fix typo
2021-09-26 16:57:55 -07:00
Fabian H
3337930292 add ssl/tls key option (#216)
Improves rsync-ssl configurability.
2021-09-26 16:44:00 -07:00
zgpmax
44cc148907 Fix spelling of nanosecond (#220)
Hyphenating the word just makes it harder to find when nanosecond support was added.
2021-09-26 16:42:02 -07:00
TomasKorbar
b2e16facd4 Eventually add write permission when setting extended attributes (#212)
* Eventually add write permission when setting extended attributes

When we need to set extended atributes of file which does not
allow write then temporarily add write permission and after
attributes are set, remove it again.

Resolves #208

Co-authored-by: Wayne Davison <wayne@opencoder.net>
2021-09-26 16:34:15 -07:00
Wayne Davison
4fd662fea9 Remove duplicate include. 2021-09-26 16:28:35 -07:00
Wayne Davison
12c058698b Add gist update logic to gensend target. 2021-09-26 14:57:22 -07:00
Wayne Davison
33095916ec Make use of a git gist instead of the samba website. 2021-09-26 12:09:17 -07:00
Wayne Davison
5818cf8596 Add missing INET6 check. 2021-09-26 11:25:18 -07:00
Jonathan Davies
1fa0bd1e87 configure.ac: Test IPv6 compatibility instead of relying on library probes (#206)
Legacy configure behaviour was to detect IPv6 support through known IPv6
capable version of common standard libraries. Now: it runs a POSIX test
to determine if IPv6 is usable (in case it has not been disabled).

Patch originally from Pierre-Olivier Mercier <nemunaire@nemunai.re>.

Signed-off-by: Jonathan Davies <jpds@protonmail.com>
2021-09-26 11:25:06 -07:00
Fabian H
592c6bc3e5 add missing - in certopt (#210)
otherwise openssl will give an error and not accept is as argument
2021-08-16 15:52:39 -07:00
Natanael Copa
efc81c93a9 Add test and fix regression for --delay-updates (#192) (#204)
Fixes regression introduced with commit 3a7bf54ad5 (A resumed
partial-dir file is transferred in-place.)
2021-07-28 09:10:55 -07:00
Wayne Davison
35d4f6737a Update the options in rrsync. 2021-07-09 11:58:27 -07:00
Wayne Davison
291a042b3e Support --crtimes on Cygwin. 2021-07-08 18:59:26 -07:00
Wayne Davison
9dad3721a9 Make whole-line comments clearer. 2021-07-04 12:42:51 -07:00
Wayne Davison
dbb1c2d10c Set whole_file = 0 when whole_file < 0. Fixes issue 114. 2021-07-04 12:15:16 -07:00
Wayne Davison
e8e34ed6fb Need to also check stdout_format_has_i in some INFO-NAME checks. 2021-06-27 11:34:57 -07:00
Wayne Davison
c529782a8d Fix compiling without ftruncate. 2021-06-27 09:45:41 -07:00
juleslagarde
2dfd48492e fix man page typo
Fix a copy/paste error that should be referring to deletions.
2021-06-16 22:39:45 -07:00
Wayne Davison
a6bdf313f2 Unset DISPLAY in environment.
Without a DISPLAY var, ssh won't try to forward X11 when making an
ssh connection.  This patch also makes use of setenv() and unsetenv()
if they are available.
2021-05-01 09:14:51 -07:00
Bart S
915685e01b Updated GLIBC check in configure.ac (#175)
The current GLIBC check does not consider we may see glibc 3.0 in the future.
2021-05-01 08:23:25 -07:00
Wayne Davison
05540220a9 Fix plural of --group option. 2021-04-03 21:09:14 -07:00
Wayne Davison
75158e1086 Fix git-set-file-times's handling of staged changed files. 2021-03-15 09:35:39 -07:00
Wayne Davison
676537cf29 Switch to using image_family for Cirrus CI. 2021-03-02 19:16:52 -08:00
Wayne Davison
d857fd42ac Install bash on FreeBSD CI. 2021-03-02 19:09:23 -08:00
Wayne Davison
5856b71eb7 Update FreeBSD CI to 12.2. 2021-03-02 19:02:08 -08:00
Wayne Davison
57adb2973a See if explicitly installing m4 gets FreeBSD CI a newer gm4 version. 2021-03-02 14:13:54 -08:00
Wayne Davison
ead44adcd3 Allow the generator's msg iobuf to get bigger too. 2021-02-25 12:28:18 -08:00
Wayne Davison
d3085f7add Rename util.c to util1.c
Fixes an issue where the Makefile's glob of *.c could sort util.c &
util2.c in an order that depends on the current collation setting.
2021-02-25 09:14:33 -08:00
Wayne Davison
1da64c37e8 A few Cygwin build tweaks. [buildall] 2021-02-10 08:07:03 -08:00
Wayne Davison
ef36b097bf Stop checking for gmake in build scripts
Since a non-cygwin gmake trips up the github cygwin action, let's just
require that the user put a good "make" early on their path (a simple
`ln -s `which gmake` ~/bin/make` with the right $PATH works fine).
2021-02-04 20:51:04 -08:00
Wayne Davison
ec3833c96e Add optional netgroup.h include for NetBSD hosts. 2021-02-01 16:31:28 -08:00
Wayne Davison
25dfc2c41d Some pip-releated tweaking. 2021-02-01 09:00:06 -08:00
Wayne Davison
d5b7889d40 Add a link to the man page to the README. 2021-02-01 08:47:44 -08:00
Wayne Davison
74561d70b5 Check for netinet/ip.h after including netinet/in.h. 2021-01-31 11:11:07 -08:00
Wayne Davison
83f7372369 A couple "make" tweaks. 2021-01-31 11:10:38 -08:00
Wayne Davison
8c3de35b0b Put 0 in parens to silence an Xcode warning. 2021-01-31 09:28:34 -08:00
Wayne Davison
ec1d5d564c Add --with-nobody-user=FOO configure option. 2021-01-15 07:38:49 -08:00
Wayne Davison
26befd9c6c Cygwin python3 is now 3.8 w/o commonmark lib. [buildall] 2021-01-01 10:02:49 -08:00
James Cook
a28c4558c5 Fix spelling error in man page. (#124)
trasnferred -> transferred
2020-12-10 09:43:04 -08:00
Wayne Davison
ed6a0dc7c2 Fix a typo. 2020-12-09 22:35:16 -08:00
Wayne Davison
9dd62525f3 Work around glibc's lchmod() issue a better way. 2020-11-29 09:40:03 -08:00
Wayne Davison
ada588a7a8 Include stdlib.h for exit() and consult HAVE_* macros more. 2020-11-29 09:13:09 -08:00
Wayne Davison
286e164ed6 Tweak cmd_txt routines in the packaging scripts. 2020-11-01 11:27:08 -08:00
Wayne Davison
85b8dc8aba Force HAVE_LCHMOD off for Linux (for now). 2020-10-30 16:37:52 -07:00
Wayne Davison
0748800118 Use the right powershell env syntax. [buildall] 2020-10-07 14:02:28 -07:00
edo
b7fab6f285 Allow cross-compilation with SIMD (x86_84) (#104)
Replace runtime SIMD check with a compile-only test in case of
cross-compilation.

You can still use '--enable-simd=no' to build x86_64 code without
SIMD instructions.
2020-10-06 22:33:57 -07:00
Wayne Davison
9fc7deab0d Update CI builds to new path-setting idiom. 2020-10-06 22:28:17 -07:00
Wayne Davison
b115bc8a5d Silence a few more warnings. 2020-09-29 16:05:29 -07:00
Wayne Davison
cd018c7a4c Use a better -Wno-pedantic heuristic. 2020-09-29 15:30:20 -07:00
Wayne Davison
9fce0eb5ab Avoid some pedantic errors & old warnings. 2020-09-29 14:51:45 -07:00
Wayne Davison
33e94849b1 Handle early gcc versions that don't understand -Wno-pedantic. 2020-09-29 14:27:59 -07:00
Wayne Davison
8f1511184a Make gcc die on init overflow of an array.
- Use -pedantic-errors with gcc to make an array-init fatal.
- Fix all the extra warnings that gcc outputs due to this option.
- Also add -Wno-pedantic to gcc if we're using the internal popt
  code (since it has lots of pedantic issues).
2020-09-29 13:18:28 -07:00
Wayne Davison
acca9d43d3 Expand the max name_num_item list size. 2020-09-29 12:57:32 -07:00
Wayne Davison
58f464f4da Change --info=skip2 messages & add info on attr changes. 2020-09-23 09:33:29 -07:00
Wayne Davison
7eb59a9152 Change from $build_cpu to $host_cpu as edo1 suggested. 2020-09-22 17:19:48 -07:00
Wayne Davison
740ed11aa8 Make the extra info on the "exists" messages optional. 2020-09-22 16:45:07 -07:00
Wayne Davison
d2a97a7ab4 Various file comparison improvements
- Rename unchanged_file() to quick_check_ok().
- Enhance quick_check_ok() to work with non-regular files.
- Add a get_file_type() function to the generator.
- Use the new functions in the generator code to make the logic simpler.
- Fix a bug where the `--alt-dest` functions were not checking if a
  special file fully matched the non-permission mode bits before
  deciding if we have found an alt-dest match.
- Enhance the `--info=skip --ignore-existing` output to include extra
  info on if the existing file differs in type or passes the standard
  quick-check logic.
- Add `--info=skip2` that authorizes rsync to perform a slow checksum
  "quick check" when ignoring existing files. This provides the uptodate
  and differs info even if we need to checksum a file to get it.
2020-09-22 12:48:02 -07:00
Wayne Davison
15bc7ded39 More NEWS updates. 2020-09-21 19:17:59 -07:00
Wayne Davison
f0810068a6 A couple whitespace tweaks. 2020-09-21 18:42:21 -07:00
Shark64
7aa2f36317 optimize avx2 code (#102)
Optimize avx2 code using only intrinsic functions supported by older gcc versions.
2020-09-21 15:11:27 -07:00
Wayne Davison
9cd85b8496 Skip an append if sender's file gets shorter.
Fixes bug #90.  Similar to a pull request by Tomas Korbar.
2020-09-21 14:57:45 -07:00
Wayne Davison
f8dcd7d452 Improve the docs for --archive.
A slightly tweaked version of a patch from Richard Michael.
2020-09-21 14:07:48 -07:00
Wayne Davison
69530b406e Avoid output variance in protocol 29. 2020-09-21 13:45:42 -07:00
Wayne Davison
122b0fdc4f Check status of tests that pipe rsync's output & simplify output diffing. 2020-09-21 13:32:41 -07:00
Wayne Davison
b990d97d35 Put CAN_HARDLINK_SYMLINK info into --version output. 2020-09-21 13:17:15 -07:00
Wayne Davison
fd6839b746 Avoid spurious "is newer" messages with --update. 2020-09-21 11:07:42 -07:00
Wayne Davison
a79d9b22b1 Update the NEWS. 2020-09-08 22:31:18 -07:00
Wayne Davison
2613c9d98a Handle a --mkpath failure
Fixes bug #96 where --mkpath makes rsync complain when a dest path
exists but the path contains an alt-dest name for the single file.
2020-09-08 10:58:47 -07:00
Wayne Davison
27aff880a9 Use new xxhash lib in cygwin build. [buildall] 2020-09-07 19:42:08 -07:00
Wayne Davison
7b53e67d64 Try using the Windows version of curl. [buildall] 2020-09-07 15:11:32 -07:00
Wayne Davison
da956469a1 Another cygwin build attempt. [buildall] 2020-09-07 14:46:27 -07:00
Wayne Davison
9c59632d8b Improve a sentence about --stderr=all. 2020-09-07 14:42:35 -07:00
Wayne Davison
d1f458d383 Try cygwin build again. [buildall] 2020-09-07 14:23:39 -07:00
Wayne Davison
a35a900ac0 Add git-version.h to "gen" target. 2020-09-06 23:36:08 -07:00
Wayne Davison
f4c3969b63 Leave git-version.h out of GENFILES so it doesn't go in a release tar. 2020-09-06 23:27:28 -07:00
Wayne Davison
ee75e51f2f Allow git-version.h to be provided for the build
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.
2020-09-06 23:09:11 -07:00
Wayne Davison
9f9240b661 Set CXX_OK=no when cross compiling. 2020-09-03 10:07:36 -07:00
Wayne Davison
48885309c7 Create SECURITY.md 2020-09-02 14:49:20 -07:00
Wayne Davison
203b3d0143 Setup for 3.2.4dev. 2020-08-27 19:36:57 -07:00
Wayne Davison
25526eb3fe Simplify the compat logic for local_server
Change the logic in compat.c to construct the client_info string value
for a local copy so that the various checks of the string don't need to
make an exception for local_server.
2020-08-27 19:23:13 -07:00
Matt McCutchen
c3f7414c45 rsync-ssl: Verify the hostname in the certificate when using openssl. 2020-08-26 14:07:02 -07:00
Wayne Davison
4c4fce5107 Add some comments about protocol versions. 2020-08-07 16:25:12 -07:00
Wayne Davison
6816b31378 Simplify where version.h is included. 2020-08-06 21:10:46 -07:00
Wayne Davison
e94bad1c15 Preparing for release of 3.2.3 2020-08-06 20:57:26 -07:00
Wayne Davison
617d726a3d Tweak a comment. 2020-08-05 21:32:44 -07:00
Wayne Davison
020eda887f Change fetch depth. 2020-08-03 14:47:38 -07:00
Wayne Davison
b5f8021a12 Don't use --always to ensure a tag is in gitver. 2020-08-03 14:27:49 -07:00
Wayne Davison
7b6947576a Avoid a build fail when git isn't installed. 2020-08-03 14:20:08 -07:00
Wayne Davison
9375a8c4c2 Make my_alloc(NULL) use malloc instead of calloc. 2020-08-03 14:01:18 -07:00
Wayne Davison
7f5c4084c7 Use touch for proto.h-tstamp since one awk wasn't updating mtime. 2020-08-03 13:33:46 -07:00
Wayne Davison
6c89f00d1b Move SUPPORT_ATIMES to rsync.h. 2020-08-03 13:27:00 -07:00
Wayne Davison
dee0993286 Create usage.c for smaller awk-dep rebuilds. 2020-08-03 12:23:18 -07:00
Wayne Davison
47351c2b16 Use RSYNC_GITVER in more output 2020-08-03 10:46:31 -07:00
Wayne Davison
16b7670614 A couple more mkgitver tweaks
- Support git worktree checkouts (has non-dir .git)
- Use --always for someone who may be missing a tag.
2020-08-03 10:23:43 -07:00
Wayne Davison
72b2a81f90 Use --abbrev=8 instead of --tags. 2020-08-01 00:43:37 -07:00
Wayne Davison
d73c26d2b7 Put git version in a file for between-release versioning. 2020-08-01 00:15:06 -07:00
Wayne Davison
e83bbeb673 Don't make .PHONY the first target in a Makefile. 2020-07-30 18:58:34 -07:00
Wayne Davison
b6aa9c5cfe Make configure less annoying
- Improve configure's notifications around the new features.
- Improve the info about man page generation and fetching.
2020-07-30 18:33:58 -07:00
Wayne Davison
dfe3b77cb5 Some Makefile improvements.
- Improve distclean.
- Remove proto.h from GENFILES (we don't need to distribute it).
- Improve finddead target.
2020-07-29 11:51:55 -07:00
Wayne Davison
cbe3b2bfe5 Tweak a comment. 2020-07-29 11:51:52 -07:00
Wayne Davison
b1ae7fc941 INSTALL changes, including some Fedora packages. 2020-07-29 11:22:23 -07:00
Wayne Davison
8695bcc2b1 Preparing for release of 3.2.3pre1 2020-07-27 17:56:25 -07:00
Wayne Davison
4ae6f708b1 Need 3.2.3 line in table & tweak to release script. 2020-07-27 17:54:48 -07:00
Wayne Davison
14c4656fb8 A couple more NEWS updates. 2020-07-27 17:31:51 -07:00
Wayne Davison
13cec31f7f Set LANG to C to help with some remote build hosts. 2020-07-27 16:48:48 -07:00
Wayne Davison
5db7e4b1ee Use linkat() if available
Some OSes have a more capable linkat() function that can hard-link
syslinks, so use linkat() when it is available.
2020-07-27 16:36:55 -07:00
Wayne Davison
54693fa992 Add a few more skip-compress suffixes. 2020-07-27 15:56:48 -07:00
Wayne Davison
3f83bcb4af Make the --append* options have more warnings. 2020-07-27 15:21:50 -07:00
Wayne Davison
e1e546d67e Don't allow a completely empty source arg. 2020-07-27 14:49:51 -07:00
Wayne Davison
3714084f48 Mention an implied option. 2020-07-27 13:49:51 -07:00
Wayne Davison
eb2c3a5e18 Replace a couple calloc() calls with new_array0(). 2020-07-26 23:30:50 -07:00
Wayne Davison
f6967eca58 Complain about a missing/non-dir --temp-dir. 2020-07-26 02:01:30 -07:00
Wayne Davison
8455bf66c2 Don't include config.h in proto.h rule. 2020-07-26 01:40:55 -07:00
Wayne Davison
00e59e01e3 Mention awk/gawk/nawk dependency. 2020-07-26 01:40:43 -07:00
Wayne Davison
91eaffe13f Mention --protect-args in --chown info. 2020-07-26 01:02:07 -07:00
Wayne Davison
2066024981 Fix issue where rdev major could get out of sync
If the receiving side read a hard-linked device, it needs to set the
value of rdev_major to the value it snags from the hard-linked data
because the sender set their rdev_major value for that file entry.
2020-07-26 00:11:28 -07:00
Wayne Davison
d274b2096f Have release script use patch-update --make (not --shell) 2020-07-25 10:52:49 -07:00
Wayne Davison
0327a2526b Fix a grammar error. 2020-07-25 10:40:46 -07:00
Wayne Davison
f43412f1d5 More spelling fixes. 2020-07-25 10:34:26 -07:00
Wayne Davison
b9010ec617 Fix some spelling errors. 2020-07-25 10:21:50 -07:00
Wayne Davison
21ecc833ea Change new stderr options to --stderr=MODE. 2020-07-25 10:03:32 -07:00
Wayne Davison
f9bb8f76ee Change daemon variable & simplify some option code
- Rename daemon_over_rsh -> daemon_connection since it is also used to
  indicate if a non-rsh daemon connection is active.
- Move the daemon-over-rsh exception out of server_options() to the one
  caller that needs that behavior.
- Don't allow noop_io_until_death() to be short-circuited when talking
  to a daemon over a socket, because it can't send errors via stderr.
2020-07-25 09:36:42 -07:00
Wayne Davison
a5a9f268fe Tweak NEWS & src_file(). 2020-07-24 23:30:32 -07:00
Wayne Davison
0a255771f4 Add --errors2stderr & --msgs2protocol options. 2020-07-24 22:48:37 -07:00
Wayne Davison
e07d79ad50 Handle the first run of configure; prefer gmake. 2020-07-24 17:37:01 -07:00
Wayne Davison
2f74eb7584 Change smart-rebuild to smart-make. 2020-07-24 17:25:09 -07:00
Wayne Davison
39741c7d50 Fix the setting of $make. 2020-07-24 16:49:55 -07:00
Wayne Davison
3f016888fd Add helper script for a smart rebuild. 2020-07-24 14:19:19 -07:00
Wayne Davison
e5a012c959 Link to the git blob for source files. 2020-07-24 13:20:11 -07:00
Wayne Davison
c3cf174e5e More changes to NEWS, README, INSTALL, & configure.ac 2020-07-24 12:38:25 -07:00
Wayne Davison
a0a7c9f2e3 Enable xattrs on Cygwin.
- Tweak configure.ac to have Cygwin use linux xattrs.
- Change CI setup to install attr packages on Cygwin.

[buildall]
2020-07-24 11:38:14 -07:00
Wayne Davison
a8f61ba937 Add Cygwin package info into INSTALL.md. 2020-07-24 11:37:50 -07:00
Wayne Davison
842d6edfdc Fix devices-fake test if rsync can't link specials
- Add info about hardlinked specials to --version output.
- Use "no hardlink-special" info to ensure that the devices-fake
  test will not fail.
2020-07-24 11:33:21 -07:00
Wayne Davison
92a8855ff3 Install python3 for cygwin [buildall] 2020-07-24 10:10:26 -07:00
Wayne Davison
def96fd7c4 Install python36 for cygwin [buildall] 2020-07-24 09:57:41 -07:00
Wayne Davison
f624a73bbc Change the --mkpath message. 2020-07-24 09:46:53 -07:00
Wayne Davison
93a373f6ba Some INSTALL improvements. 2020-07-24 09:42:49 -07:00
Wayne Davison
01742c07e6 Add --mkpath option. Fixes bugzilla bug 4621. 2020-07-23 20:54:38 -07:00
Wayne Davison
e00662f263 Add packages to INSTALL.md; put INSTALL.md on ftp site 2020-07-23 17:29:13 -07:00
Wayne Davison
491ddb08a4 Simplify md_parser assignment. 2020-07-23 17:28:34 -07:00
Wayne Davison
918cb39fed Fix multi-line code blocks. 2020-07-23 16:22:12 -07:00
Wayne Davison
1369fe43e1 Tweak ubuntu configure args. 2020-07-23 12:32:41 -07:00
Wayne Davison
150f3416ac Setup commonmark on Cygwin. 2020-07-23 12:20:40 -07:00
Wayne Davison
d8941be8cb Simplify the msgs2stderr default logic. 2020-07-23 12:15:50 -07:00
Wayne Davison
592059c8fd Improve error output for local & remote-shell xfers 2020-07-23 11:23:47 -07:00
Wayne Davison
37f4a23f60 Drop a superfluous "+". 2020-07-22 21:42:24 -07:00
Wayne Davison
27be94c889 A few more build improvements
Includes Ben's RSYNC_MAX_SKIPPED=3 suggestion for FreeBSD and a fix for
the artifact file list for Cygwin.
2020-07-22 21:01:01 -07:00
Wayne Davison
974f49e22a Add --crtimes option. 2020-07-22 12:12:18 -07:00
Wayne Davison
9f7506ac1b Improve --itemize-changes doc. 2020-07-22 11:30:00 -07:00
Wayne Davison
8779d6c8bb Switch to RSYNC_MAX_SKIPPED test setting. 2020-07-22 11:00:26 -07:00
Wayne Davison
96be713fd2 Update NEWS. 2020-07-21 12:37:56 -07:00
Wayne Davison
13d8fc9542 Avoid some extraneous parent-dir warnings
Don't complain about an absent parent dir if the current file is marked
as missing and there is a marked-as-missing entry for the parent dir.
2020-07-21 11:54:54 -07:00
Wayne Davison
f74473b151 Don't create a path for a file marked as missing. 2020-07-21 11:54:48 -07:00
Wayne Davison
5eda68f11b Tweak include syntax. 2020-07-20 18:45:36 -07:00
Wayne Davison
f635207347 Save the build into an artifact. 2020-07-20 14:44:35 -07:00
Wayne Davison
64f7e893f3 Ignore *.exe files (for Cygwin builds). 2020-07-20 14:43:06 -07:00
Wayne Davison
31556ec7a8 Use just $(...) instead of a mix of that and ${...}. 2020-07-20 14:42:13 -07:00
Wayne Davison
9ad3f4385f Make the daily build happen a few hours later. 2020-07-18 23:17:25 -07:00
Wayne Davison
e9899dbdb4 Add strict (no-skipping) checks and use them. 2020-07-17 11:20:04 -07:00
Wayne Davison
18cffa8aa9 A couple minor changes. 2020-07-17 10:56:22 -07:00
Wayne Davison
7e07a32504 Add the name converter daemon parameter.
This is based on the long-standing patch but with the protocol changed
to just use newlines as delimiters instead of null chars (since names
should not contain a newline AND it makes it easier to write a helper
script).  Lots of other small improvements and a better default value
for "numeric ids" when using "use chroot" with "name converter".
2020-07-17 10:30:59 -07:00
Wayne Davison
be11a496bb Run a daily build. 2020-07-16 22:04:40 -07:00
Wayne Davison
c6f5f0b505 Let's try cygwin again. (#69)
Setup an optional cygwin build that is currently triggered when a [buildall] is in the commit message (the build is currently quite slow).
2020-07-15 14:30:22 -07:00
Wayne Davison
f553da1730 GitHub artifact test didn't work. 2020-07-15 13:53:40 -07:00
Wayne Davison
40753bcbf7 Tweak the save path. 2020-07-15 13:42:36 -07:00
Wayne Davison
33df361d52 Avoid normal build on cygwin-save change. 2020-07-15 13:20:40 -07:00
Wayne Davison
f4db970718 Try to get cygwin-save to run. 2020-07-15 13:16:36 -07:00
Wayne Davison
ab3928898f Try to save cygwin install in an artifact. 2020-07-15 12:31:38 -07:00
Ben RUBSON
13f274fd02 Force git line endings (#68) 2020-07-15 10:20:52 -07:00
Wayne Davison
b51fe50e7f Tweak the workflows filename. 2020-07-14 21:47:50 -07:00
Wayne Davison
1829a2ee0d Disable cygwin for now. 2020-07-14 21:47:11 -07:00
Wayne Davison
65370a0f56 Try a couple different way to fix the build. 2020-07-14 21:32:41 -07:00
Wayne Davison
23213099e9 Try Cygwin build in actions. 2020-07-14 21:18:52 -07:00
Wayne Davison
25e08110d5 Let's try a Cygwin build on Cirrus. 2020-07-14 20:41:44 -07:00
Wayne Davison
95f683039d Mention the auto-build-save setup. 2020-07-14 20:25:02 -07:00
Ben RUBSON
129d7195ff Enable FreeBSD CI ssl (#66) 2020-07-14 13:22:55 -07:00
Wayne Davison
044339d6b4 Reduce the installed pkg items since they are so slow. 2020-07-13 16:26:58 -07:00
Wayne Davison
4c4fc7462a A few more NEWS & man tweaks. 2020-07-13 16:14:27 -07:00
Wayne Davison
22cb57ee20 Try using cmarkgfm. 2020-07-13 15:44:43 -07:00
Wayne Davison
4c0be4da13 Avoid a failed test on Cygwin. 2020-07-13 15:33:07 -07:00
Wayne Davison
4549855126 Search for cmark. 2020-07-13 14:09:24 -07:00
Wayne Davison
284c28c773 Add new code to recv_group_name() too. 2020-07-13 13:43:17 -07:00
Wayne Davison
d2406ae372 Give up on commonmark. 2020-07-13 13:42:28 -07:00
Wayne Davison
1e9c34972a Avoid a crash if id-0 doesn't exist. 2020-07-13 13:18:38 -07:00
Wayne Davison
116bd19324 One more commonmark try. 2020-07-13 13:12:39 -07:00
Wayne Davison
883de22c29 Avoid a test failure if id didn't work. 2020-07-13 12:59:22 -07:00
Wayne Davison
18f500a7a4 Try another way to get commonmark working. 2020-07-13 12:59:07 -07:00
Wayne Davison
d14b0ca4db Install commonmark on FreeBSD. 2020-07-13 12:18:13 -07:00
Wayne Davison
4156e7d464 Tweak lsh's Usage message & opening comment. 2020-07-13 12:01:00 -07:00
Wayne Davison
9e48da65c1 Search for commonmark pkg. 2020-07-13 12:00:56 -07:00
Wayne Davison
2cdf9416ee Tweak brew run. 2020-07-13 10:41:26 -07:00
Wayne Davison
cd0c83e485 Setup a macOS CI. 2020-07-13 10:38:17 -07:00
Wayne Davison
0e814e956c A couple more NEWS items. 2020-07-12 23:45:55 -07:00
Wayne Davison
f47e5a7732 Mention file & line on OOM and overflow errors.
Also simplify output of src file paths in errors & warnings when
built in a alternate build dir.
2020-07-12 23:25:21 -07:00
Wayne Davison
91fff802b9 Check for overflow the right way. 2020-07-12 22:45:01 -07:00
Wayne Davison
3c8ac20d63 Fix a typo. 2020-07-12 20:51:21 -07:00
Wayne Davison
38a521defd More NEWS tweaks. 2020-07-12 20:49:01 -07:00
Wayne Davison
2f13049600 Add "@netgroup" names to host matching. 2020-07-12 19:16:57 -07:00
Wayne Davison
af531cf787 Add the --stop-after & --stop-at options. 2020-07-12 18:32:41 -07:00
Wayne Davison
d495e343c0 A few word tweaks. 2020-07-12 12:38:12 -07:00
Ben RUBSON
de7e4d00ab Improve FreeBSD tests (#61)
Improve FreeBSD tests & use a ZFS mount for the CI's testtmp.
2020-07-12 12:36:02 -07:00
Wayne Davison
374cc1be74 Get my yaml continuation line right. 2020-07-11 16:22:48 -07:00
Wayne Davison
8b25488fe9 More CI tweaks
- Add to ubuntu build: make install & rsync-ssl test run
- Don't fail a build if samba.org daemon list has an issue.
2020-07-11 16:08:22 -07:00
Wayne Davison
4f5742baa0 Make sure FreeBSD has bash installed. 2020-07-11 15:51:05 -07:00
Wayne Davison
2b416de4ca More FreeBSD script separation. 2020-07-11 15:37:51 -07:00
Wayne Davison
1f41b7dca1 Add a little more FreeBSD testing. 2020-07-11 15:32:51 -07:00
Wayne Davison
486e7852db Add FreeBSD test & re-enable linux build. 2020-07-11 15:03:31 -07:00
Wayne Davison
a68a92793c Just disable md2man on FreeBSD for now. 2020-07-11 12:28:05 -07:00
Wayne Davison
a84e8aced7 Add 2 more FreeBSD pkg installs. 2020-07-11 12:22:05 -07:00
Wayne Davison
3bc2d9aeaa Add some FreeBSD pkg installs and pause on linux. 2020-07-11 12:11:13 -07:00
benrubson
6214c26bd3 Add FreeBSD CI 2020-07-11 11:54:33 -07:00
Wayne Davison
da7a350667 Some number & string improvements
- Use strdup(do_big_num(...)) to replace num_to_byte_string(...).
- Allow a ',' for a decimal point in a SIZE option in some locales.
- Get rid of old (now unused) strdup() compatibility function.
- Try harder to include the newline in a single error message write.
2020-07-11 11:39:36 -07:00
Wayne Davison
66ca4fc97b Allow --block-size's size to have a suffix.
Change the block_size global to be an int32.
2020-07-10 13:00:42 -07:00
Wayne Davison
7d63f8b249 Add missing "M" in SIZE suffixes; mention bytes are the default. 2020-07-10 09:59:17 -07:00
Wayne Davison
bb1365dd77 Fix see_token zstd case. 2020-07-10 09:47:16 -07:00
Wayne Davison
bcc273d460 Clean more built .h files. 2020-07-09 15:18:11 -07:00
Wayne Davison
a6da3c67f8 Must read the nsec val even w/o CAN_SET_NSEC. 2020-07-08 14:17:01 -07:00
Wayne Davison
ab110fc8fb Warning fixes & impossible-failure improvements
- Silence a couple warnings for less-common builds.
- Use a better impossible-failure idiom than assert(0).
2020-07-08 12:26:19 -07:00
Wayne Davison
7265d96116 Avoid non-updating proto.h on Alpine. 2020-07-07 23:50:55 -07:00
Wayne Davison
560b63b051 Avoid a test failure on Alpine. 2020-07-07 23:05:41 -07:00
Wayne Davison
0eb82a7c90 Fix xattr issue with MIGHT_NEED_PRE.
Fixes bugzilla 13113.
2020-07-07 20:07:31 -07:00
Wayne Davison
f92a5182fc Tweak the NEWS. 2020-07-07 19:50:13 -07:00
Wayne Davison
fb6fabc116 Fix an xattr free of the wrong object.
In uncache_tmp_xattrs() the code used to find the value to unlink,
update the single-linked list, and then free the wrong pointer.
This fixes bug #50.
2020-07-07 14:25:58 -07:00
Wayne Davison
c3269275a8 Don't use UNUSED() when an arg is used sometimes. 2020-07-07 13:12:51 -07:00
Wayne Davison
7e47855d47 Update the NEWS. 2020-07-07 12:38:20 -07:00
Wayne Davison
d2d6ad481a Allow --max-alloc=0 for unlimited. 2020-07-07 11:56:23 -07:00
Wayne Davison
5dcb49c7dd Allow --bwlimit=0 again. 2020-07-07 11:43:33 -07:00
Wayne Davison
19d8550cf4 One more TANDEM include. 2020-07-06 09:41:31 -07:00
Wayne Davison
7610f76aea Remove another file_struct kluge. 2020-07-06 09:31:22 -07:00
Wayne Davison
59cb358fda More TANDEM changes
- Handle a non-0 root uid.
- Handle alternate major/minor/MAKEDEV funcs.
- Other misc compatibility tweaks.
2020-07-06 00:05:46 -07:00
Wayne Davison
bb16db1747 Send the uid/gid 0 name since not all systems use 0 for root. 2020-07-05 22:51:12 -07:00
Wayne Davison
d6f0342a34 Change name map funcs to return a const char*. 2020-07-05 22:17:09 -07:00
Wayne Davison
6f6e5b51cc Some TANDEM ACL support. 2020-07-05 20:09:16 -07:00
Wayne Davison
28de25a664 Some whitespace & paren cleanup. 2020-07-05 20:09:13 -07:00
Wayne Davison
052b34dceb A bit more configure tweaking. 2020-07-05 19:16:32 -07:00
Wayne Davison
748b5c5d53 Some configure tweaks for TANDEM. 2020-07-05 19:08:44 -07:00
Wayne Davison
e1e4ffe057 Some C99 flexible array changes
- Use C99 flexible arrays when possible, and fall back on the existing
  fname[1] kluges on older compilers.
- Avoid static initialization of a flexible array, which is not really
  in the C standard.
2020-07-05 17:25:53 -07:00
Wayne Davison
1b53b2ff4b Tweak a comment. 2020-07-05 14:42:36 -07:00
Wayne Davison
da45cecfc8 Tweak a couple var names. 2020-07-05 14:22:09 -07:00
Wayne Davison
87b5d233e9 Setup for 3.2.3dev. 2020-07-05 09:26:40 -07:00
Wayne Davison
194cee671d Preparing for release of 3.2.2 2020-07-04 23:12:59 -07:00
Wayne Davison
b7c5520add Handle tweaked NEWS headings & protocol change. 2020-07-04 23:11:46 -07:00
Wayne Davison
2bee307592 Get rid of some superfluous lz4 code. 2020-07-04 16:13:00 -07:00
Wayne Davison
85e62c330d Tweak indentation. 2020-07-04 16:10:37 -07:00
Wayne Davison
0add026a5d Initialize values string in a more consistent spot. 2020-07-04 14:21:15 -07:00
Wayne Davison
f4184849c4 Use module_id more consistently after it is set. 2020-07-04 10:28:39 -07:00
Wayne Davison
565cde84a7 Don't turn off the user's open-noatime unless the module is forcing the value. 2020-07-04 10:28:38 -07:00
Paul Slootman
f0e670b4c6 Add "open noatime" module option to rsyncd.conf 2020-07-04 09:30:35 -07:00
Wayne Davison
ef8951779d Fix issue in --compress-level doc. 2020-07-03 11:58:36 -07:00
Wayne Davison
e285f8f9d2 Some NEWS and man page tweaks. 2020-07-02 22:33:40 -07:00
Wayne Davison
cb383673f6 Tweak a var name. 2020-07-02 09:34:08 -07:00
Wayne Davison
0768d620a5 Another table tweak. 2020-07-01 14:20:14 -07:00
Wayne Davison
d640d78f91 Change how protocol changes are mentioned; fix table in html. 2020-07-01 14:14:24 -07:00
Wayne Davison
544b3d8b3b A few more systemd tweaks. 2020-07-01 12:10:14 -07:00
Wayne Davison
ce12142c45 Don't set systemd ProtectHome=on by default. 2020-07-01 10:41:13 -07:00
Wayne Davison
c83a81ca38 Move name exceptions into the txt file. 2020-07-01 09:05:21 -07:00
Wayne Davison
d88db22ae8 Add support for the remaining parser types. 2020-07-01 08:15:12 -07:00
Wayne Davison
a1cc50ba96 Preparing for release of 3.2.2pre3 2020-06-30 23:17:50 -07:00
Wayne Davison
feb2fff894 Put the optimizations into their own list. 2020-06-30 19:31:59 -07:00
Wayne Davison
7d30490ef4 Simplify the daemon parameter definitions
The code now derives all the struct defines, default value assignments,
parser-param defines, and lp_foo() accessor functions from a single list
of daemon parameters.
2020-06-30 19:30:28 -07:00
Wayne Davison
317beebef8 Avoid crash of transfer logging w/default log format. 2020-06-30 12:16:52 -07:00
Wayne Davison
7a413c9722 Avoid strdup redefinition warning. 2020-06-30 08:27:20 -07:00
Wayne Davison
5be7363297 Avoid bloating the src-dir scan. 2020-06-29 21:45:56 -07:00
Wayne Davison
646784f0e5 Move the new target after "all". 2020-06-29 20:04:54 -07:00
Wayne Davison
18ed3f0279 More patch-update improvements; configure.ac tweak; Makefile tweaks. 2020-06-29 19:53:05 -07:00
418 changed files with 41068 additions and 14041 deletions

17
.gitattributes vendored Normal file
View File

@@ -0,0 +1,17 @@
* text=auto eol=lf
# The rsync-web/ subdirectory holds the project website source content
# (mirrors what gets pushed to https://rsync.samba.org). Exclude it from
# `git archive` output so the release source tarball produced by
# packaging/release.py step_7_tarball does not bloat with HTML the
# tarball doesn't need.
/rsync-web/ export-ignore
# old_versions/ holds static binaries of historical rsync releases, used by the
# version-mixing test suite (.github/workflows/ubuntu-version-mix.yml) to run
# the current code against a real old peer over the daemon / remote-shell.
# Mark the binaries as binary so the `text=auto eol=lf` rule above can't try to
# normalise line endings and corrupt them; export-ignore keeps them out of the
# release source tarball.
/old_versions/rsync_* binary
/old_versions/rsync_* export-ignore

43
.github/workflows/actionlint.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Lint GitHub Actions workflows
# Static-check the workflow YAML with rhysd/actionlint. Catches missing
# secrets, bad expressions, expression-type errors, unsupported runner
# images, and (via embedded shellcheck) common pitfalls in `run:` scripts.
# Trigger only on changes under .github/workflows/ so the rest of the
# matrix isn't billed when nothing here moves.
on:
push:
branches: [ master ]
paths:
- '.github/workflows/*.yml'
- '.github/actionlint.yaml'
- '.github/actionlint.yml'
pull_request:
branches: [ master ]
paths:
- '.github/workflows/*.yml'
- '.github/actionlint.yaml'
- '.github/actionlint.yml'
permissions:
contents: read
jobs:
actionlint:
runs-on: ubuntu-latest
name: actionlint
steps:
- uses: actions/checkout@v4
- name: install actionlint
# Pin a version so this job is reproducible; bump deliberately.
# The download script verifies a SHA256 of the release tarball.
run: |
bash <(curl --proto '=https' --tlsv1.2 -fsSL \
https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) \
1.7.12
echo "$PWD" >>"$GITHUB_PATH"
- name: actionlint --version
run: actionlint -version
- name: actionlint .github/workflows/*.yml
run: actionlint -color

83
.github/workflows/almalinux-8-build.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
name: Test rsync on AlmaLinux 8
# Older-LTS coverage on the Fedora/RHEL family to help with backporting
# security fixes. AlmaLinux 8 is the RHEL 8 rebuild and is the oldest
# active LTS in this family (RHEL 8 full support runs to 2029).
# GitHub Actions has no native runner for this family, so the job runs
# inside an almalinux:8 container hosted on ubuntu-latest.
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/almalinux-8-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/almalinux-8-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
container:
image: almalinux:8
name: Test rsync on AlmaLinux 8
steps:
- name: install git
# actions/checkout needs git in the container before the checkout step.
run: dnf -y install git
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
# PowerTools is needed for libzstd-devel etc; xxhash and lz4 dev
# headers live in EPEL on RHEL 8. The default python3 on RHEL 8
# is 3.6, which is too old for runtests.py (uses capture_output=
# / text= introduced in 3.7), so install python39 and point
# /usr/bin/python3 at it.
run: |
dnf -y install epel-release
dnf config-manager --set-enabled powertools
dnf -y install gcc gcc-c++ make autoconf automake m4 \
python39 python39-pip diffutils \
openssl openssl-devel \
attr libattr-devel acl libacl-devel \
zstd libzstd-devel \
lz4 lz4-devel \
xxhash xxhash-devel
alternatives --set python3 /usr/bin/python3.9
pip3 install commonmark
- name: configure
run: ./configure --with-rrsync
- name: make
run: make
- name: info
run: ./rsync --version
- name: check
# In the container we already run as root, so no sudo. The
# crtimes-not-supported skip matches the other Linux jobs;
# daemon-chroot-acl and proxy-response-line-too-long skip because
# the default (secure) transport opens no listening socket.
run: RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef make check
- name: check (TCP daemon transport)
# Second run exercising the real loopback-TCP daemon path.
run: ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: ssl file list
run: ./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: almalinux-8-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

View File

@@ -0,0 +1,121 @@
name: Build static rsync for Android
# Cross-compiles statically-linked rsync binaries with the Android NDK,
# suitable for dropping onto a phone (adb push / Termux) with no shared
# libraries. arm64-v8a covers all modern phones; armeabi-v7a covers older
# 32-bit devices. The binaries are uploaded as workflow artifacts.
#
# These are cross-compiled, so the test suite can't run here; we sanity
# check that each binary is the right architecture, is static, and that
# it executes (`--version`) under qemu-user.
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/android-static-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/android-static-build.yml'
schedule:
- cron: '42 8 * * *'
workflow_dispatch:
env:
# Minimum supported API level. 24 (Android 7.0) runs on every modern
# phone while keeping broad reach; bump if you need newer Bionic APIs.
ANDROID_API: 24
jobs:
build:
runs-on: ubuntu-latest
name: ${{ matrix.abi }}
strategy:
fail-fast: false
matrix:
include:
- abi: arm64-v8a # modern phones
triple: aarch64-linux-android
qemu: qemu-aarch64-static
- abi: armeabi-v7a # older 32-bit phones
triple: armv7a-linux-androideabi
qemu: qemu-arm-static
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install build prerequisites
run: sudo apt-get update && sudo apt-get install -y autoconf automake gawk qemu-user-static
- name: Configure and build (${{ matrix.abi }})
shell: bash
run: |
set -euo pipefail
NDK="${ANDROID_NDK_LATEST_HOME:-$ANDROID_NDK_ROOT}"
TC="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin"
export CC="$TC/${{ matrix.triple }}${ANDROID_API}-clang"
export AR="$TC/llvm-ar" RANLIB="$TC/llvm-ranlib" STRIP="$TC/llvm-strip"
export CFLAGS="-O2" LDFLAGS="-static"
# Bionic doesn't declare lchmod()/lutimes() until API 36, but the
# symbols link, so configure mis-detects them -- force them off so
# rsync uses its fallbacks. The other cache vars restore values
# that configure can't probe when cross-compiling (Android runs a
# normal Linux kernel, so these match the native Linux result).
export ac_cv_func_lchmod=no ac_cv_func_lutimes=no \
rsync_cv_HAVE_SOCKETPAIR=yes \
rsync_cv_MKNOD_CREATES_FIFOS=yes \
rsync_cv_MKNOD_CREATES_SOCKETS=yes
# Self-contained build: drop optional external libraries so the
# static binary needs nothing at runtime. rsync keeps md5/md4
# checksums and its bundled zlib.
./configure --host=${{ matrix.triple }} --build=x86_64-pc-linux-gnu \
--enable-ipv6 \
--disable-zstd --disable-lz4 --disable-xxhash --disable-openssl \
--disable-iconv --disable-iconv-open \
--disable-acl-support --disable-xattr-support \
--disable-md2man --disable-roll-simd \
--with-included-popt --with-included-zlib
# Generate the awk-built headers serially first so the parallel
# build can't race on proto.h <- daemon-parm.h.
make proto.h
make -j"$(nproc)" rsync
"$STRIP" rsync
- name: Verify binary
shell: bash
run: |
set -euo pipefail
file rsync
# Gate: must be a statically-linked executable (no interpreter).
file rsync | grep -q "statically linked"
if file rsync | grep -q "dynamically linked"; then
echo "ERROR: binary is not static" >&2; exit 1
fi
# Best-effort: confirm it actually runs under qemu-user.
${{ matrix.qemu }} ./rsync --version | head -3 || \
echo "WARNING: qemu smoke test did not run cleanly (check on a real device)"
- name: Package
shell: bash
run: |
set -euo pipefail
VER=$(sed -n 's/.*RSYNC_VERSION "\([^"]*\)".*/\1/p' version.h)
out="rsync-${VER}-android-${{ matrix.abi }}"
mkdir -p dist
cp rsync "dist/$out"
( cd dist && sha256sum "$out" > "$out.sha256" )
echo "ARTIFACT_NAME=rsync-android-${{ matrix.abi }}" >>"$GITHUB_ENV"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: ${{ env.ARTIFACT_NAME }}
path: dist/

View File

@@ -1,31 +0,0 @@
name: build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: prepare-packages
run: sudo apt-get install fakeroot acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm
- name: prepare-source
run: ./prepare-source
- name: configure
run: ./configure --with-included-popt --with-included-zlib
- name: make
run: make
- name: version-summary
run: ./rsync --version
- name: make check
run: make check
- name: make check30
run: make check30
- name: make check29
run: make check29

72
.github/workflows/coverage.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: Coverage (Ubuntu)
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/coverage.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/coverage.yml'
schedule:
- cron: '42 9 * * *'
workflow_dispatch:
jobs:
coverage:
runs-on: ubuntu-latest
name: gcov coverage
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl gcovr
echo "/usr/local/bin" >>"$GITHUB_PATH"
- name: configure
run: ./configure --enable-coverage --with-rrsync
- name: make
run: make
- name: info
run: rsync --version
# Two coverage runs: the default pipe transport, then a second pass over a
# real loopback rsyncd (--use-tcp) which also exercises the require_tcp-only
# tests. gcovr's --print-summary line/branch/decision totals go to the step
# log (and the job summary below), so the numbers are visible in CI.
# `make coverage` exits with the suite's status, so a regression fails CI.
- name: coverage (pipe transport)
run: |
set -o pipefail
sudo make coverage 2>&1 | tee cov-pipe.log
- name: coverage (TCP transport)
run: |
set -o pipefail
sudo make coverage-tcp 2>&1 | tee cov-tcp.log
- name: coverage summary
if: always()
run: |
{
echo "## gcov coverage"
echo "### Pipe transport (\`make coverage\`)"
echo '```'
grep -E '^(lines|functions|branches|decisions):' cov-pipe.log || echo '(no summary -- see step log)'
echo '```'
echo "### TCP transport (\`make coverage-tcp\`)"
echo '```'
grep -E '^(lines|functions|branches|decisions):' cov-tcp.log || echo '(no summary -- see step log)'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: upload HTML reports
if: always()
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: coverage-html
path: |
coverage
coverage-tcp

68
.github/workflows/cygwin-build.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: Test rsync on Cygwin
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/cygwin-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/cygwin-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: windows-2022
name: Test rsync on Cygwin
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: cygwin
run: choco install -y --no-progress cygwin cyg-get
- name: prep
run: |
cyg-get make autoconf automake gcc-core attr libattr-devel python39 python39-pip libzstd-devel liblz4-devel libssl-devel libxxhash0 libxxhash-devel
echo "C:/tools/cygwin/bin" >>$Env:GITHUB_PATH
- name: commonmark
run: bash -c 'python3 -mpip install --user commonmark'
- name: configure
run: bash -c './configure --with-rrsync'
- name: make
run: bash -c 'make'
- name: install
run: bash -c 'make install'
- name: info
run: bash -c '/usr/local/bin/rsync --version'
- name: check
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on Cygwin
# (rsyncfns.py drives xattrs via getfattr/setfattr from the `attr`
# package installed above), verified on a real Cygwin host. The real
# chown/devices tests still skip (need root/mknod), as do the
# RESOLVE_BENEATH symlink-race tests. symlink-dirlink-basis also now
# RUNS (the #915 non-daemon basis open uses a plain do_open, restoring
# following an in-tree dir-symlink basis without RESOLVE_BENEATH).
run: bash -c 'RSYNC_EXPECT_SKIPPED=acls-default,acls-depth,acls,bare-do-open-symlink-race,chdir-symlink-race,chown,daemon-access-ip,daemon-chroot-acl,devices,dir-sgid,open-noatime,protected-regular,proxy-response-line-too-long,recv-discard-nullderef,sender-flist-symlink-leak,simd-checksum make check'
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd; the default
# 'make check' above uses the secure stdio-pipe transport.
run: bash -c './runtests.py --rsync-bin=`pwd`/rsync.exe --use-tcp -j 8'
- name: ssl file list
run: bash -c 'PATH="/usr/local/bin:$PATH" rsync-ssl --no-motd download.samba.org::rsyncftp/ || true'
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: cygwin-bin
path: |
rsync.exe
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

64
.github/workflows/fleettest.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Test fleettest harness
# Bitrot check for testsuite/fleettest.py (the developer fleet CI harness).
# fleettest is meant to be run by developers on a modern Ubuntu box, so this
# job runs only on ubuntu-latest: it stands up a one-host "fleet" of two
# targets that both ssh to localhost and runs a real fleettest pass against it.
# It does not run on the BSD/Solaris/macOS/Cygwin matrix.
on:
push:
branches: [ master ]
paths:
- 'testsuite/fleettest.py'
- '.github/workflows/fleettest.yml'
pull_request:
branches: [ master ]
paths:
- 'testsuite/fleettest.py'
- '.github/workflows/fleettest.yml'
workflow_dispatch:
schedule:
- cron: '17 7 * * 1'
jobs:
fleettest:
runs-on: ubuntu-latest
name: fleettest against localhost
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y gcc g++ gawk autoconf automake \
acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev \
python3-cmarkgfm openssl rsync openssh-server
- name: set up ssh to localhost
run: |
mkdir -p ~/.ssh && chmod 700 ~/.ssh
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
sudo systemctl start ssh || sudo service ssh start
# fleettest connects with `ssh -o BatchMode=yes localhost`, which won't
# answer a host-key prompt -- so pre-trust localhost in known_hosts.
ssh-keyscan -H localhost 127.0.0.1 >> ~/.ssh/known_hosts 2>/dev/null
ssh -o BatchMode=yes -o ConnectTimeout=15 localhost 'echo ssh-to-localhost-ok'
- name: write localhost fleet config
run: |
cat > fleettest-ci.json <<'EOF'
{ "targets": [
{ "name": "local-a", "ssh_host": "localhost", "workflow": "none.yml",
"configure_flags": [], "builddir": "rsync-citest-a", "privilege": "sudo" },
{ "name": "local-b", "ssh_host": "localhost", "workflow": "none.yml",
"configure_flags": [], "builddir": "rsync-citest-b", "privilege": "sudo" }
] }
EOF
- name: fleettest --list (config sanity)
run: python3 testsuite/fleettest.py --fleet fleettest-ci.json --list
- name: run fleettest against localhost
# Two targets both on localhost exercise the parallel multi-target path
# and the per-run dir / port isolation; exit 0 iff every cell is OK.
run: python3 testsuite/fleettest.py --fleet fleettest-ci.json --timing

52
.github/workflows/freebsd-build.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Test rsync on FreeBSD
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/freebsd-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/freebsd-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
name: Test rsync on FreeBSD
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test in FreeBSD VM
id: test
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y bash autotools m4 devel/xxhash zstd liblz4 python3 archivers/liblz4 git
run: |
freebsd-version
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
make
./rsync --version
make check
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: freebsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

66
.github/workflows/macos-build.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: Test rsync on macOS
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/macos-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/macos-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: macos-latest
name: Test rsync on macOS
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
brew install automake openssl xxhash zstd lz4
pip3 install --user --break-system-packages commonmark
echo "$(brew --prefix)/bin" >>"$GITHUB_PATH"
- name: configure
run: |
BREW_PREFIX=$(brew --prefix)
OPENSSL_PREFIX=$(brew --prefix openssl)
CPPFLAGS="-I${BREW_PREFIX}/include -I${OPENSSL_PREFIX}/include" \
LDFLAGS="-L${BREW_PREFIX}/lib -L${OPENSSL_PREFIX}/lib" \
./configure --with-rrsync
- name: make
run: make
- name: install
run: sudo make install
- name: info
run: rsync --version
- name: check
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on macOS
# (rsyncfns.py drives xattrs via the `xattr` command), verified on a
# real macOS host, so they're no longer in the skip set.
run: sudo RSYNC_EXPECT_SKIPPED=acls-default,acls-depth,chmod-temp-dir,daemon-access-ip,daemon-chroot-acl,dir-sgid,open-noatime,preallocate,protected-regular,proxy-response-line-too-long,recv-discard-nullderef,simd-checksum,sparse make check
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd; the default
# 'make check' above uses the secure stdio-pipe transport.
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: macos-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

53
.github/workflows/netbsd-build.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Test rsync on NetBSD
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/netbsd-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/netbsd-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
name: Test rsync on NetBSD
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test in NetBSD VM
id: test
uses: vmactions/netbsd-vm@v1
with:
usesh: true
prepare: |
PATH=/usr/sbin:$PATH pkg_add autoconf automake python312
ln -sf /usr/pkg/bin/python3.12 /usr/pkg/bin/python3
run: |
uname -a
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
make
./rsync --version
make check
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: netbsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

62
.github/workflows/openbsd-build.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: Test rsync on OpenBSD
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/openbsd-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/openbsd-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
name: Test rsync on OpenBSD
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test in OpenBSD VM
id: test
uses: vmactions/openbsd-vm@v1
with:
usesh: true
prepare: |
pkg_add -I bash autoconf%2.71 automake%1.16
run: |
uname -a
export AUTOCONF_VERSION=2.71
export AUTOMAKE_VERSION=1.16
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
make
./rsync --version
make check
# The --use-tcp daemon tests run at -j2 here (vs -j8 elsewhere): this
# job runs inside a nested VM, and at -j8 the many concurrent loopback
# daemons occasionally lose a connection-handshake timing race under
# that resource pressure, hanging one test to the 300s timeout. It is
# an environment artifact, not an rsync bug (the handshake is
# deadlock-free and unreproducible elsewhere, even pinned to 1 CPU at
# -j8); -j2 keeps the VM from over-subscribing. The pipe `make check`
# above stays at the default parallelism.
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 2
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: openbsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

52
.github/workflows/solaris-build.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Test rsync on Solaris
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/solaris-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/solaris-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
name: Test rsync on Solaris
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test in Solaris VM
id: test
uses: vmactions/solaris-vm@v1
with:
usesh: true
prepare: |
pkg install bash automake gnu-m4 pkg://solaris/runtime/python-35 autoconf gcc git
run: |
uname -a
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
make
./rsync --version
make check
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: solaris-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

View File

@@ -0,0 +1,65 @@
name: Test rsync on Ubuntu 22.04
# Older-LTS coverage to help with backporting security fixes. ubuntu-22.04
# is currently the oldest GitHub Actions runner image (20.04 was retired
# in April 2025).
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-22.04-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-22.04-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: ubuntu-22.04
name: Test rsync on Ubuntu 22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
echo "/usr/local/bin" >>"$GITHUB_PATH"
- name: configure
run: ./configure --with-rrsync
- name: make
run: make
- name: install
run: sudo make install
- name: info
run: rsync --version
- name: check
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef make check
- name: check30
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef make check30
- name: check29
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef make check29
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd; the default
# 'make check' above uses the secure stdio-pipe transport.
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: ubuntu-22.04-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

91
.github/workflows/ubuntu-build.yml vendored Normal file
View File

@@ -0,0 +1,91 @@
name: Test rsync on Ubuntu
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-build.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
name: Test rsync on Ubuntu
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
echo "/usr/local/bin" >>"$GITHUB_PATH"
- name: configure
run: ./configure --with-rrsync
- name: make
run: make
- name: install/uninstall DESTDIR smoke test
run: |
set -e
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
make install-all DESTDIR="$tmp"
for path in \
/usr/local/bin/rsync \
/usr/local/bin/rsync-ssl \
/usr/local/bin/rrsync \
/usr/local/share/man/man1/rsync.1 \
/usr/local/share/man/man1/rsync-ssl.1 \
/usr/local/share/man/man1/rrsync.1 \
/usr/local/share/man/man5/rsyncd.conf.5 \
/etc/stunnel/rsyncd.conf
do
test -e "$tmp$path"
done
make uninstall-all DESTDIR="$tmp"
leftover="$(find "$tmp" -type f -print)"
if [ -n "$leftover" ]; then
printf '%s\n' "$leftover"
exit 1
fi
- name: install
run: sudo make install
- name: info
run: rsync --version
- name: check
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef make check
- name: check30
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef make check30
- name: check29
run: sudo RSYNC_EXPECT_SKIPPED=crtimes,daemon-access-ip,daemon-chroot-acl,proxy-response-line-too-long,recv-discard-nullderef make check29
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd. The default
# 'make check' above uses the secure stdio-pipe transport (no listening
# sockets); this run exercises the real TCP accept/auth path. Skip-set
# is env-dependent here (chroot-acl), so leave RSYNC_EXPECT_SKIPPED unset.
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: ubuntu-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

View File

@@ -0,0 +1,77 @@
name: Test rsync version mixing on Ubuntu
# Runs the CURRENT test suite with two different rsync binaries: the freshly
# built ./rsync as the client/driver, and a committed OLD static binary
# (old_versions/rsync_<ver>) as the daemon / remote-shell peer. This exercises
# real version mixing over the wire -- more convincing than --protocol forcing,
# which only makes the current binary speak an old protocol.
#
# Direction is fixed: the current binary always drives (only it understands the
# new test scripts); the old binary is only ever the server/daemon side. The
# reverse (old client driving new scripts) is not possible -- but one test,
# reverse-daemon-delta, swaps the roles internally (current build as the daemon,
# old binary as the client) to cover the backward-compat direction: a current
# daemon serving the installed base of old clients.
#
# The per-version manifest testsuite/expect/rsync_<ver>.expect lists exactly
# which tests run and each one's expected outcome (pass/skip/fail/xfail), so an
# old peer's known feature gaps are recorded rather than treated as breakage.
#
# All peers run in a SINGLE job (looped, not a matrix) so the PR shows one check
# line rather than one per version. Each peer/transport is a foldable ::group::
# in the log, and a failure annotates which peer/transport broke.
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-version-mix.yml'
pull_request:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-version-mix.yml'
schedule:
- cron: '52 8 * * *'
jobs:
version-mix:
runs-on: ubuntu-latest
name: rsync version-mix
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
echo "/usr/local/bin" >>"$GITHUB_PATH"
- name: configure
run: ./configure --with-rrsync
- name: make
# check-progs builds rsync AND the test helper programs (tls, trimslash,
# t_unsafe, ...) that runtests.py requires; plain `make` does not.
run: make check-progs
- name: info
run: ./rsync --version | head -1
- name: version mixing (all peers, pipe + TCP transports)
run: |
rc=0
for peer in old_versions/rsync_*; do
chmod +x "$peer"
name=$(basename "$peer")
expect="testsuite/expect/$name.expect"
for transport in pipe tcp; do
tcp=()
[ "$transport" = tcp ] && tcp=(--use-tcp)
echo "::group::$name ($transport): $("$peer" --version | head -1)"
if ! ./runtests.py --rsync-bin="$PWD/rsync" --rsync-bin2="$PWD/$peer" \
--expect-result "$expect" "${tcp[@]}" -j 8; then
echo "::error::version-mix failed: $name ($transport)"
rc=1
fi
echo "::endgroup::"
done
done
exit $rc

12
.gitignore vendored
View File

@@ -15,22 +15,27 @@ config.status
aclocal.m4
/proto.h
/proto.h-tstamp
/rsync*.1
/rsync*.5
/rsync*.[15]
/rrsync
/rrsync*.1
/rsync*.html
/rrsync*.html
/help-rsync*.h
/default-cvsignore.h
/default-dont-compress.h
/daemon-parm.h
/.md2man-works
/autom4te*.cache
/confdefs.h
/conftest*
/dox
/getgroups
/gists
/gmon.out
/rsync
/stunnel-rsyncd.conf
/shconfig
/git-version.h
/testdir
/tests-dont-exist
/testtmp
@@ -47,8 +52,11 @@ aclocal.m4
/testsuite/chown-fake.test
/testsuite/devices-fake.test
/testsuite/xattrs-hlink.test
/testsuite/fleettest.json
/patches
/patches.gen
/build
/auto-build-save
.deps
/*.exe
*.dSYM/

26
COPYING
View File

@@ -1,7 +1,16 @@
REGARDING OPENSSL AND XXHASH
In addition, as a special exception, the copyright holders give
permission to dynamically link rsync with the OpenSSL and xxhash
libraries when those libraries are being distributed in compliance
with their license terms, and to distribute a dynamically linked
combination of rsync and these libraries. This is also considered
to be covered under the GPL's System Libraries exception.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -645,7 +654,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@@ -664,20 +673,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
REGARDING OPENSSL AND XXHASH
In addition, as a special exception, the copyright holders give
permission to dynamically link rsync with the OpenSSL and xxhash
libraries when those libraries are being distributed in compliance
with their license terms, and to distribute a dynamically linked
combination of rsync and these libraries. This is also considered
to be covered under the GPL's System Libraries exception.
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,13 +1,202 @@
To build and install rsync:
# How to build and install rsync
$ ./configure
$ make
# make install
When building rsync, you'll want to install various libraries in order to get
all the features enabled. The configure script will alert you when the
newest libraries are missing and tell you the appropriate `--disable-LIB`
option to use if you want to just skip that feature. What follows are various
support libraries that you may want to install to build rsync with the maximum
features (the impatient can skip down to the package summary):
You may set the installation directory and other parameters by options
to ./configure. To see them, use:
## Ubuntu users: skip the build, use the PPA
$ ./configure --help
If you are on a currently supported Ubuntu series (jammy 22.04 LTS, noble
24.04 LTS, questing 25.10, resolute 26.04 LTS) and just want the latest
upstream rsync, the rsync project maintains a Launchpad PPA that tracks
stable releases:
> sudo add-apt-repository ppa:rsyncproject/rsync
> sudo apt update && sudo apt install rsync
See [the PPA page][ppa] for current build status across architectures.
[ppa]: https://launchpad.net/~rsyncproject/+archive/ubuntu/rsync
To test the upcoming release instead, there is also a [`rsync-latest`
PPA][ppa-latest] that is rebuilt from the tip of the git master branch. These
are development snapshots whose version numbers (such as
`3.5.0~git20260601...`) deliberately sort below the matching stable release, so
the stable PPA above will never silently move you from a release onto a
snapshot. Use it for testing only -- it may contain unreleased changes:
> sudo add-apt-repository ppa:rsyncproject/rsync-latest
> sudo apt update && sudo apt install rsync
[ppa-latest]: https://launchpad.net/~rsyncproject/+archive/ubuntu/rsync-latest
The rest of this document covers building from source.
## The basic setup
You need to have a C compiler installed and optionally a C++ compiler in order
to try to build some hardware-accelerated checksum routines. Rsync also needs
a modern awk, which might be provided via gawk or nawk on some OSes.
## Autoconf & manpages
If you're installing from the git repo (instead of a release tar file) you'll
also need the GNU autotools (autoconf & automake) and your choice of 2 python3
markdown libraries: cmarkgfm or commonmark (needed to generate the manpages).
If your OS doesn't provide a python3-cmarkgfm or python3-commonmark package,
you can run the following to install the commonmark python library for your
build user (after installing python3's pip package):
> python3 -mpip install --user commonmark
You can test if you've got it fixed by running (from the rsync checkout):
> ./md-convert --test rsync-ssl.1.md
Alternately, you can avoid generating the manpages by fetching the very latest
versions (that match the latest git source) from the [generated-files][6] dir.
One way to do that is to run:
> ./prepare-source fetchgen
[6]: https://download.samba.org/pub/rsync/generated-files/
## ACL support
To support copying ACL file information, make sure you have an acl
development library installed. It also helps to have the helper programs
installed to manipulate ACLs and to run the rsync testsuite.
## Xattr support
To support copying xattr file information, make sure you have an attr
development library installed. It also helps to have the helper programs
installed to manipulate xattrs and to run the rsync testsuite.
## xxhash
The [xxHash library][1] provides extremely fast checksum functions that can
make the "rsync algorithm" run much more quickly, especially when matching
blocks in large files. Installing this development library adds xxhash
checksums as the default checksum algorithm. You'll need at least v0.8.0
if you want rsync to include the full range of its checksum algorithms.
[1]: https://cyan4973.github.io/xxHash/
## zstd
The [zstd library][2] compression algorithm that uses less CPU than
the default zlib algorithm at the same compression level. Note that you
need at least version 1.4, so you might need to skip the zstd compression if
you can only install a 1.3 release. Installing this development library
adds zstd compression as the default compression algorithm.
[2]: http://facebook.github.io/zstd/
## lz4
The [lz4 library][3] compression algorithm that uses very little CPU, though
it also has the smallest compression ratio of other algorithms. Installing
this development library adds lz4 compression as an available compression
algorithm.
[3]: https://lz4.github.io/lz4/
## openssl crypto
The [openssl crypto library][4] provides some hardware accelerated checksum
algorithms for MD4 and MD5. Installing this development library makes rsync
use the (potentially) faster checksum routines when computing MD4 & MD5
checksums.
[4]: https://www.openssl.org/docs/man1.0.2/man3/crypto.html
## Package summary
To help you get the libraries installed, here are some package install commands
for various OSes. The commands are split up to correspond with the above
items, but feel free to combine the package names into a single install, if you
like.
- For Debian and Ubuntu (Debian Buster users may want to briefly(?) enable
buster-backports to update zstd from 1.3 to 1.4):
> sudo apt install -y gcc g++ gawk autoconf automake python3-cmarkgfm
> sudo apt install -y acl libacl1-dev
> sudo apt install -y attr libattr1-dev
> sudo apt install -y libxxhash-dev
> sudo apt install -y libzstd-dev
> sudo apt install -y liblz4-dev
> sudo apt install -y libssl-dev
Or run support/install_deps_ubuntu.sh
- For CentOS (use EPEL for python3-pip):
> sudo yum -y install epel-release
> sudo yum -y install gcc g++ gawk autoconf automake python3-pip
> sudo yum -y install acl libacl-devel
> sudo yum -y install attr libattr-devel
> sudo yum -y install xxhash-devel
> sudo yum -y install libzstd-devel
> sudo yum -y install lz4-devel
> sudo yum -y install openssl-devel
> python3 -mpip install --user commonmark
- For Fedora 33:
> sudo dnf -y install acl libacl-devel
> sudo dnf -y install attr libattr-devel
> sudo dnf -y install xxhash-devel
> sudo dnf -y install libzstd-devel
> sudo dnf -y install lz4-devel
> sudo dnf -y install openssl-devel
- For FreeBSD (this assumes that the python3 version is 3.7):
> sudo pkg install -y autotools python3 py37-CommonMark
> sudo pkg install -y xxhash
> sudo pkg install -y zstd
> sudo pkg install -y liblz4
- For macOS:
> brew install automake
> brew install xxhash
> brew install zstd
> brew install lz4
> brew install openssl
- For Cygwin (with all cygwin programs stopped, run the appropriate setup program from a cmd shell):
> setup-x86_64 --quiet-mode -P make,gawk,autoconf,automake,gcc-core,python38,python38-pip
> setup-x86_64 --quiet-mode -P attr,libattr-devel
> setup-x86_64 --quiet-mode -P libzstd-devel
> setup-x86_64 --quiet-mode -P liblz4-devel
> setup-x86_64 --quiet-mode -P libssl-devel
Sometimes cygwin has commonmark packaged and sometimes it doesn't. Now that
its python38 has stabilized, you could install python38-commonmark. Or just
avoid the issue by running this from a bash shell as your build user:
> python3 -mpip install --user commonmark
## Build and install
After installing the various libraries, you need to configure, build, and
install the source:
> ./configure
> make
> sudo make install
The default install path is /usr/local/bin, but you can set the installation
directory and other parameters using options to ./configure. To see them, use:
> ./configure --help
Configure tries to figure out if the local system uses group "nobody" or
"nogroup" by looking in the /etc/group file. (This is only used for the
@@ -19,56 +208,68 @@ config.h, or just override them in your /etc/rsyncd.conf file.
As of 2.4.7, rsync uses Eric Troan's popt option-parsing library. A
cut-down copy of a recent release is included in the rsync distribution,
and will be used if there is no popt library on your build host, or if
the --with-included-popt option is passed to ./configure.
the `--with-included-popt` option is passed to ./configure.
If you configure using --enable-maintainer-mode, then rsync will try
If you configure using `--enable-maintainer-mode`, then rsync will try
to pop up an xterm on DISPLAY=:0 if it crashes. You might find this
useful, but it should be turned off for production builds.
MAKE COMPATIBILITY
------------------
If you want to automatically use a separate "build" directory based on
the current git branch name, start with a pristine git checkout and run
"mkdir auto-build-save" before you run the first ./configure command.
That will cause a fresh build dir to spring into existence along with a
special Makefile symlink that allows you to run "make" and "./configure"
from the source dir (the "build" dir gets auto switched based on branch).
This is helpful when using the branch-from-patch and patch-update scripts
to maintain the official rsync patches. If you ever need to build from
a "detached head" git position then you'll need to manually chdir into
the build dir to run make. I also like to create 2 more symlinks in the
source dir: `ln -s build/rsync . ; ln -s build/testtmp .`
## Make compatibility
Note that Makefile.in has a rule that uses a wildcard in a prerequisite. If
your make has a problem with this rule, you will see an error like this:
Don't know how to make ./*.c
You can change the "proto.h-tstamp" target in Makefile.in to list all the *.c
You can change the "proto.h-tstamp" target in Makefile.in to list all the \*.c
filenames explicitly in order to avoid this issue.
RPM NOTES
---------
## RPM notes
Under packaging you will find .spec files for several distributions.
The .spec file in packaging/lsb can be used for Linux systems that
adhere to the Linux Standards Base (e.g., RedHat and others).
HP-UX NOTES
-----------
## HP-UX notes
The HP-UX 10.10 "bundled" C compiler seems not to be able to cope with
ANSI C. You may see this error message in config.log if ./configure
fails:
(Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
(Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
Install gcc or HP's "ANSI/C Compiler".
MAC OSX NOTES
-------------
## Mac OS X notes
Some versions of Mac OS X (Darwin) seem to have an IPv6 stack, but do
not completely implement the "New Sockets" API.
not completely implement the "New Sockets" API.
<http://www.ipv6.org/impl/mac.html> says that Apple started to support
IPv6 in 10.2 (Jaguar). If your build fails, try again after running
configure with --disable-ipv6.
[This site][5] says that Apple started to support IPv6 in 10.2 (Jaguar). If
your build fails, try again after running configure with `--disable-ipv6`.
IBM AIX NOTES
-------------
Apple Silicon macs may install packages in a slightly different location and require flags.
CFLAGS="-I /opt/homebrew/include" LDFLAGS="-L /opt/homebrew/lib"
[5]: http://www.ipv6.org/impl/mac.html
## IBM AIX notes
IBM AIX has a largefile problem with mkstemp. See IBM PR-51921.
The workaround is to append the below to config.h
#ifdef _LARGE_FILES
#undef HAVE_SECURE_MKSTEMP
#endif
The workaround is to append the following to config.h:
> #ifdef _LARGE_FILES
> #undef HAVE_SECURE_MKSTEMP
> #endif

View File

@@ -1,4 +1,4 @@
# The input file that configure uses to create the Makefile for rsync.
# The Makefile for rsync (configure creates it from Makefile.in).
prefix=@prefix@
datarootdir=@datarootdir@
@@ -6,6 +6,7 @@ exec_prefix=@exec_prefix@
bindir=@bindir@
libdir=@libdir@/rsync
mandir=@mandir@
with_rrsync=@with_rrsync@
LIBS=@LIBS@
CC=@CC@
@@ -29,37 +30,41 @@ SHELL=/bin/sh
.SUFFIXES:
.SUFFIXES: .c .o
SIMD_x86_64=simd-checksum-x86_64.o
ASM_x86_64=lib/md5-asm-x86_64.o
ROLL_SIMD_x86_64=simd-checksum-x86_64.o
ROLL_ASM_x86_64=simd-checksum-avx2.o
MD5_ASM_x86_64=lib/md5-asm-x86_64.o
GENFILES=configure.sh aclocal.m4 config.h.in proto.h proto.h-tstamp rsync.1 rsync.1.html \
rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html
GENFILES=configure.sh aclocal.m4 config.h.in rsync.1 rsync.1.html \
rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html \
@GEN_RRSYNC@
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \
lib/pool_alloc.h lib/mdigest.h lib/md-defines.h version.h
lib/pool_alloc.h lib/mdigest.h lib/md-defines.h
LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
util.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
util1.o util2.o main.o checksum.o match.o syscall.o android.o log.o backup.o delete.o
OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
OBJS3=progress.o pipe.o @ASM@
usage.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
OBJS3=progress.o pipe.o @MD5_ASM@ @ROLL_SIMD@ @ROLL_ASM@
DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
popt/popthelp.o popt/poptparse.o
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) @SIMD@ $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
popt_OBJS= popt/popt.o popt/poptconfig.o \
popt/popthelp.o popt/poptparse.o popt/poptint.o
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
TLS_OBJ = tls.o syscall.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
TLS_OBJ = tls.o syscall.o android.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
# Programs we must have to run the test cases
CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(EXEEXT)
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) t_chmod_secure$(EXEEXT) \
t_secure_relpath$(EXEEXT) wildtest$(EXEEXT) simdtest$(EXEEXT)
CHECK_SYMLINKS = testsuite/chown-fake.test testsuite/devices-fake.test testsuite/xattrs-hlink.test
CHECK_SYMLINKS = testsuite/chown-fake_test.py testsuite/devices-fake_test.py \
testsuite/xattrs-hlink_test.py testsuite/exclude-lsh_test.py
# Objects for CHECK_PROGS to clean
CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest.o
CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o t_chmod_secure.o t_secure_relpath.o trimslash.o wildtest.o
# note that the -I. is needed to handle config.h when using VPATH
.c.o:
@@ -67,41 +72,71 @@ CHECK_OBJS=tls.o testrun.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.
$(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) -c $< @CC_SHOBJ_FLAG@
@OBJ_RESTORE@
# NOTE: consider running "packaging/smart-make" instead of "make" to auto-handle
# any changes to configure.sh and the main Makefile prior to a "make all".
all: Makefile rsync$(EXEEXT) stunnel-rsyncd.conf @MAKE_RRSYNC@ @MAKE_MAN@
.PHONY: all
all: Makefile rsync$(EXEEXT) stunnel-rsyncd.conf @MAKE_MAN@
.PHONY: install
install: all
-${MKDIR_P} ${DESTDIR}${bindir}
${INSTALLCMD} ${INSTALL_STRIP} -m 755 rsync$(EXEEXT) ${DESTDIR}${bindir}
${INSTALLCMD} -m 755 $(srcdir)/rsync-ssl ${DESTDIR}${bindir}
-${MKDIR_P} ${DESTDIR}${mandir}/man1
-${MKDIR_P} ${DESTDIR}${mandir}/man5
if test -f rsync.1; then ${INSTALLMAN} -m 644 rsync.1 ${DESTDIR}${mandir}/man1; fi
if test -f rsync-ssl.1; then ${INSTALLMAN} -m 644 rsync-ssl.1 ${DESTDIR}${mandir}/man1; fi
if test -f rsyncd.conf.5; then ${INSTALLMAN} -m 644 rsyncd.conf.5 ${DESTDIR}${mandir}/man5; fi
-$(MKDIR_P) $(DESTDIR)$(bindir)
$(INSTALLCMD) $(INSTALL_STRIP) -m 755 rsync$(EXEEXT) $(DESTDIR)$(bindir)
$(INSTALLCMD) -m 755 $(srcdir)/rsync-ssl $(DESTDIR)$(bindir)
-$(MKDIR_P) $(DESTDIR)$(mandir)/man1
-$(MKDIR_P) $(DESTDIR)$(mandir)/man5
for fn in rsync.1 rsync-ssl.1; do \
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man1; \
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man1; fi; \
done
for fn in rsyncd.conf.5; do \
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man5; \
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man5; fi; \
done
if test "$(with_rrsync)" = yes; then \
$(INSTALLCMD) -m 755 rrsync $(DESTDIR)$(bindir); \
fn=rrsync.1; \
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man1; \
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man1; fi; \
fi
install-ssl-daemon: stunnel-rsyncd.conf
-${MKDIR_P} ${DESTDIR}/etc/stunnel
${INSTALLCMD} -m 644 stunnel-rsyncd.conf ${DESTDIR}/etc/stunnel/rsyncd.conf
-$(MKDIR_P) $(DESTDIR)/etc/stunnel
$(INSTALLCMD) -m 644 stunnel-rsyncd.conf $(DESTDIR)/etc/stunnel/rsyncd.conf
@if ! ls /etc/rsync-ssl/certs/server.* >/dev/null 2>/dev/null; then \
echo "Note that you'll need to install the certificate used by /etc/stunnel/rsyncd.conf"; \
fi
install-all: install install-ssl-client install-ssl-daemon
install-all: install install-ssl-daemon
install-strip:
$(MAKE) INSTALL_STRIP='-s' install
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)$(bindir)/rsync$(EXEEXT) $(DESTDIR)$(bindir)/rsync-ssl
rm -f $(DESTDIR)$(bindir)/rrsync
rm -f $(DESTDIR)$(mandir)/man1/rsync.1 $(DESTDIR)$(mandir)/man1/rsync-ssl.1
rm -f $(DESTDIR)$(mandir)/man1/rrsync.1
rm -f $(DESTDIR)$(mandir)/man5/rsyncd.conf.5
.PHONY: uninstall-ssl-daemon
uninstall-ssl-daemon:
rm -f $(DESTDIR)/etc/stunnel/rsyncd.conf
.PHONY: uninstall-all
uninstall-all: uninstall uninstall-ssl-daemon
rsync$(EXEEXT): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
rrsync: support/rrsync
cp -p $(srcdir)/support/rrsync rrsync
$(OBJS): $(HEADERS)
$(CHECK_OBJS): $(HEADERS)
tls.o xattrs.o: lib/sysxattrs.h
options.o: latest-year.h help-rsync.h help-rsyncd.h
exclude.o: default-cvsignore.h
loadparm.o: default-dont-compress.h
usage.o: version.h latest-year.h help-rsync.h help-rsyncd.h git-version.h default-cvsignore.h
loadparm.o: default-dont-compress.h daemon-parm.h
flist.o: rounding.h
@@ -111,6 +146,9 @@ default-cvsignore.h default-dont-compress.h: rsync.1.md define-from-md.awk
help-rsync.h help-rsyncd.h: rsync.1.md help-from-md.awk
$(AWK) -f $(srcdir)/help-from-md.awk -v hfile=$@ $(srcdir)/rsync.1.md
daemon-parm.h: daemon-parm.txt daemon-parm.awk
$(AWK) -f $(srcdir)/daemon-parm.awk $(srcdir)/daemon-parm.txt
rounding.h: rounding.c rsync.h proto.h
@for r in 0 1 3; do \
if $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o rounding -DEXTRA_ROUNDING=$$r -I. $(srcdir)/rounding.c >rounding.out 2>&1; then \
@@ -129,11 +167,20 @@ rounding.h: rounding.c rsync.h proto.h
fi
@rm -f rounding.out
simd-checksum-x86_64.o: simd-checksum-x86_64.cpp
@$(srcdir)/cmdormsg disable-simd $(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $(srcdir)/simd-checksum-x86_64.cpp
git-version.h: ALWAYS_RUN
$(srcdir)/mkgitver
lib/md5-asm-x86_64.o: lib/md5-asm-x86_64.S config.h lib/md-defines.h
@$(srcdir)/cmdormsg disable-asm $(CC) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/lib/md5-asm-x86_64.S
.PHONY: ALWAYS_RUN
ALWAYS_RUN:
simd-checksum-x86_64.o: simd-checksum-x86_64.cpp
@$(srcdir)/cmd-or-msg disable-roll-simd $(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $(srcdir)/simd-checksum-x86_64.cpp
simd-checksum-avx2.o: simd-checksum-avx2.S
@$(srcdir)/cmd-or-msg disable-roll-asm $(CC) $(CFLAGS) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/simd-checksum-avx2.S
lib/md5-asm-x86_64.o: lib/md5-asm-x86_64.S lib/md-defines.h
@$(srcdir)/cmd-or-msg disable-md5-asm $(CC) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/lib/md5-asm-x86_64.S
tls$(EXEEXT): $(TLS_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TLS_OBJ) $(LIBS)
@@ -147,23 +194,27 @@ getgroups$(EXEEXT): getgroups.o
getfsdev$(EXEEXT): getfsdev.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
TRIMSLASH_OBJ = trimslash.o syscall.o t_stub.o lib/compat.o lib/snprintf.o
TRIMSLASH_OBJ = trimslash.o syscall.o android.o util2.o t_stub.o lib/compat.o lib/snprintf.o
trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
T_UNSAFE_OBJ = t_unsafe.o syscall.o android.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
T_CHMOD_SECURE_OBJ = t_chmod_secure.o syscall.o android.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o
t_chmod_secure$(EXEEXT): $(T_CHMOD_SECURE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_CHMOD_SECURE_OBJ) $(LIBS)
T_SECURE_RELPATH_OBJ = t_secure_relpath.o syscall.o android.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o
t_secure_relpath$(EXEEXT): $(T_SECURE_RELPATH_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_SECURE_RELPATH_OBJ) $(LIBS)
.PHONY: conf
conf: configure.sh config.h.in
.PHONY: gen
gen: conf proto.h man
.PHONY: gensend
gensend: gen
rsync -aic $(GENFILES) $${SAMBA_HOST-samba.org}:/home/ftp/pub/rsync/generated-files/
gen: conf proto.h man git-version.h
aclocal.m4: $(srcdir)/m4/*.m4
aclocal -I $(srcdir)/m4
@@ -185,7 +236,7 @@ configure.sh config.h.in: configure.ac aclocal.m4
else \
echo "config.h.in has CHANGED."; \
fi
@if test -f configure.sh.old -o -f config.h.in.old; then \
@if test -f configure.sh.old || test -f config.h.in.old; then \
if test "$(MAKECMDGOALS)" = reconfigure; then \
echo 'Continuing with "make reconfigure".'; \
else \
@@ -228,25 +279,31 @@ proto: proto.h-tstamp
proto.h: proto.h-tstamp
@if test -f proto.h; then :; else cp -p $(srcdir)/proto.h .; fi
proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c config.h
$(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c
proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
$(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
.PHONY: man
man: rsync.1 rsync-ssl.1 rsyncd.conf.5
man: rsync.1 rsync-ssl.1 rsyncd.conf.5 @MAKE_RRSYNC_1@
rsync.1: rsync.1.md md2man version.h Makefile
@$(srcdir)/maybe-make-man $(srcdir) rsync.1.md
rsync.1: rsync.1.md md-convert version.h Makefile
@$(srcdir)/maybe-make-man rsync.1.md
rsync-ssl.1: rsync-ssl.1.md md2man version.h Makefile
@$(srcdir)/maybe-make-man $(srcdir) rsync-ssl.1.md
rsync-ssl.1: rsync-ssl.1.md md-convert version.h Makefile
@$(srcdir)/maybe-make-man rsync-ssl.1.md
rsyncd.conf.5: rsyncd.conf.5.md md2man version.h Makefile
@$(srcdir)/maybe-make-man $(srcdir) rsyncd.conf.5.md
rsyncd.conf.5: rsyncd.conf.5.md md-convert version.h Makefile
@$(srcdir)/maybe-make-man rsyncd.conf.5.md
rrsync.1: support/rrsync.1.md md-convert Makefile
@$(srcdir)/maybe-make-man support/rrsync.1.md
.PHONY: clean
clean: cleantests
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \
rounding rounding.h *.old rsync*.1 rsync*.5 rsync*.html
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) @MAKE_RRSYNC@ \
git-version.h rounding rounding.h *.old rsync*.1 rsync*.5 @MAKE_RRSYNC_1@ \
*.html daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp
rm -f *.gcno *.gcda lib/*.gcno lib/*.gcda zlib/*.gcno zlib/*.gcda popt/*.gcno popt/*.gcda
rm -rf coverage coverage-tcp coverage-all coverage-fallback
.PHONY: cleantests
cleantests:
@@ -257,16 +314,11 @@ cleantests:
# the source directory.
.PHONY: distclean
distclean: clean
rm -f Makefile config.h config.status
rm -f stunnel-rsyncd.conf
rm -f lib/dummy popt/dummy zlib/dummy
rm -f $(srcdir)/Makefile $(srcdir)/config.h $(srcdir)/config.status
rm -f $(srcdir)/lib/dummy $(srcdir)/popt/dummy $(srcdir)/zlib/dummy
rm -f config.cache config.log
rm -f $(srcdir)/config.cache $(srcdir)/config.log
rm -f shconfig $(srcdir)/shconfig
rm -f $(GENFILES)
rm -rf autom4te.cache
for dir in $(srcdir) . ; do \
(cd "$$dir" && rm -rf Makefile config.h config.status stunnel-rsyncd.conf \
lib/dummy popt/dummy zlib/dummy config.cache config.log shconfig \
$(GENFILES) autom4te.cache) ; \
done
# this target is really just for my use. It only works on a limited
# range of machines and is used to produce a list of potentially
@@ -276,6 +328,7 @@ finddead:
nm *.o */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
nm *.o */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
comm -13 nmused.txt nmfns.txt
@rm nmused.txt nmfns.txt
# 'check' is the GNU name, 'test' is the name for everybody else :-)
.PHONY: test
@@ -291,30 +344,139 @@ test: check
# catch Bash-isms earlier even if we're running on GNU. Of course, we
# might lose in the future where POSIX diverges from old sh.
# `make check` runs tests in parallel by default. Override with
# `make check CHECK_J=1` (serial) or any other value.
CHECK_J = 8
# Parallelism for `make coverage`. Defaults to the same as CHECK_J: the
# coverage build sets -fprofile-update=atomic (atomic in-memory counters) and
# gcc's libgcov serializes the per-source .gcda read-modify-write merge with a
# file lock, so concurrent rsync processes (incl. the forked sender/generator/
# receiver) accumulate exactly -- verified by a count-linearity check (a hot
# line accumulates identically at -j1 and -P16). Override with
# `make coverage COVERAGE_J=1` if your libgcov does not lock .gcda merges.
COVERAGE_J = $(CHECK_J)
# Output directory and extra runtests.py flags for `make coverage`. The
# `coverage-tcp` target reuses the coverage recipe with --use-tcp (real
# loopback rsyncd, which exercises the TCP accept/auth path and the
# require_tcp-only tests) and a separate output directory.
COVERAGE_DIR = coverage
COVERAGE_RUNFLAGS =
# Bundled third-party code that rsync ships but does not own; excluded from the
# coverage report so the percentages reflect rsync's own source. zlib/ and popt/
# are wholly vendored; the named lib/ files are PostgreSQL (getaddrinfo) and ISC
# (inet_ntop/inet_pton) / standalone (getpass) imports. The other lib/*.c
# (md5, mdfour, wildmatch, permstring, pool_alloc, snprintf, sysacls, sysxattrs,
# compat) are rsync's own and stay in the report.
COVERAGE_EXCLUDE = -e '(^|/)zlib/' -e '(^|/)popt/' \
-e '(^|/)lib/(getaddrinfo|getpass|inet_ntop|inet_pton)\.'
# Build everything the test suite needs (rsync + helper programs + symlinks)
# WITHOUT running it. Used by CI jobs that invoke runtests.py directly with
# custom options (e.g. the version-mix workflow's --rsync-bin2/--expect-result).
.PHONY: check-progs
check-progs: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
.PHONY: check
check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J)
.PHONY: check29
check29: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=29
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J) --protocol=29
.PHONY: check30
check30: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=30
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(CHECK_J) --protocol=30
# Whole-suite gcov coverage report (HTML, with branch + decision coverage).
# Requires a build configured with --enable-coverage and the `gcovr` tool
# (pip install gcovr). Runs the suite in parallel (COVERAGE_J, default CHECK_J):
# this is safe because the coverage build uses -fprofile-update=atomic and
# libgcov locks the per-source .gcda during its merge, so concurrent rsync
# processes accumulate exactly (see COVERAGE_J above). Use COVERAGE_J=1 if your
# toolchain's libgcov does not lock .gcda merges.
.PHONY: coverage
coverage: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
@case '$(CFLAGS)' in *--coverage*) ;; \
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
find . -name '*.gcda' -delete
@rc=0; $(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(COVERAGE_J) $(COVERAGE_RUNFLAGS) || rc=$$?; \
rm -rf $(COVERAGE_DIR) && mkdir -p $(COVERAGE_DIR); \
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
--html-details -o $(COVERAGE_DIR)/index.html . || exit $$?; \
echo "Coverage report written to $(COVERAGE_DIR)/index.html"; \
if test $$rc != 0; then \
echo "*** test suite FAILED (status $$rc) -- coverage report still written above"; \
fi; \
exit $$rc
# Same as `make coverage` but with the daemon tests run over a real loopback
# rsyncd (--use-tcp), into a separate report directory.
.PHONY: coverage-tcp
coverage-tcp:
$(MAKE) coverage COVERAGE_RUNFLAGS=--use-tcp COVERAGE_DIR=coverage-tcp
# Comprehensive single report: run the suite under several configurations,
# accumulating into the shared .gcda counters (NOT cleared between runs), then
# emit one merged, rsync-scoped report. Covers the default (pipe) transport, the
# protocol-29/30 compat branches, and the real-TCP daemon path (which also runs
# the require_tcp-only tests). Run under sudo to additionally cover root-only
# paths (devices, chown, use-chroot, protected-regular). Local target -- CI uses
# the plain `coverage`/`coverage-tcp` targets.
.PHONY: coverage-all
coverage-all: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
@case '$(CFLAGS)' in *--coverage*) ;; \
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
find . -name '*.gcda' -delete
@rc=0; \
for cfg in '' '--protocol=30' '--protocol=29' '--use-tcp'; do \
echo "===== coverage-all: runtests.py $$cfg ====="; \
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT) -j $(COVERAGE_J) $$cfg || rc=$$?; \
done; \
rm -rf coverage-all && mkdir -p coverage-all; \
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
--html-details -o coverage-all/index.html . || exit $$?; \
echo "Merged coverage report written to coverage-all/index.html"; \
if test $$rc != 0; then \
echo "*** some suite runs FAILED (status $$rc) -- report still written above"; \
fi; \
exit $$rc
# Coverage for the portable (non-openat2) resolver tier. Requires a SEPARATE
# build configured with --enable-coverage --disable-openat2: its .gcno differ
# from the openat2 build, so this report cannot be merged with the others.
.PHONY: coverage-fallback
coverage-fallback:
$(MAKE) coverage COVERAGE_DIR=coverage-fallback
wildtest.o: wildtest.c t_stub.o lib/wildmatch.c rsync.h config.h
wildtest$(EXEEXT): wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@ $(LIBS)
testsuite/chown-fake.test:
ln -s chown.test $(srcdir)/testsuite/chown-fake.test
simdtest$(EXEEXT): simd-checksum-x86_64.cpp $(HEADERS)
@if test x"@ROLL_SIMD@" != x; then \
$(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DTEST_SIMD_CHECKSUM1 \
-o $@ $(srcdir)/simd-checksum-x86_64.cpp @ROLL_ASM@ $(LIBS); \
else \
touch $@; \
fi
testsuite/devices-fake.test:
ln -s devices.test $(srcdir)/testsuite/devices-fake.test
testsuite/chown-fake_test.py:
ln -s chown_test.py $(srcdir)/testsuite/chown-fake_test.py
testsuite/xattrs-hlink.test:
ln -s xattrs.test $(srcdir)/testsuite/xattrs-hlink.test
testsuite/devices-fake_test.py:
ln -s devices_test.py $(srcdir)/testsuite/devices-fake_test.py
testsuite/xattrs-hlink_test.py:
ln -s xattrs_test.py $(srcdir)/testsuite/xattrs-hlink_test.py
testsuite/exclude-lsh_test.py:
ln -s exclude_test.py $(srcdir)/testsuite/exclude-lsh_test.py
# This does *not* depend on building or installing: you can use it to
# check a version installed from a binary or some other source tree,
@@ -322,7 +484,7 @@ testsuite/xattrs-hlink.test:
.PHONY: installcheck
installcheck: $(CHECK_PROGS) $(CHECK_SYMLINKS)
POSIXLY_CORRECT=1 TOOLDIR=`pwd` rsync_bin="$(bindir)/rsync$(EXEEXT)" srcdir="$(srcdir)" $(srcdir)/runtests.sh
$(srcdir)/runtests.py --rsync-bin="$(bindir)/rsync$(EXEEXT)" --srcdir="$(srcdir)" --tooldir=`pwd` -j $(CHECK_J)
# TODO: Add 'dist' target; need to know which files will be included
@@ -339,4 +501,4 @@ doxygen:
.PHONY: doxygen-upload
doxygen-upload:
rsync -avzv $(srcdir)/dox/html/ --delete \
$${SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
$${RSYNC_SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/

1421
NEWS.md
View File

File diff suppressed because it is too large Load Diff

View File

@@ -23,8 +23,18 @@ options. To get a complete list of supported options type:
rsync --help
See the manpage for more detailed information.
See the [manpage][0] for more detailed information.
[0]: https://download.samba.org/pub/rsync/rsync.1
BUILDING AND INSTALLING
-----------------------
If you need to build rsync yourself, check out the [INSTALL][1] page for
information on what libraries and packages you can use to get the maximum
features in your build.
[1]: https://github.com/RsyncProject/rsync/blob/master/INSTALL.md
SETUP
-----
@@ -55,17 +65,17 @@ RSYNC DAEMONS
-------------
Rsync can also talk to "rsync daemons" which can provide anonymous or
authenticated rsync. See the rsyncd.conf(5) man page for details on how
to setup an rsync daemon. See the rsync(1) man page for info on how to
authenticated rsync. See the rsyncd.conf(5) manpage for details on how
to setup an rsync daemon. See the rsync(1) manpage for info on how to
connect to an rsync daemon.
WEB SITE
--------
The main rsync web site is here:
For more information, visit the [main rsync web site][2].
> https://rsync.samba.org/
[2]: https://rsync.samba.org/
You'll find a FAQ list, downloads, resources, HTML versions of the
manpages, etc.
@@ -77,66 +87,67 @@ MAILING LISTS
There is a mailing list for the discussion of rsync and its applications
that is open to anyone to join. New releases are announced on this
list, and there is also an announcement-only mailing list for those that
want official announcements. See the mailing-list page for full
details:
want official announcements. See the [mailing-list page][3] for full
details.
> https://rsync.samba.org/lists.html
[3]: https://rsync.samba.org/lists.html
DISCORD
-------
There is also an rsync [Discord server][d] for real-time chat about rsync
and its development.
[d]: https://discord.gg/Avfvy9zhdp
BUG REPORTS
-----------
To visit this web page for full the details on bug reporting:
The [bug-tracking web page][4] has full details on bug reporting.
> https://rsync.samba.org/bugtracking.html
[4]: https://rsync.samba.org/bug-tracking.html
That page contains links to the current bug list, and information on how
to report a bug well. You might also like to try searching the Internet
for the error message you've received, or looking in the mailing list
archives at:
That page contains links to the current bug list, and information on how to
do a good job when reporting a bug. You might also like to try searching
the Internet for the error message you've received, or looking in the
[mailing list archives][5].
> https://mail-archive.com/rsync@lists.samba.org/
[5]: https://mail-archive.com/rsync@lists.samba.org/
To send a bug report, follow the instructions on the bug-tracking
page of the web site.
Alternately, email your bug report to <rsync@lists.samba.org>.
For security issues please email details of the issue to <rsync.project@gmail.com>.
GIT REPOSITORY
--------------
If you want to get the very latest version of rsync direct from the
source code repository, then you will need to use git. The git repo
is hosted on github and on samba's site. Feel free to access it here:
is hosted [on GitHub][6] and [on Samba's site][7].
> https://github.com/WayneD/rsync
[6]: https://github.com/RsyncProject/rsync
[7]: https://git.samba.org/?p=rsync.git;a=summary
A backup git repo is available on the samba site:
See [the download page][8] for full details on all the ways to grab the
source.
> git clone git://git.samba.org/rsync.git
See the download page for full details on all the ways to grab the
source:
> https://rsync.samba.org/download.html
[8]: https://rsync.samba.org/download.html
COPYRIGHT
---------
Rsync was originally written by Andrew Tridgell and is currently
maintained by Wayne Davison. It has been improved by many developers
from around the world.
Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
people from around the world have helped to maintain and improve it.
Rsync may be used, modified and redistributed only under the terms of
the GNU General Public License, found in the file COPYING in this
distribution, or at:
the GNU General Public License, found in the file [COPYING][9] in this
distribution, or at [the Free Software Foundation][10].
> https://www.fsf.org/licenses/gpl.html
AVAILABILITY
------------
The main web site for rsync is https://rsync.samba.org/
[9]: https://github.com/RsyncProject/rsync/blob/master/COPYING
[10]: https://www.fsf.org/licenses/gpl.html

13
SECURITY.md Normal file
View File

@@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
Only the current release of the software is actively supported. If you need
help backporting fixes into an older release, feel free to ask.
## Reporting a Vulnerability
Email your vulnerability information to rsync's maintainer:
Rsync Project <rsync.project@gmail.com>

11
TODO
View File

@@ -15,7 +15,6 @@ Create more granular verbosity 2003/05/15
DOCUMENTATION --------------------------------------------------------
Keep list of open issues and todos on the web site
Perhaps redo manual as SGML
LOGGING --------------------------------------------------------------
Memory accounting
@@ -213,16 +212,6 @@ DOCUMENTATION --------------------------------------------------------
Keep list of open issues and todos on the web site
-- --
Perhaps redo manual as SGML
The man page is getting rather large, and there is more information
that ought to be added.
TexInfo source is probably a dying format.
Linuxdoc looks like the most likely contender. I know DocBook is
favoured by some people, but it's so bloody verbose, even with emacs
support.

View File

@@ -2,7 +2,7 @@
* Routines to authenticate access to a daemon (hosts allow/deny).
*
* Copyright (C) 1998 Andrew Tridgell
* Copyright (C) 2004-2020 Wayne Davison
* Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +20,9 @@
#include "rsync.h"
#include "ifuncs.h"
#ifdef HAVE_NETGROUP_H
#include <netgroup.h>
#endif
static int allow_forward_dns;
@@ -34,6 +37,11 @@ static int match_hostname(const char **host_ptr, const char *addr, const char *t
if (!host || !*host)
return 0;
#ifdef HAVE_INNETGR
if (*tok == '@' && tok[1])
return innetgr(tok + 1, host, NULL, NULL);
#endif
/* First check if the reverse-DNS-determined hostname matches. */
if (iwildmatch(tok, host))
return 1;
@@ -91,7 +99,7 @@ static void make_mask(char *mask, int plen, int addrlen)
return;
}
static int match_address(const char *addr, const char *tok)
static int match_address(const char *addr, char *tok)
{
char *p;
struct addrinfo hints, *resa, *rest;

13
acls.c
View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2006-2020 Wayne Davison
* Copyright (C) 2006-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@ extern int dry_run;
extern int am_root;
extern int read_only;
extern int list_only;
extern int orig_umask;
extern mode_t orig_umask;
extern int numeric_ids;
extern int inc_recurse;
extern int preserve_devices;
@@ -519,6 +519,7 @@ static int get_rsync_acl(const char *fname, rsync_acl *racl,
sys_acl_free_acl(sacl);
if (!ok) {
rsyserr(FERROR_XFER, errno, "get_acl: unpack_smb_acl(%s)", fname);
return -1;
}
} else if (no_acl_syscall_error(errno)) {
@@ -696,7 +697,7 @@ static uint32 recv_acl_access(int f, uchar *name_follows_ptr)
static uchar recv_ida_entries(int f, ida_entries *ent)
{
uchar computed_mask_bits = 0;
int i, count = read_varint(f);
int i, count = read_varint_bounded(f, 0, MAX_WIRE_ACL_COUNT, "ACL count");
ent->idas = count ? new_array(id_access, count) : NULL;
ent->count = count;
@@ -712,7 +713,7 @@ static uchar recv_ida_entries(int f, ida_entries *ent)
else
id = recv_group_name(f, id, NULL);
} else if (access & NAME_IS_USER) {
if (inc_recurse && am_root && !numeric_ids)
if (inc_recurse && !numeric_ids)
id = match_uid(id);
} else {
if (inc_recurse && (!am_root || !numeric_ids))
@@ -763,6 +764,8 @@ static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode
#ifdef HAVE_OSX_ACLS
/* If we received a superfluous mask, throw it away. */
duo_item->racl.mask_obj = NO_ENTRY;
(void)mode;
(void)computed_mask_bits;
#else
if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
/* Mask must be non-empty with lists. */
@@ -979,7 +982,7 @@ static int set_rsync_acl(const char *fname, acl_duo *duo_item,
&& !pack_smb_acl(&duo_item->sacl, &duo_item->racl))
return -1;
#ifdef HAVE_OSX_ACLS
mode = 0; /* eliminate compiler warning */
(void)mode; /* eliminate compiler warning */
#else
if (type == SMB_ACL_TYPE_ACCESS) {
cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl, cur_mode, mode);

82
android.c Normal file
View File

@@ -0,0 +1,82 @@
/*
* Android-specific helpers.
*
* openat2() usability probe
* -------------------------
* openat2(2) is invoked directly via syscall() because the C library lacked a
* wrapper for it for years. Under a seccomp filter that uses
* SECCOMP_RET_TRAP -- as the Android application sandbox does -- a disallowed
* syscall raises SIGSYS and *kills the process* rather than failing with
* ENOSYS, so inspecting errno after the call is too late. We therefore probe
* openat2() once, behind a temporary SIGSYS handler, so a trapped syscall is
* caught and secure_relative_open_linux() can fall back to the portable
* per-component O_NOFOLLOW resolver instead of the whole process dying.
*
* This is only needed on Android, so the probe body is compiled only there.
* __ANDROID__ is defined by Bionic's headers and reflects the *target*, not
* the build host: it is set both for NDK cross-compiles (from a Linux/macOS
* host) and for native Termux builds, and is unset on every other platform.
* That makes it a reliable compile-time switch for cross builds -- there is
* nothing to detect in configure. Everywhere else openat2() is never
* seccomp-trapped to SIGSYS (a missing syscall simply returns ENOSYS), so
* openat2_usable() collapses to a constant 1 with no run-time cost.
*/
#include "rsync.h"
#if defined(__ANDROID__) && defined(HAVE_OPENAT2)
#include <setjmp.h>
#include <sys/syscall.h>
#include <linux/openat2.h>
static sigjmp_buf openat2_probe_env;
static void openat2_probe_handler(int signo)
{
(void)signo;
siglongjmp(openat2_probe_env, 1);
}
#endif
int openat2_usable(void)
{
#if defined(__ANDROID__) && defined(HAVE_OPENAT2)
static int cached = -1;
struct sigaction sa, old_sa;
if (cached >= 0)
return cached;
memset(&sa, 0, sizeof sa);
sa.sa_handler = openat2_probe_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGSYS, &sa, &old_sa) != 0)
return cached = 0;
if (sigsetjmp(openat2_probe_env, 1) != 0) {
/* SIGSYS delivered: openat2 is blocked by a seccomp filter. */
cached = 0;
} else {
struct open_how how;
int fd;
memset(&how, 0, sizeof how);
how.flags = O_RDONLY | O_DIRECTORY;
how.resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS;
fd = syscall(SYS_openat2, AT_FDCWD, ".", &how, sizeof how);
if (fd >= 0)
close(fd);
/* Usable only if the probe actually succeeded. Any failure --
* ENOSYS (kernel < 5.6), a seccomp SECCOMP_RET_ERRNO denial
* (EPERM/EACCES), or EINVAL (RESOLVE_BENEATH unsupported) --
* means we must fall back to the portable O_NOFOLLOW walk. */
cached = fd >= 0;
}
sigaction(SIGSYS, &old_sa, NULL);
return cached;
#else
return 1;
#endif
}

View File

@@ -2,7 +2,7 @@
* Support rsync daemon authentication.
*
* Copyright (C) 1998-2000 Andrew Tridgell
* Copyright (C) 2002-2020 Wayne Davison
* Copyright (C) 2002-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@
extern int read_only;
extern char *password_file;
extern struct name_num_obj valid_auth_checksums;
/***************************************************************************
encode a buffer using base64 - simple and slow algorithm. null terminates
@@ -72,9 +73,9 @@ static void gen_challenge(const char *addr, char *challenge)
SIVAL(input, 20, tv.tv_usec);
SIVAL(input, 24, getpid());
sum_init(-1, 0);
len = sum_init(valid_auth_checksums.negotiated_nni, 0);
sum_update(input, sizeof input);
len = sum_end(digest);
sum_end(digest);
base64_encode(digest, len, challenge, 0);
}
@@ -86,10 +87,10 @@ static void generate_hash(const char *in, const char *challenge, char *out)
char buf[MAX_DIGEST_LEN];
int len;
sum_init(-1, 0);
len = sum_init(valid_auth_checksums.negotiated_nni, 0);
sum_update(in, strlen(in));
sum_update(challenge, strlen(challenge));
len = sum_end(buf);
sum_end(buf);
base64_encode(buf, len, out, 0);
}
@@ -119,7 +120,7 @@ static const char *check_secret(int module, const char *user, const char *group,
if ((st.st_mode & 06) != 0) {
rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
ok = 0;
} else if (MY_UID() == 0 && st.st_uid != 0) {
} else if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
ok = 0;
}
@@ -196,7 +197,7 @@ static const char *getpassf(const char *filename)
rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
exit_cleanup(RERR_SYNTAX);
}
if (MY_UID() == 0 && st.st_uid != 0) {
if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
exit_cleanup(RERR_SYNTAX);
}
@@ -227,7 +228,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
char *users = lp_auth_users(module);
char challenge[MAX_DIGEST_LEN*2];
char line[BIGPATHBUFLEN];
char **auth_uid_groups = NULL;
const char **auth_uid_groups = NULL;
int auth_uid_groups_cnt = -1;
const char *err = NULL;
int group_match = -1;
@@ -238,6 +239,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
if (!users || !*users)
return "";
negotiate_daemon_auth(f_out, 0);
gen_challenge(addr, challenge);
io_printf(f_out, "%s%s\n", leader, challenge);
@@ -287,7 +289,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
else {
gid_t *gid_array = gid_list.items;
auth_uid_groups_cnt = gid_list.count;
auth_uid_groups = new_array(char *, auth_uid_groups_cnt);
auth_uid_groups = new_array(const char *, auth_uid_groups_cnt);
for (j = 0; j < auth_uid_groups_cnt; j++)
auth_uid_groups[j] = gid_to_group(gid_array[j]);
}
@@ -313,7 +315,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
else if (opt_ch == 'd')
err = "denied by rule";
else {
char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
const char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
err = check_secret(module, line, group, challenge, pass);
}
@@ -324,7 +326,7 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
int j;
for (j = 0; j < auth_uid_groups_cnt; j++) {
if (auth_uid_groups[j])
free(auth_uid_groups[j]);
free((char*)auth_uid_groups[j]);
}
free(auth_uid_groups);
}
@@ -350,6 +352,7 @@ void auth_client(int fd, const char *user, const char *challenge)
if (!user || !*user)
user = "nobody";
negotiate_daemon_auth(-1, 1);
if (!(pass = getpassf(password_file))
&& !(pass = getenv("RSYNC_PASSWORD"))) {

View File

@@ -2,7 +2,7 @@
* Backup handling code.
*
* Copyright (C) 1999 Andrew Tridgell
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@ static int validate_backup_dir(void)
{
STRUCT_STAT st;
if (do_lstat(backup_dir_buf, &st) < 0) {
if (do_lstat_at(backup_dir_buf, &st) < 0) {
if (errno == ENOENT)
return 0;
rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
@@ -98,7 +98,7 @@ static BOOL copy_valid_path(const char *fname)
for ( ; b; name = b + 1, b = strchr(name, '/')) {
*b = '\0';
while (do_mkdir(backup_dir_buf, ACCESSPERMS) < 0) {
while (do_mkdir_at(backup_dir_buf, ACCESSPERMS) < 0) {
if (errno == EEXIST) {
val = validate_backup_dir();
if (val > 0)
@@ -197,7 +197,7 @@ static inline int link_or_rename(const char *from, const char *to,
if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
return 0; /* Use copy code. */
#endif
if (do_link(from, to) == 0) {
if (do_link_at(from, to) == 0) {
if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
return 2;
@@ -207,7 +207,7 @@ static inline int link_or_rename(const char *from, const char *to,
return 0;
}
#endif
if (do_rename(from, to) == 0) {
if (do_rename_at(from, to) == 0) {
if (stp->st_nlink > 1 && !S_ISDIR(stp->st_mode)) {
/* If someone has hard-linked the file into the backup
* dir, rename() might return success but do nothing! */
@@ -246,7 +246,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
goto success;
if (errno == EEXIST || errno == EISDIR) {
STRUCT_STAT bakst;
if (do_lstat(buf, &bakst) == 0) {
if (do_lstat_at(buf, &bakst) == 0) {
int flags = get_del_for_flag(bakst.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
if (delete_item(buf, bakst.st_mode, flags) != 0)
return 0;
@@ -277,7 +277,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
/* Check to see if this is a device file, or link */
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|| (preserve_specials && IS_SPECIAL(file->mode))) {
if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
if (do_mknod_at(buf, file->mode, sx.st.st_rdev) < 0)
rsyserr(FERROR, errno, "mknod %s failed", full_fname(buf));
else if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: DEVICE %s successful.\n", fname);
@@ -294,7 +294,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
}
ret = 2;
} else {
if (do_symlink(sl, buf) < 0)
if (do_symlink_at(sl, buf) < 0)
rsyserr(FERROR, errno, "link %s -> \"%s\"", full_fname(buf), sl);
else if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: SYMLINK %s successful.\n", fname);
@@ -304,7 +304,8 @@ int make_backup(const char *fname, BOOL prefer_rename)
#endif
if (!ret && !S_ISREG(file->mode)) {
rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
if (INFO_GTE(NONREG, 1))
rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
unmake_file(file);
#ifdef SUPPORT_ACLS
uncache_tmp_acls();

View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1999 Weiss
* Copyright (C) 2004 Chris Shoemaker
* Copyright (C) 2004-2020 Wayne Davison
* Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -75,7 +75,7 @@ static int *flag_ptr[] = {
NULL
};
static char *flag_name[] = {
static const char *const flag_name[] = {
"--recurse (-r)",
"--owner (-o)",
"--group (-g)",
@@ -194,7 +194,7 @@ static int write_opt(const char *opt, const char *arg)
{
int len = strlen(opt);
int err = write(batch_sh_fd, " ", 1) != 1;
err = write(batch_sh_fd, opt, len) != len ? 1 : 0;
err = write(batch_sh_fd, opt, len) != len ? 1 : 0;
if (arg) {
err |= write(batch_sh_fd, "=", 1) != 1;
err |= write_arg(arg);

View File

@@ -2,7 +2,7 @@
* Simple byteorder handling.
*
* Copyright (C) 1992-1995 Andrew Tridgell
* Copyright (C) 2007-2020 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -129,4 +129,3 @@ SIVAL(char *buf, int pos, uint32 val)
{
SIVALu((uchar*)buf, pos, val);
}

View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2004-2020 Wayne Davison
* Copyright (C) 2004-2023 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@
#include "rsync.h"
#ifdef SUPPORT_XXHASH
#include "xxhash.h"
#include <xxhash.h>
# if XXH_VERSION_NUMBER >= 800
# define SUPPORT_XXH3 1
# endif
@@ -42,41 +42,94 @@ extern int protocol_version;
extern int proper_seed_order;
extern const char *checksum_choice;
struct name_num_obj valid_checksums = {
"checksum", NULL, NULL, 0, 0, {
#define NNI_BUILTIN (1<<0)
#define NNI_EVP (1<<1)
#define NNI_EVP_OK (1<<2)
struct name_num_item valid_checksums_items[] = {
#ifdef SUPPORT_XXH3
{ CSUM_XXH3_128, "xxh128", NULL },
{ CSUM_XXH3_64, "xxh3", NULL },
{ CSUM_XXH3_128, 0, "xxh128", NULL },
{ CSUM_XXH3_64, 0, "xxh3", NULL },
#endif
#ifdef SUPPORT_XXHASH
{ CSUM_XXH64, "xxh64", NULL },
{ CSUM_XXH64, "xxhash", NULL },
{ CSUM_XXH64, 0, "xxh64", NULL },
{ CSUM_XXH64, 0, "xxhash", NULL },
#endif
{ CSUM_MD5, "md5", NULL },
{ CSUM_MD4, "md4", NULL },
{ CSUM_NONE, "none", NULL },
{ 0, NULL, NULL }
}
{ CSUM_MD5, NNI_BUILTIN|NNI_EVP, "md5", NULL },
{ CSUM_MD4, NNI_BUILTIN|NNI_EVP, "md4", NULL },
#ifdef SHA_DIGEST_LENGTH
{ CSUM_SHA1, NNI_EVP, "sha1", NULL },
#endif
{ CSUM_NONE, 0, "none", NULL },
{ 0, 0, NULL, NULL }
};
int xfersum_type = 0; /* used for the file transfer checksums */
int checksum_type = 0; /* used for the pre-transfer (--checksum) checksums */
struct name_num_obj valid_checksums = {
"checksum", NULL, 0, 0, valid_checksums_items
};
int parse_csum_name(const char *name, int len)
struct name_num_item valid_auth_checksums_items[] = {
#ifdef SHA512_DIGEST_LENGTH
{ CSUM_SHA512, NNI_EVP, "sha512", NULL },
#endif
#ifdef SHA256_DIGEST_LENGTH
{ CSUM_SHA256, NNI_EVP, "sha256", NULL },
#endif
#ifdef SHA_DIGEST_LENGTH
{ CSUM_SHA1, NNI_EVP, "sha1", NULL },
#endif
{ CSUM_MD5, NNI_BUILTIN|NNI_EVP, "md5", NULL },
{ CSUM_MD4, NNI_BUILTIN|NNI_EVP, "md4", NULL },
{ 0, 0, NULL, NULL }
};
struct name_num_obj valid_auth_checksums = {
"daemon auth checksum", NULL, 0, 0, valid_auth_checksums_items
};
/* These cannot make use of openssl, so they're marked just as built-in */
struct name_num_item implied_checksum_md4 =
{ CSUM_MD4, NNI_BUILTIN, "md4", NULL };
struct name_num_item implied_checksum_md5 =
{ CSUM_MD5, NNI_BUILTIN, "md5", NULL };
struct name_num_item *xfer_sum_nni; /* used for the transfer checksum2 computations */
int xfer_sum_len;
struct name_num_item *file_sum_nni; /* used for the pre-transfer --checksum computations */
int file_sum_len, file_sum_extra_cnt;
#ifdef USE_OPENSSL
const EVP_MD *xfer_sum_evp_md;
const EVP_MD *file_sum_evp_md;
EVP_MD_CTX *ctx_evp = NULL;
#endif
static int initialized_choices = 0;
struct name_num_item *parse_csum_name(const char *name, int len)
{
struct name_num_item *nni;
if (len < 0 && name)
len = strlen(name);
init_checksum_choices();
if (!name || (len == 4 && strncasecmp(name, "auto", 4) == 0)) {
if (protocol_version >= 30)
return CSUM_MD5;
if (protocol_version >= 27)
return CSUM_MD4_OLD;
if (protocol_version >= 21)
return CSUM_MD4_BUSTED;
return CSUM_MD4_ARCHAIC;
if (protocol_version >= 30) {
if (!proper_seed_order)
return &implied_checksum_md5;
name = "md5";
len = 3;
} else {
if (protocol_version >= 27)
implied_checksum_md4.num = CSUM_MD4_OLD;
else if (protocol_version >= 21)
implied_checksum_md4.num = CSUM_MD4_BUSTED;
else
implied_checksum_md4.num = CSUM_MD4_ARCHAIC;
return &implied_checksum_md4;
}
}
nni = get_nni_by_name(&valid_checksums, name, len);
@@ -86,44 +139,74 @@ int parse_csum_name(const char *name, int len)
exit_cleanup(RERR_UNSUPPORTED);
}
return nni->num;
return nni;
}
static const char *checksum_name(int num)
#ifdef USE_OPENSSL
static const EVP_MD *csum_evp_md(struct name_num_item *nni)
{
struct name_num_item *nni = get_nni_by_num(&valid_checksums, num);
const EVP_MD *emd;
if (!(nni->flags & NNI_EVP))
return NULL;
return nni ? nni->name : num < CSUM_MD4 ? "md4" : "UNKNOWN";
#ifdef USE_MD5_ASM
if (nni->num == CSUM_MD5)
emd = NULL;
else
#endif
emd = EVP_get_digestbyname(nni->name);
if (emd && !(nni->flags & NNI_EVP_OK)) { /* Make sure it works before we advertise it */
if (!ctx_evp && !(ctx_evp = EVP_MD_CTX_create()))
out_of_memory("csum_evp_md");
/* Some routines are marked as legacy and are not enabled in the openssl.cnf file.
* If we can't init the emd, we'll fall back to our built-in code. */
if (EVP_DigestInit_ex(ctx_evp, emd, NULL) == 0)
emd = NULL;
else
nni->flags = (nni->flags & ~NNI_BUILTIN) | NNI_EVP_OK;
}
if (!emd)
nni->flags &= ~NNI_EVP;
return emd;
}
#endif
void parse_checksum_choice(int final_call)
{
if (valid_checksums.negotiated_name)
xfersum_type = checksum_type = valid_checksums.negotiated_num;
if (valid_checksums.negotiated_nni)
xfer_sum_nni = file_sum_nni = valid_checksums.negotiated_nni;
else {
char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
const char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
if (cp) {
xfersum_type = parse_csum_name(checksum_choice, cp - checksum_choice);
checksum_type = parse_csum_name(cp+1, -1);
xfer_sum_nni = parse_csum_name(checksum_choice, cp - checksum_choice);
file_sum_nni = parse_csum_name(cp+1, -1);
} else
xfersum_type = checksum_type = parse_csum_name(checksum_choice, -1);
xfer_sum_nni = file_sum_nni = parse_csum_name(checksum_choice, -1);
if (am_server && checksum_choice)
validate_choice_vs_env(NSTR_CHECKSUM, xfersum_type, checksum_type);
validate_choice_vs_env(NSTR_CHECKSUM, xfer_sum_nni->num, file_sum_nni->num);
}
xfer_sum_len = csum_len_for_type(xfer_sum_nni->num, 0);
file_sum_len = csum_len_for_type(file_sum_nni->num, 0);
#ifdef USE_OPENSSL
xfer_sum_evp_md = csum_evp_md(xfer_sum_nni);
file_sum_evp_md = csum_evp_md(file_sum_nni);
#endif
if (xfersum_type == CSUM_NONE)
file_sum_extra_cnt = (file_sum_len + EXTRA_LEN - 1) / EXTRA_LEN;
if (xfer_sum_nni->num == CSUM_NONE)
whole_file = 1;
/* Snag the checksum name for both write_batch's option output & the following debug output. */
if (valid_checksums.negotiated_name)
checksum_choice = valid_checksums.negotiated_name;
if (valid_checksums.negotiated_nni)
checksum_choice = valid_checksums.negotiated_nni->name;
else if (checksum_choice == NULL)
checksum_choice = checksum_name(xfersum_type);
checksum_choice = xfer_sum_nni->name;
if (final_call && DEBUG_GTE(NSTR, am_server ? 3 : 1)) {
rprintf(FINFO, "%s%s checksum: %s\n",
am_server ? "Server" : "Client",
valid_checksums.negotiated_name ? " negotiated" : "",
valid_checksums.negotiated_nni ? " negotiated" : "",
checksum_choice);
}
}
@@ -143,6 +226,18 @@ int csum_len_for_type(int cst, BOOL flist_csum)
return MD4_DIGEST_LEN;
case CSUM_MD5:
return MD5_DIGEST_LEN;
#ifdef SHA_DIGEST_LENGTH
case CSUM_SHA1:
return SHA_DIGEST_LENGTH;
#endif
#ifdef SHA256_DIGEST_LENGTH
case CSUM_SHA256:
return SHA256_DIGEST_LENGTH;
#endif
#ifdef SHA512_DIGEST_LENGTH
case CSUM_SHA512:
return SHA512_DIGEST_LENGTH;
#endif
case CSUM_XXH64:
case CSUM_XXH3_64:
return 64/8;
@@ -168,6 +263,9 @@ int canonical_checksum(int csum_type)
break;
case CSUM_MD4:
case CSUM_MD5:
case CSUM_SHA1:
case CSUM_SHA256:
case CSUM_SHA512:
return -1;
case CSUM_XXH64:
case CSUM_XXH3_64:
@@ -179,7 +277,7 @@ int canonical_checksum(int csum_type)
return 0;
}
#ifndef HAVE_SIMD /* See simd-checksum-*.cpp. */
#ifndef USE_ROLL_SIMD /* See simd-checksum-*.cpp. */
/*
a simple 32 bit checksum that can be updated from either end
(inspired by Mark Adler's Adler-32 checksum)
@@ -202,9 +300,25 @@ uint32 get_checksum1(char *buf1, int32 len)
}
#endif
/* The "sum" buffer must be at least MAX_DIGEST_LEN bytes! */
void get_checksum2(char *buf, int32 len, char *sum)
{
switch (xfersum_type) {
#ifdef USE_OPENSSL
if (xfer_sum_evp_md) {
static EVP_MD_CTX *evp = NULL;
uchar seedbuf[4];
if (!evp && !(evp = EVP_MD_CTX_create()))
out_of_memory("get_checksum2");
EVP_DigestInit_ex(evp, xfer_sum_evp_md, NULL);
if (checksum_seed) {
SIVALu(seedbuf, 0, checksum_seed);
EVP_DigestUpdate(evp, seedbuf, 4);
}
EVP_DigestUpdate(evp, (uchar *)buf, len);
EVP_DigestFinal_ex(evp, (uchar *)sum, NULL);
} else
#endif
switch (xfer_sum_nni->num) {
#ifdef SUPPORT_XXHASH
case CSUM_XXH64:
SIVAL64(sum, 0, XXH64(buf, len, checksum_seed));
@@ -222,40 +336,26 @@ void get_checksum2(char *buf, int32 len, char *sum)
}
#endif
case CSUM_MD5: {
MD5_CTX m5;
md_context m5;
uchar seedbuf[4];
MD5_Init(&m5);
md5_begin(&m5);
if (proper_seed_order) {
if (checksum_seed) {
SIVALu(seedbuf, 0, checksum_seed);
MD5_Update(&m5, seedbuf, 4);
md5_update(&m5, seedbuf, 4);
}
MD5_Update(&m5, (uchar *)buf, len);
md5_update(&m5, (uchar *)buf, len);
} else {
MD5_Update(&m5, (uchar *)buf, len);
md5_update(&m5, (uchar *)buf, len);
if (checksum_seed) {
SIVALu(seedbuf, 0, checksum_seed);
MD5_Update(&m5, seedbuf, 4);
md5_update(&m5, seedbuf, 4);
}
}
MD5_Final((uchar *)sum, &m5);
md5_result(&m5, (uchar *)sum);
break;
}
case CSUM_MD4:
#ifdef USE_OPENSSL
{
MD4_CTX m4;
MD4_Init(&m4);
MD4_Update(&m4, (uchar *)buf, len);
if (checksum_seed) {
uchar seedbuf[4];
SIVALu(seedbuf, 0, checksum_seed);
MD4_Update(&m4, seedbuf, 4);
}
MD4_Final((uchar *)sum, &m4);
break;
}
#endif
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC: {
@@ -266,9 +366,8 @@ void get_checksum2(char *buf, int32 len, char *sum)
mdfour_begin(&m);
if (len > len1) {
if (buf1)
free(buf1);
if (len > len1 || !buf1) {
free(buf1);
buf1 = new_array(char, len+4);
len1 = len;
}
@@ -288,7 +387,7 @@ void get_checksum2(char *buf, int32 len, char *sum)
* are multiples of 64. This is fixed by calling mdfour_update()
* even when there are no more bytes.
*/
if (len - i > 0 || xfersum_type > CSUM_MD4_BUSTED)
if (len - i > 0 || xfer_sum_nni->num > CSUM_MD4_BUSTED)
mdfour_update(&m, (uchar *)(buf1+i), len-i);
mdfour_result(&m, (uchar *)sum);
@@ -306,15 +405,33 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
int32 remainder;
int fd;
memset(sum, 0, MAX_DIGEST_LEN);
fd = do_open(fname, O_RDONLY, 0);
if (fd == -1)
fd = do_open_checklinks(fname);
if (fd == -1) {
memset(sum, 0, file_sum_len);
return;
}
buf = map_file(fd, len, MAX_MAP_SIZE, CHUNK_SIZE);
switch (checksum_type) {
#ifdef USE_OPENSSL
if (file_sum_evp_md) {
static EVP_MD_CTX *evp = NULL;
if (!evp && !(evp = EVP_MD_CTX_create()))
out_of_memory("file_checksum");
EVP_DigestInit_ex(evp, file_sum_evp_md, NULL);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
EVP_DigestUpdate(evp, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
remainder = (int32)(len - i);
if (remainder > 0)
EVP_DigestUpdate(evp, (uchar *)map_ptr(buf, i, remainder), remainder);
EVP_DigestFinal_ex(evp, (uchar *)sum, NULL);
} else
#endif
switch (file_sum_nni->num) {
#ifdef SUPPORT_XXHASH
case CSUM_XXH64: {
static XXH64_state_t* state = NULL;
@@ -374,38 +491,21 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
}
#endif
case CSUM_MD5: {
MD5_CTX m5;
md_context m5;
MD5_Init(&m5);
md5_begin(&m5);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
MD5_Update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
md5_update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
remainder = (int32)(len - i);
if (remainder > 0)
MD5_Update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder);
md5_update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder);
MD5_Final((uchar *)sum, &m5);
md5_result(&m5, (uchar *)sum);
break;
}
case CSUM_MD4:
#ifdef USE_OPENSSL
{
MD4_CTX m4;
MD4_Init(&m4);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
MD4_Update(&m4, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
remainder = (int32)(len - i);
if (remainder > 0)
MD4_Update(&m4, (uchar *)map_ptr(buf, i, remainder), remainder);
MD4_Final((uchar *)sum, &m4);
break;
}
#endif
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC: {
@@ -413,15 +513,15 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
mdfour_begin(&m);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
mdfour_update(&m, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK), CSUM_CHUNK);
/* Prior to version 27 an incorrect MD4 checksum was computed
* by failing to call mdfour_tail() for block sizes that
* are multiples of 64. This is fixed by calling mdfour_update()
* even when there are no more bytes. */
remainder = (int32)(len - i);
if (remainder > 0 || checksum_type > CSUM_MD4_BUSTED)
if (remainder > 0 || file_sum_nni->num > CSUM_MD4_BUSTED)
mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
mdfour_result(&m, (uchar *)sum);
@@ -429,7 +529,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
}
default:
rprintf(FERROR, "Invalid checksum-choice for --checksum: %s (%d)\n",
checksum_name(checksum_type), checksum_type);
file_sum_nni->name, file_sum_nni->num);
exit_cleanup(RERR_UNSUPPORTED);
}
@@ -438,30 +538,43 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
}
static int32 sumresidue;
static union {
md_context md;
#ifdef USE_OPENSSL
MD4_CTX m4;
#endif
MD5_CTX m5;
} ctx;
static md_context ctx_md;
#ifdef SUPPORT_XXHASH
static XXH64_state_t* xxh64_state;
#endif
#ifdef SUPPORT_XXH3
static XXH3_state_t* xxh3_state;
#endif
static int cursum_type;
static struct name_num_item *cur_sum_nni;
int cur_sum_len;
void sum_init(int csum_type, int seed)
#ifdef USE_OPENSSL
static const EVP_MD *cur_sum_evp_md;
#endif
/* Initialize a hash digest accumulator. Data is supplied via
* sum_update() and the resulting binary digest is retrieved via
* sum_end(). This only supports one active sum at a time. */
int sum_init(struct name_num_item *nni, int seed)
{
char s[4];
if (csum_type < 0)
csum_type = parse_csum_name(NULL, 0);
cursum_type = csum_type;
if (!nni)
nni = parse_csum_name(NULL, 0);
cur_sum_nni = nni;
cur_sum_len = csum_len_for_type(nni->num, 0);
#ifdef USE_OPENSSL
cur_sum_evp_md = csum_evp_md(nni);
#endif
switch (csum_type) {
#ifdef USE_OPENSSL
if (cur_sum_evp_md) {
if (!ctx_evp && !(ctx_evp = EVP_MD_CTX_create()))
out_of_memory("file_checksum");
EVP_DigestInit_ex(ctx_evp, cur_sum_evp_md, NULL);
} else
#endif
switch (cur_sum_nni->num) {
#ifdef SUPPORT_XXHASH
case CSUM_XXH64:
if (!xxh64_state && !(xxh64_state = XXH64_createState()))
@@ -482,20 +595,16 @@ void sum_init(int csum_type, int seed)
break;
#endif
case CSUM_MD5:
MD5_Init(&ctx.m5);
md5_begin(&ctx_md);
break;
case CSUM_MD4:
#ifdef USE_OPENSSL
MD4_Init(&ctx.m4);
#else
mdfour_begin(&ctx.md);
mdfour_begin(&ctx_md);
sumresidue = 0;
#endif
break;
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC:
mdfour_begin(&ctx.md);
mdfour_begin(&ctx_md);
sumresidue = 0;
SIVAL(s, 0, seed);
sum_update(s, 4);
@@ -505,19 +614,19 @@ void sum_init(int csum_type, int seed)
default: /* paranoia to prevent missing case values */
exit_cleanup(RERR_UNSUPPORTED);
}
return cur_sum_len;
}
/**
* Feed data into an MD4 accumulator, md. The results may be
* retrieved using sum_end(). md is used for different purposes at
* different points during execution.
*
* @todo Perhaps get rid of md and just pass in the address each time.
* Very slightly clearer and slower.
**/
/* Feed data into a hash digest accumulator. */
void sum_update(const char *p, int32 len)
{
switch (cursum_type) {
#ifdef USE_OPENSSL
if (cur_sum_evp_md) {
EVP_DigestUpdate(ctx_evp, (uchar *)p, len);
} else
#endif
switch (cur_sum_nni->num) {
#ifdef SUPPORT_XXHASH
case CSUM_XXH64:
XXH64_update(xxh64_state, p, len);
@@ -532,39 +641,35 @@ void sum_update(const char *p, int32 len)
break;
#endif
case CSUM_MD5:
MD5_Update(&ctx.m5, (uchar *)p, len);
md5_update(&ctx_md, (uchar *)p, len);
break;
case CSUM_MD4:
#ifdef USE_OPENSSL
MD4_Update(&ctx.m4, (uchar *)p, len);
break;
#endif
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC:
if (len + sumresidue < CSUM_CHUNK) {
memcpy(ctx.md.buffer + sumresidue, p, len);
memcpy(ctx_md.buffer + sumresidue, p, len);
sumresidue += len;
break;
}
if (sumresidue) {
int32 i = CSUM_CHUNK - sumresidue;
memcpy(ctx.md.buffer + sumresidue, p, i);
mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, CSUM_CHUNK);
memcpy(ctx_md.buffer + sumresidue, p, i);
mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, CSUM_CHUNK);
len -= i;
p += i;
}
while (len >= CSUM_CHUNK) {
mdfour_update(&ctx.md, (uchar *)p, CSUM_CHUNK);
mdfour_update(&ctx_md, (uchar *)p, CSUM_CHUNK);
len -= CSUM_CHUNK;
p += CSUM_CHUNK;
}
sumresidue = len;
if (sumresidue)
memcpy(ctx.md.buffer, p, sumresidue);
memcpy(ctx_md.buffer, p, sumresidue);
break;
case CSUM_NONE:
break;
@@ -573,13 +678,18 @@ void sum_update(const char *p, int32 len)
}
}
/* NOTE: all the callers of sum_end() pass in a pointer to a buffer that is
* MAX_DIGEST_LEN in size, so even if the csum-len is shorter that that (i.e.
* CSUM_MD4_ARCHAIC), we don't have to worry about limiting the data we write
* into the "sum" buffer. */
int sum_end(char *sum)
/* The sum buffer only needs to be as long as the current checksum's digest
* len, not MAX_DIGEST_LEN. Note that for CSUM_MD4_ARCHAIC that is the full
* MD4_DIGEST_LEN even if the file-list code is going to ignore all but the
* first 2 bytes of it. */
void sum_end(char *sum)
{
switch (cursum_type) {
#ifdef USE_OPENSSL
if (cur_sum_evp_md) {
EVP_DigestFinal_ex(ctx_evp, (uchar *)sum, NULL);
} else
#endif
switch (cur_sum_nni->num) {
#ifdef SUPPORT_XXHASH
case CSUM_XXH64:
SIVAL64(sum, 0, XXH64_digest(xxh64_state));
@@ -597,22 +707,18 @@ int sum_end(char *sum)
}
#endif
case CSUM_MD5:
MD5_Final((uchar *)sum, &ctx.m5);
md5_result(&ctx_md, (uchar *)sum);
break;
case CSUM_MD4:
#ifdef USE_OPENSSL
MD4_Final((uchar *)sum, &ctx.m4);
break;
#endif
case CSUM_MD4_OLD:
mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, sumresidue);
mdfour_result(&ctx.md, (uchar *)sum);
mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, sumresidue);
mdfour_result(&ctx_md, (uchar *)sum);
break;
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC:
if (sumresidue)
mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, sumresidue);
mdfour_result(&ctx.md, (uchar *)sum);
mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, sumresidue);
mdfour_result(&ctx_md, (uchar *)sum);
break;
case CSUM_NONE:
*sum = '\0';
@@ -620,6 +726,78 @@ int sum_end(char *sum)
default: /* paranoia to prevent missing case values */
exit_cleanup(RERR_UNSUPPORTED);
}
return csum_len_for_type(cursum_type, 0);
}
#if defined SUPPORT_XXH3 || defined USE_OPENSSL
static void verify_digest(struct name_num_item *nni, BOOL check_auth_list)
{
#ifdef SUPPORT_XXH3
static int xxh3_result = 0;
#endif
#ifdef USE_OPENSSL
static int prior_num = 0, prior_flags = 0, prior_result = 0;
#endif
#ifdef SUPPORT_XXH3
if (nni->num == CSUM_XXH3_64 || nni->num == CSUM_XXH3_128) {
if (!xxh3_result) {
char buf[32816];
int j;
for (j = 0; j < (int)sizeof buf; j++)
buf[j] = ' ' + (j % 96);
sum_init(nni, 0);
sum_update(buf, 32816);
sum_update(buf, 31152);
sum_update(buf, 32474);
sum_update(buf, 9322);
xxh3_result = XXH3_64bits_digest(xxh3_state) != 0xadbcf16d4678d1de ? -1 : 1;
}
if (xxh3_result < 0)
nni->num = CSUM_gone;
return;
}
#endif
#ifdef USE_OPENSSL
if (BITS_SETnUNSET(nni->flags, NNI_EVP, NNI_BUILTIN|NNI_EVP_OK)) {
if (nni->num == prior_num && nni->flags == prior_flags) {
nni->flags = prior_result;
if (!(nni->flags & NNI_EVP))
nni->num = CSUM_gone;
} else {
prior_num = nni->num;
prior_flags = nni->flags;
if (!csum_evp_md(nni))
nni->num = CSUM_gone;
prior_result = nni->flags;
if (check_auth_list && (nni = get_nni_by_num(&valid_auth_checksums, prior_num)) != NULL)
verify_digest(nni, False);
}
}
#endif
}
#endif
void init_checksum_choices()
{
#if defined SUPPORT_XXH3 || defined USE_OPENSSL
struct name_num_item *nni;
#endif
if (initialized_choices)
return;
#if defined USE_OPENSSL && OPENSSL_VERSION_NUMBER < 0x10100000L
OpenSSL_add_all_algorithms();
#endif
#if defined SUPPORT_XXH3 || defined USE_OPENSSL
for (nni = valid_checksums.list; nni->name; nni++)
verify_digest(nni, True);
for (nni = valid_auth_checksums.list; nni->name; nni++)
verify_digest(nni, False);
#endif
initialized_choices = 1;
}

View File

@@ -137,7 +137,7 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
if (DEBUG_GTE(EXIT, 2)) {
rprintf(FINFO,
"[%s] _exit_cleanup(code=%d, file=%s, line=%d): entered\n",
who_am_i(), code, file, line);
who_am_i(), code, src_file(file), line);
}
#include "case_N.h"
@@ -198,7 +198,7 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
switch_step++;
if (cleanup_fname)
do_unlink(cleanup_fname);
do_unlink_at(cleanup_fname);
if (exit_code)
kill_all(SIGUSR1);
if (cleanup_pid && cleanup_pid == getpid()) {
@@ -222,10 +222,6 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
* we don't want to output a duplicate error. */
if ((exit_code && line > 0)
|| am_daemon || (logfile_name && (am_server || !INFO_GTE(STATS, 1)))) {
#ifdef HAVE_USLEEP /* A tiny delay just in case both sender & receiver are sending a msg at the same time. */
if (am_server && exit_code)
usleep(50);
#endif
log_exit(exit_code, exit_file, exit_line);
}
@@ -273,8 +269,16 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
break;
}
if (called_from_signal_handler)
if (called_from_signal_handler) {
#ifdef GCOV_COVERAGE
/* _exit() bypasses the gcov atexit flush; rsync's generator (and
* other processes) normally finish via the signal handler, so
* without this they would write no .gcda. Harmless otherwise. */
extern void __gcov_dump(void);
__gcov_dump();
#endif
_exit(exit_code);
}
exit(exit_code);
}

View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2002-2020 Wayne Davison
* Copyright (C) 2002-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -49,7 +49,7 @@ static char ipaddr_buf[100];
static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len);
static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size);
static int valid_ipaddr(const char *s);
static int valid_ipaddr(const char *s, int allow_scope);
/* Return the IP addr of the client as a string. */
char *client_addr(int fd)
@@ -73,7 +73,7 @@ char *client_addr(int fd)
if ((p = strchr(ipaddr_buf, ' ')) != NULL)
*p = '\0';
}
if (valid_ipaddr(ipaddr_buf))
if (valid_ipaddr(ipaddr_buf, True))
return ipaddr_buf;
}
@@ -139,7 +139,7 @@ char *client_name(const char *ipaddr)
break;
#endif
default:
assert(0);
NOISY_DEATH("Unknown ai_family value");
}
freeaddrinfo(answer);
@@ -156,7 +156,7 @@ char *client_name(const char *ipaddr)
}
/* Try to read an proxy protocol header (V1 or V2). Returns 1 on success or 0 on failure. */
/* Try to read a proxy protocol header (V1 or V2). Returns 1 on success or 0 on failure. */
int read_proxy_protocol_header(int fd)
{
union {
@@ -167,7 +167,7 @@ int read_proxy_protocol_header(int fd)
char sig[PROXY_V2_SIG_SIZE];
char ver_cmd;
char fam;
char len[2];
unsigned char len[2];
union {
struct {
char src_addr[4];
@@ -213,12 +213,14 @@ int read_proxy_protocol_header(int fd)
if (size != sizeof hdr.v2.addr.ip4)
return 0;
inet_ntop(AF_INET, hdr.v2.addr.ip4.src_addr, ipaddr_buf, sizeof ipaddr_buf);
return valid_ipaddr(ipaddr_buf);
return valid_ipaddr(ipaddr_buf, False);
#ifdef INET6
case PROXY_FAM_TCPv6:
if (size != sizeof hdr.v2.addr.ip6)
return 0;
inet_ntop(AF_INET6, hdr.v2.addr.ip6.src_addr, ipaddr_buf, sizeof ipaddr_buf);
return valid_ipaddr(ipaddr_buf);
return valid_ipaddr(ipaddr_buf, False);
#endif
default:
break;
}
@@ -274,7 +276,7 @@ int read_proxy_protocol_header(int fd)
if ((sp = strchr(p, ' ')) == NULL)
return 0;
*sp = '\0';
if (!valid_ipaddr(p))
if (!valid_ipaddr(p, False))
return 0;
strlcpy(ipaddr_buf, p, sizeof ipaddr_buf); /* It will always fit when valid. */
@@ -282,7 +284,7 @@ int read_proxy_protocol_header(int fd)
if ((sp = strchr(p, ' ')) == NULL)
return 0;
*sp = '\0';
if (!valid_ipaddr(p))
if (!valid_ipaddr(p, False))
return 0;
/* Ignore destination address. */
@@ -464,7 +466,7 @@ static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, cha
}
/* Returns 1 for a valid IPv4 or IPv6 addr, or 0 for a bad one. */
static int valid_ipaddr(const char *s)
static int valid_ipaddr(const char *s, int allow_scope)
{
int i;
@@ -482,6 +484,11 @@ static int valid_ipaddr(const char *s)
for (count = 0; count < 8; count++) {
if (!*s)
return saw_double_colon;
if (allow_scope && *s == '%') {
if (saw_double_colon)
break;
return 0;
}
if (strchr(s, ':') == NULL && strchr(s, '.') != NULL) {
if ((!saw_double_colon && count != 6) || (saw_double_colon && count > 6))
@@ -507,8 +514,11 @@ static int valid_ipaddr(const char *s)
}
}
if (!ipv4_at_end)
return !*s;
if (!ipv4_at_end) {
if (allow_scope && *s == '%')
for (s++; isAlNum(s); s++) { }
return !*s && s[-1] != '%';
}
}
/* IPv4 */

View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2001-2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2002-2020 Wayne Davison
* Copyright (C) 2002-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include "rsync.h"
#include "itypes.h"
#include "ifuncs.h"
extern int quiet;
extern int dry_run;
@@ -29,6 +30,7 @@ extern int list_only;
extern int am_sender;
extern int am_server;
extern int am_daemon;
extern int am_chrooted;
extern int am_root;
extern int msgs2stderr;
extern int rsync_port;
@@ -36,8 +38,9 @@ extern int protect_args;
extern int ignore_errors;
extern int preserve_xattrs;
extern int kluge_around_eof;
extern int daemon_over_rsh;
extern int munge_symlinks;
extern int use_secure_symlinks;
extern int open_noatime;
extern int sanitize_paths;
extern int numeric_ids;
extern int filesfrom_fd;
@@ -46,6 +49,7 @@ extern int protocol_version;
extern int io_timeout;
extern int no_detach;
extern int write_batch;
extern int old_style_args;
extern int default_af_hint;
extern int logfile_format_has_i;
extern int logfile_format_has_o_or_i;
@@ -65,11 +69,13 @@ extern uid_t our_uid;
extern gid_t our_gid;
char *auth_user;
char *daemon_auth_choices;
int read_only = 0;
int module_id = -1;
int pid_file_fd = -1;
int early_input_len = 0;
char *early_input = NULL;
pid_t namecvt_pid = 0;
struct chmod_mode_struct *daemon_chmod_modes;
#define EARLY_INPUT_CMD "#early_input="
@@ -84,6 +90,7 @@ unsigned int module_dirlen = 0;
char *full_module_path;
static int rl_nulls = 0;
static int namecvt_fd_req = -1, namecvt_fd_ans = -1;
#ifdef HAVE_SIGACTION
static struct sigaction sigact;
@@ -145,13 +152,9 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[],
static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client)
{
int remote_sub = -1;
#if SUBPROTOCOL_VERSION != 0
int our_sub = protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
#else
int our_sub = 0;
#endif
int our_sub = get_subprotocol_version();
io_printf(f_out, "@RSYNCD: %d.%d\n", protocol_version, our_sub);
output_daemon_greeting(f_out, am_client);
if (!am_client) {
char *motd = lp_motd_file();
if (motd && *motd) {
@@ -183,16 +186,30 @@ static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int
}
if (remote_sub < 0) {
if (remote_protocol == 30) {
if (remote_protocol >= 30) {
if (am_client)
rprintf(FERROR, "rsync: server is speaking an incompatible beta of protocol 30\n");
rprintf(FERROR, "rsync: the server omitted the subprotocol value: %s\n", buf);
else
io_printf(f_out, "@ERROR: your client is speaking an incompatible beta of protocol 30\n");
io_printf(f_out, "@ERROR: your client omitted the subprotocol value: %s\n", buf);
return -1;
}
remote_sub = 0;
}
daemon_auth_choices = strchr(buf + 9, ' ');
if (daemon_auth_choices) {
char *cp;
daemon_auth_choices = strdup(daemon_auth_choices + 1);
if ((cp = strchr(daemon_auth_choices, '\n')) != NULL)
*cp = '\0';
} else if (remote_protocol > 31) {
if (am_client)
rprintf(FERROR, "rsync: the server omitted the digest name list: %s\n", buf);
else
io_printf(f_out, "@ERROR: your client omitted the digest name list: %s\n", buf);
return -1;
}
if (protocol_version > remote_protocol) {
protocol_version = remote_protocol;
if (remote_sub)
@@ -278,10 +295,6 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
fclose(f);
}
/* set daemon_over_rsh to false since we need to build the
* true set of args passed through the rsh/ssh connection;
* this is a no-op for direct-socket-connection mode */
daemon_over_rsh = 0;
server_options(sargs, &sargc);
if (sargc >= MAX_ARGS - 2)
@@ -289,20 +302,45 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
sargs[sargc++] = ".";
if (!old_style_args)
snprintf(line, sizeof line, " %.*s/", modlen, modname);
while (argc > 0) {
if (sargc >= MAX_ARGS - 1) {
arg_overflow:
rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
exit_cleanup(RERR_SYNTAX);
}
if (strncmp(*argv, modname, modlen) == 0
&& argv[0][modlen] == '\0')
if (strncmp(*argv, modname, modlen) == 0 && argv[0][modlen] == '\0')
sargs[sargc++] = modname; /* we send "modname/" */
else if (**argv == '-') {
if (asprintf(sargs + sargc++, "./%s", *argv) < 0)
out_of_memory("start_inband_exchange");
} else
sargs[sargc++] = *argv;
else {
char *arg = *argv;
int extra_chars = *arg == '-' ? 2 : 0; /* a leading dash needs a "./" prefix. */
/* If --old-args was not specified, make sure that the arg won't split at a mod name! */
if (!old_style_args && (p = strstr(arg, line)) != NULL) {
do {
extra_chars += 2;
} while ((p = strstr(p+1, line)) != NULL);
}
if (extra_chars) {
char *f = arg;
char *t = arg = new_array(char, strlen(arg) + extra_chars + 1);
if (*f == '-') {
*t++ = '.';
*t++ = '/';
}
while (*f) {
if (*f == ' ' && strncmp(f, line, modlen+2) == 0) {
*t++ = '[';
*t++ = *f++;
*t++ = ']';
} else
*t++ = *f++;
}
*t = '\0';
}
sargs[sargc++] = arg;
}
argv++;
argc--;
}
@@ -356,7 +394,7 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
if (rl_nulls) {
for (i = 0; i < sargc; i++) {
if (!sargs[i]) /* stop at --protect-args NULL */
if (!sargs[i]) /* stop at --secluded-args NULL */
break;
write_sbuf(f_out, sargs[i]);
write_byte(f_out, 0);
@@ -381,7 +419,7 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
return 0;
}
#ifdef HAVE_PUTENV
#if defined HAVE_SETENV || defined HAVE_PUTENV
static int read_arg_from_pipe(int fd, char *buf, int limit)
{
char *bp = buf, *eob = buf + limit - 1;
@@ -404,27 +442,61 @@ static int read_arg_from_pipe(int fd, char *buf, int limit)
}
#endif
static void set_env_str(const char *var, const char *str)
void set_env_str(const char *var, const char *str)
{
#ifdef HAVE_SETENV
if (setenv(var, str, 1) < 0)
out_of_memory("set_env_str");
#else
#ifdef HAVE_PUTENV
char *mem;
if (asprintf(&mem, "%s=%s", var, str) < 0)
out_of_memory("set_env_str");
putenv(mem);
#else
(void)var;
(void)str;
#endif
#endif
}
#if defined HAVE_SETENV || defined HAVE_PUTENV
static void set_envN_str(const char *var, int num, const char *str)
{
#ifdef HAVE_SETENV
char buf[128];
(void)snprintf(buf, sizeof buf, "%s%d", var, num);
if (setenv(buf, str, 1) < 0)
out_of_memory("set_env_str");
#else
#ifdef HAVE_PUTENV
char *mem;
if (asprintf(&mem, "%s%d=%s", var, num, str) < 0)
out_of_memory("set_envN_str");
putenv(mem);
#endif
#endif
}
void set_env_num(const char *var, long num)
{
#ifdef HAVE_SETENV
char val[64];
(void)snprintf(val, sizeof val, "%ld", num);
if (setenv(var, val, 1) < 0)
out_of_memory("set_env_str");
#else
#ifdef HAVE_PUTENV
char *mem;
if (asprintf(&mem, "%s=%ld", var, num) < 0)
out_of_memory("set_env_num");
putenv(mem);
}
#endif
#endif
}
/* Used for both early exec & pre-xfer exec */
/* Used for "early exec", "pre-xfer exec", and the "name converter" script. */
static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
{
int arg_fds[2], error_fds[2], arg_fd;
@@ -452,15 +524,13 @@ static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
set_env_str("RSYNC_REQUEST", buf);
for (j = 0; ; j++) {
char *p;
len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
if (len <= 0) {
if (!len)
break;
_exit(1);
}
if (asprintf(&p, "RSYNC_ARG%d=%s", j, buf) >= 0)
putenv(p);
set_envN_str("RSYNC_ARG", j, buf);
}
dup2(arg_fd, STDIN_FILENO);
@@ -491,7 +561,9 @@ static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
return pid;
}
static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int am_early)
#endif
static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int exec_type)
{
int j = 0;
@@ -510,10 +582,11 @@ static void write_pre_exec_args(int write_fd, char *request, char **early_argv,
}
write_byte(write_fd, 0);
if (am_early && early_input_len)
if (exec_type == 1 && early_input_len)
write_buf(write_fd, early_input, early_input_len);
close(write_fd);
if (exec_type != 2) /* the name converter needs this left open */
close(write_fd);
}
static char *finish_pre_exec(const char *desc, pid_t pid, int read_fd)
@@ -630,7 +703,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
int set_uid;
char *p, *err_msg = NULL;
char *name = lp_name(i);
int use_chroot = lp_use_chroot(i);
int use_chroot = lp_use_chroot(i); /* might be 1 (yes), 0 (no), or -1 (unset) */
int ret, pre_exec_arg_fd = -1, pre_exec_error_fd = -1;
int save_munge_symlinks;
pid_t pre_exec_pid = 0;
@@ -695,17 +768,17 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
module_id = i;
if (lp_transfer_logging(i) && !logfile_format)
logfile_format = lp_log_format(i);
if (lp_transfer_logging(module_id) && !logfile_format)
logfile_format = lp_log_format(module_id);
if (log_format_has(logfile_format, 'i'))
logfile_format_has_i = 1;
if (logfile_format_has_i || log_format_has(logfile_format, 'o'))
logfile_format_has_o_or_i = 1;
uid = MY_UID();
am_root = (uid == 0);
am_root = (uid == ROOT_UID);
p = *lp_uid(i) ? lp_uid(i) : am_root ? NOBODY_USER : NULL;
p = *lp_uid(module_id) ? lp_uid(module_id) : am_root ? NOBODY_USER : NULL;
if (p) {
if (!user_to_uid(p, &uid, True)) {
rprintf(FLOG, "Invalid uid %s\n", p);
@@ -716,7 +789,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
} else
set_uid = 0;
p = *lp_gid(i) ? conf_strtok(lp_gid(i)) : NULL;
p = *lp_gid(module_id) ? conf_strtok(lp_gid(module_id)) : NULL;
if (p) {
/* The "*" gid must be the first item in the list. */
if (strcmp(p, "*") == 0) {
@@ -749,12 +822,26 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
return -1;
}
module_dir = lp_path(i);
module_dir = lp_path(module_id);
if (*module_dir == '\0') {
rprintf(FLOG, "No path specified for module %s\n", name);
io_printf(f_out, "@ERROR: no path setting.\n");
return -1;
}
if (use_chroot < 0) {
if (strstr(module_dir, "/./") != NULL)
use_chroot = 1; /* The module is expecting a chroot inner & outer path. */
else if (chroot("/") < 0) {
rprintf(FLOG, "chroot test failed: %s. "
"Switching 'use chroot' from unset to false.\n",
strerror(errno));
use_chroot = 0;
} else {
if (chdir("/") < 0)
rsyserr(FLOG, errno, "chdir(\"/\") failed");
use_chroot = 1;
}
}
if (use_chroot) {
if ((p = strstr(module_dir, "/./")) != NULL) {
*p = '\0'; /* Temporary... */
@@ -786,38 +873,39 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
} else
set_filter_dir(module_dir, module_dirlen);
p = lp_filter(i);
p = lp_filter(module_id);
parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3);
p = lp_include_from(i);
p = lp_include_from(module_id);
parse_filter_file(&daemon_filter_list, p, rule_template(FILTRULE_INCLUDE),
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
p = lp_include(i);
p = lp_include(module_id);
parse_filter_str(&daemon_filter_list, p,
rule_template(FILTRULE_INCLUDE | FILTRULE_WORD_SPLIT),
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
p = lp_exclude_from(i);
p = lp_exclude_from(module_id);
parse_filter_file(&daemon_filter_list, p, rule_template(0),
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES | XFLG_FATAL_ERRORS);
p = lp_exclude(i);
p = lp_exclude(module_id);
parse_filter_str(&daemon_filter_list, p, rule_template(FILTRULE_WORD_SPLIT),
XFLG_ABS_IF_SLASH | XFLG_DIR2WILD3 | XFLG_OLD_PREFIXES);
log_init(1);
#ifdef HAVE_PUTENV
if ((*lp_early_exec(i) || *lp_prexfer_exec(i) || *lp_postxfer_exec(i))
#if defined HAVE_SETENV || defined HAVE_PUTENV
if ((*lp_early_exec(module_id) || *lp_prexfer_exec(module_id)
|| *lp_postxfer_exec(module_id) || *lp_name_converter(module_id))
&& !getenv("RSYNC_NO_XFER_EXEC")) {
set_env_num("RSYNC_PID", (long)getpid());
/* For post-xfer exec, fork a new process to run the rsync
* daemon while this process waits for the exit status and
* runs the indicated command at that point. */
if (*lp_postxfer_exec(i)) {
if (*lp_postxfer_exec(module_id)) {
pid_t pid = fork();
if (pid < 0) {
rsyserr(FLOG, errno, "fork failed");
@@ -837,7 +925,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
else
status = -1;
set_env_num("RSYNC_EXIT_STATUS", status);
if (shell_exec(lp_postxfer_exec(i)) < 0)
if (shell_exec(lp_postxfer_exec(module_id)) < 0)
status = -1;
_exit(status);
}
@@ -845,9 +933,9 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
/* For early exec, fork a child process to run the indicated
* command and wait for it to exit. */
if (*lp_early_exec(i)) {
if (*lp_early_exec(module_id)) {
int arg_fd;
pid_t pid = start_pre_exec(lp_early_exec(i), &arg_fd, NULL);
pid_t pid = start_pre_exec(lp_early_exec(module_id), &arg_fd, NULL);
if (pid == (pid_t)-1) {
rsyserr(FLOG, errno, "early exec preparation failed");
io_printf(f_out, "@ERROR: early exec preparation failed\n");
@@ -864,14 +952,23 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
/* For pre-xfer exec, fork a child process to run the indicated
* command, though it first waits for the parent process to
* send us the user's request via a pipe. */
if (*lp_prexfer_exec(i)) {
pre_exec_pid = start_pre_exec(lp_prexfer_exec(i), &pre_exec_arg_fd, &pre_exec_error_fd);
if (*lp_prexfer_exec(module_id)) {
pre_exec_pid = start_pre_exec(lp_prexfer_exec(module_id), &pre_exec_arg_fd, &pre_exec_error_fd);
if (pre_exec_pid == (pid_t)-1) {
rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
return -1;
}
}
if (*lp_name_converter(module_id)) {
namecvt_pid = start_pre_exec(lp_name_converter(module_id), &namecvt_fd_req, &namecvt_fd_ans);
if (namecvt_pid == (pid_t)-1) {
rsyserr(FLOG, errno, "name-converter exec preparation failed");
io_printf(f_out, "@ERROR: name-converter exec preparation failed\n");
return -1;
}
}
}
#endif
@@ -881,32 +978,23 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
}
if (use_chroot) {
/*
* XXX: The 'use chroot' flag is a fairly reliable
* source of confusion, because it fails under two
* important circumstances: running as non-root,
* running on Win32 (or possibly others). On the
* other hand, if you are running as root, then it
* might be better to always use chroot.
*
* So, perhaps if we can't chroot we should just issue
* a warning, unless a "require chroot" flag is set,
* in which case we fail.
*/
/* Cache timezone data before chroot makes /etc/localtime inaccessible */
tzset();
if (chroot(module_chdir)) {
rsyserr(FLOG, errno, "chroot %s failed", module_chdir);
rsyserr(FLOG, errno, "chroot(\"%s\") failed", module_chdir);
io_printf(f_out, "@ERROR: chroot failed\n");
return -1;
}
am_chrooted = 1;
module_chdir = module_dir;
}
if (!change_dir(module_chdir, CD_NORMAL))
return path_failure(f_out, module_chdir, True);
if (module_dirlen || (!use_chroot && !*lp_daemon_chroot()))
if (module_dirlen)
sanitize_paths = 1;
if ((munge_symlinks = lp_munge_symlinks(i)) < 0)
if ((munge_symlinks = lp_munge_symlinks(module_id)) < 0)
munge_symlinks = !use_chroot || module_dirlen;
if (munge_symlinks) {
STRUCT_STAT st;
@@ -920,6 +1008,15 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
}
}
/* Enable secure symlink handling for any non-chrooted daemon module.
* This prevents TOCTOU race attacks where an attacker could switch a
* directory to a symlink between path validation and file open.
* Match the gate used by the do_*_at() wrappers in syscall.c
* (am_daemon && !am_chrooted) -- the protection has nothing to do
* with symlink munging, so a module configured with
* "munge symlinks = false" must still get the secure-open path. */
use_secure_symlinks = am_daemon && !am_chrooted;
if (gid_list.count) {
gid_t *gid_array = gid_list.items;
if (setgid(gid_array[0])) {
@@ -958,11 +1055,11 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
}
our_uid = MY_UID();
am_root = (our_uid == 0);
am_root = (our_uid == ROOT_UID);
}
if (lp_temp_dir(i) && *lp_temp_dir(i)) {
tmpdir = lp_temp_dir(i);
if (lp_temp_dir(module_id) && *lp_temp_dir(module_id)) {
tmpdir = lp_temp_dir(module_id);
if (strlen(tmpdir) >= MAXPATHLEN - 10) {
rprintf(FLOG,
"the 'temp dir' value for %s is WAY too long -- ignoring.\n",
@@ -973,7 +1070,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
io_printf(f_out, "@RSYNCD: OK\n");
read_args(f_in, name, line, sizeof line, rl_nulls, &argv, &argc, &request);
read_args(f_in, name, line, sizeof line, rl_nulls, 1, &argv, &argc, &request);
orig_argv = argv;
save_munge_symlinks = munge_symlinks;
@@ -983,13 +1080,18 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
if (protect_args && ret) {
orig_early_argv = orig_argv;
protect_args = 2;
read_args(f_in, name, line, sizeof line, 1, &argv, &argc, &request);
read_args(f_in, name, line, sizeof line, 1, 0, &argv, &argc, &request);
orig_argv = argv;
ret = parse_arguments(&argc, (const char ***) &argv);
} else
orig_early_argv = NULL;
/* The default is to use the user's setting unless the module sets True or False. */
if (lp_open_noatime(module_id) >= 0)
open_noatime = lp_open_noatime(module_id);
munge_symlinks = save_munge_symlinks; /* The client mustn't control this. */
if (am_daemon > 0)
msgs2stderr = 0; /* A non-rsh-run daemon doesn't have stderr for msgs. */
@@ -998,6 +1100,9 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
err_msg = finish_pre_exec("pre-xfer exec", pre_exec_pid, pre_exec_error_fd);
}
if (namecvt_pid)
write_pre_exec_args(namecvt_fd_req, request, orig_early_argv, orig_argv, 2);
if (orig_early_argv)
free(orig_early_argv);
@@ -1008,7 +1113,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
if (write_batch < 0)
dry_run = 1;
if (lp_fake_super(i)) {
if (lp_fake_super(module_id)) {
if (preserve_xattrs > 1)
preserve_xattrs = 1;
am_root = -1;
@@ -1033,7 +1138,7 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
#ifndef DEBUG
/* don't allow the logs to be flooded too fast */
limit_output_verbosity(lp_max_verbosity(i));
limit_output_verbosity(lp_max_verbosity(module_id));
#endif
if (protocol_version < 23 && (protocol_version == 22 || am_sender))
@@ -1094,20 +1199,21 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
#endif
if (!numeric_ids
&& (use_chroot ? lp_numeric_ids(i) != False : lp_numeric_ids(i) == True))
&& (use_chroot ? lp_numeric_ids(module_id) != False && !*lp_name_converter(module_id)
: lp_numeric_ids(module_id) == True))
numeric_ids = -1; /* Set --numeric-ids w/o breaking protocol. */
if (lp_timeout(i) && (!io_timeout || lp_timeout(i) < io_timeout))
set_io_timeout(lp_timeout(i));
if (lp_timeout(module_id) && (!io_timeout || lp_timeout(module_id) < io_timeout))
set_io_timeout(lp_timeout(module_id));
/* If we have some incoming/outgoing chmod changes, append them to
* any user-specified changes (making our changes have priority).
* We also get a pointer to just our changes so that a receiver
* process can use them separately if --perms wasn't specified. */
if (am_sender)
p = lp_outgoing_chmod(i);
p = lp_outgoing_chmod(module_id);
else
p = lp_incoming_chmod(i);
p = lp_incoming_chmod(module_id);
if (*p && !(daemon_chmod_modes = parse_chmod(p, &chmod_modes))) {
rprintf(FLOG, "Invalid \"%sing chmod\" directive: %s\n",
am_sender ? "outgo" : "incom", p);
@@ -1118,6 +1224,38 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
return 0;
}
BOOL namecvt_call(const char *cmd, const char **name_p, id_t *id_p)
{
char buf[1024];
int got, len;
if (*name_p)
len = snprintf(buf, sizeof buf, "%s %s\n", cmd, *name_p);
else
len = snprintf(buf, sizeof buf, "%s %ld\n", cmd, (long)*id_p);
if (len >= (int)sizeof buf) {
rprintf(FERROR, "namecvt_call() request was too large.\n");
exit_cleanup(RERR_UNSUPPORTED);
}
while ((got = write(namecvt_fd_req, buf, len)) != len) {
if (got < 0 && errno == EINTR)
continue;
rprintf(FERROR, "Connection to name-converter failed.\n");
exit_cleanup(RERR_SOCKETIO);
}
if (!read_line_old(namecvt_fd_ans, buf, sizeof buf, 0))
return False;
if (*name_p)
*id_p = (id_t)atol(buf);
else
*name_p = strdup(buf);
return True;
}
/* send a list of available modules to the client. Don't list those
with "list = False". */
static void send_listing(int fd)
@@ -1174,11 +1312,51 @@ int start_daemon(int f_in, int f_out)
if (lp_proxy_protocol() && !read_proxy_protocol_header(f_in))
return -1;
/* Do reverse DNS lookup before chroot/setuid. The result is cached,
* so the later client_name() call will use this cached value. This
* ensures hostname-based ACLs work even when DNS is unavailable
* after chroot.
*
* "reverse lookup" can be set globally OR per-module, so we also
* scan each module: a deployment with "reverse lookup = no" in the
* global section but "reverse lookup = yes" in a specific module
* still triggers a post-chroot lookup at access-check time
* (rsync_module() in this file), which would also fail in the
* chroot and turn hostname-based deny rules into silent bypasses. */
{
int need_reverse = lp_reverse_lookup(-1);
int j, num_modules = lp_num_modules();
for (j = 0; !need_reverse && j < num_modules; j++) {
if (lp_reverse_lookup(j))
need_reverse = 1;
}
if (need_reverse)
(void)client_name(client_addr(f_in));
}
p = lp_daemon_chroot();
if (*p) {
log_init(0); /* Make use we've initialized syslog before chrooting. */
if (chroot(p) < 0 || chdir("/") < 0) {
rsyserr(FLOG, errno, "daemon chroot %s failed", p);
tzset();
if (chroot(p) < 0) {
rsyserr(FLOG, errno, "daemon chroot(\"%s\") failed", p);
return -1;
}
/* Deliberately do NOT set am_chrooted here. am_chrooted
* gates the per-module symlink-race defenses
* (secure_relative_open() and the do_*_at() wrappers in
* syscall.c) and means "the kernel is enforcing path
* confinement at the module boundary". The daemon chroot
* confines path resolution to the daemon-chroot directory,
* not to any individual module path -- modules sharing the
* daemon chroot are still distinguishable filesystem
* subtrees and a sender-controlled symlink in module A
* could redirect a syscall to module B (or to other files
* inside the daemon chroot) without the per-module
* defenses. Leave am_chrooted=0 here so secure_relative_open()
* still fires for "use chroot = no" modules. */
if (chdir("/") < 0) {
rsyserr(FLOG, errno, "daemon chdir(\"/\") failed");
return -1;
}
}
@@ -1207,7 +1385,7 @@ int start_daemon(int f_in, int f_out)
return -1;
}
our_uid = MY_UID();
am_root = (our_uid == 0);
am_root = (our_uid == ROOT_UID);
}
addr = client_addr(f_in);
@@ -1400,7 +1578,7 @@ int daemon_main(void)
log_init(0);
rprintf(FLOG, "rsyncd version %s starting, listening on port %d\n",
RSYNC_VERSION, rsync_port);
rsync_version(), rsync_port);
/* TODO: If listening on a particular address, then show that
* address too. In fact, why not just do getnameinfo on the
* local address??? */

View File

260
compat.c
View File

@@ -3,7 +3,7 @@
*
* Copyright (C) Andrew Tridgell 1996
* Copyright (C) Paul Mackerras 1996
* Copyright (C) 2004-2020 Wayne Davison
* Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include "rsync.h"
#include "itypes.h"
#include "ifuncs.h"
extern int am_server;
extern int am_sender;
@@ -43,6 +44,7 @@ extern int protect_args;
extern int preserve_uid;
extern int preserve_gid;
extern int preserve_atimes;
extern int preserve_crtimes;
extern int preserve_acls;
extern int preserve_xattrs;
extern int xfer_flags_as_varint;
@@ -50,19 +52,25 @@ extern int need_messages_from_generator;
extern int delete_mode, delete_before, delete_during, delete_after;
extern int do_compression;
extern int do_compression_level;
extern int do_compression_threads;
extern int saw_stderr_opt;
extern int msgs2stderr;
extern char *shell_cmd;
extern char *partial_dir;
extern char *files_from;
extern char *filesfrom_host;
extern const char *checksum_choice;
extern const char *compress_choice;
extern char *daemon_auth_choices;
extern filter_rule_list filter_list;
extern int need_unsorted_flist;
#ifdef ICONV_OPTION
extern iconv_t ic_send, ic_recv;
extern char *iconv_opt;
#endif
extern struct name_num_obj valid_checksums;
extern struct name_num_obj valid_checksums, valid_auth_checksums;
extern struct name_num_item *xfer_sum_nni;
int remote_protocol = 0;
int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
@@ -73,9 +81,13 @@ int want_xattr_optim = 0;
int proper_seed_order = 0;
int inplace_partial = 0;
int do_negotiated_strings = 0;
int xmit_id0_names = 0;
struct name_num_item *xattr_sum_nni;
int xattr_sum_len = 0;
/* These index values are for the file-list's extra-attribute array. */
int pathname_ndx, depth_ndx, atimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
int sender_symlink_iconv = 0; /* sender should convert symlink content */
@@ -86,19 +98,21 @@ int filesfrom_convert = 0;
#define MAX_NSTR_STRLEN 256
struct name_num_obj valid_compressions = {
"compress", NULL, NULL, 0, 0, {
struct name_num_item valid_compressions_items[] = {
#ifdef SUPPORT_ZSTD
{ CPRES_ZSTD, "zstd", NULL },
{ CPRES_ZSTD, 0, "zstd", NULL },
#endif
#ifdef SUPPORT_LZ4
{ CPRES_LZ4, "lz4", NULL },
{ CPRES_LZ4, 0, "lz4", NULL },
#endif
{ CPRES_ZLIBX, "zlibx", NULL },
{ CPRES_ZLIB, "zlib", NULL },
{ CPRES_NONE, "none", NULL },
{ 0, NULL, NULL }
}
{ CPRES_ZLIBX, 0, "zlibx", NULL },
{ CPRES_ZLIB, 0, "zlib", NULL },
{ CPRES_NONE, 0, "none", NULL },
{ 0, 0, NULL, NULL }
};
struct name_num_obj valid_compressions = {
"compress", NULL, 0, 0, valid_compressions_items
};
#define CF_INC_RECURSE (1<<0)
@@ -109,6 +123,7 @@ struct name_num_obj valid_compressions = {
#define CF_CHKSUM_SEED_FIX (1<<5)
#define CF_INPLACE_PARTIAL_DIR (1<<6)
#define CF_VARINT_FLIST_FLAGS (1<<7)
#define CF_ID0_NAMES (1<<8)
static const char *client_info;
@@ -117,13 +132,9 @@ static const char *client_info;
* of that protocol for it to be advertised as available. */
static void check_sub_protocol(void)
{
char *dot;
const char *dot;
int their_protocol, their_sub;
#if SUBPROTOCOL_VERSION != 0
int our_sub = protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
#else
int our_sub = 0;
#endif
int our_sub = get_subprotocol_version();
/* client_info starts with VER.SUB string if client is a pre-release. */
if (!(their_protocol = atoi(client_info))
@@ -150,7 +161,13 @@ static void check_sub_protocol(void)
void set_allow_inc_recurse(void)
{
client_info = shell_cmd ? shell_cmd : "";
if (!local_server)
client_info = shell_cmd ? shell_cmd : "";
else if (am_server) {
char buf[64];
maybe_add_e_option(buf, sizeof buf);
client_info = *buf ? strdup(buf+1) : ""; /* The +1 skips the leading "e". */
}
if (!recurse || use_qsort)
allow_inc_recurse = 0;
@@ -158,15 +175,14 @@ void set_allow_inc_recurse(void)
&& (delete_before || delete_after
|| delay_updates || prune_empty_dirs))
allow_inc_recurse = 0;
else if (am_server && !local_server
&& (strchr(client_info, 'i') == NULL))
else if (am_server && strchr(client_info, 'i') == NULL)
allow_inc_recurse = 0;
}
void parse_compress_choice(int final_call)
{
if (valid_compressions.negotiated_name)
do_compression = valid_compressions.negotiated_num;
if (valid_compressions.negotiated_nni)
do_compression = valid_compressions.negotiated_nni->num;
else if (compress_choice) {
struct name_num_item *nni = get_nni_by_name(&valid_compressions, compress_choice, -1);
if (!nni) {
@@ -188,8 +204,8 @@ void parse_compress_choice(int final_call)
compress_choice = NULL;
/* Snag the compression name for both write_batch's option output & the following debug output. */
if (valid_compressions.negotiated_name)
compress_choice = valid_compressions.negotiated_name;
if (valid_compressions.negotiated_nni)
compress_choice = valid_compressions.negotiated_nni->name;
else if (compress_choice == NULL) {
struct name_num_item *nni = get_nni_by_num(&valid_compressions, do_compression);
compress_choice = nni ? nni->name : "UNKNOWN";
@@ -199,7 +215,7 @@ void parse_compress_choice(int final_call)
&& (do_compression != CPRES_NONE || do_compression_level != CLVL_NOT_SPECIFIED)) {
rprintf(FINFO, "%s%s compress: %s (level %d)\n",
am_server ? "Server" : "Client",
valid_compressions.negotiated_name ? " negotiated" : "",
valid_compressions.negotiated_nni ? " negotiated" : "",
compress_choice, do_compression_level);
}
}
@@ -212,6 +228,8 @@ struct name_num_item *get_nni_by_name(struct name_num_obj *nno, const char *name
len = strlen(name);
for (nni = nno->list; nni->name; nni++) {
if (nni->num == CSUM_gone)
continue;
if (strncasecmp(name, nni->name, len) == 0 && nni->name[len] == '\0')
return nni;
}
@@ -246,10 +264,12 @@ static void init_nno_saw(struct name_num_obj *nno, int val)
if (!nno->saw) {
nno->saw = new_array0(uchar, nno->saw_len);
/* We'll take this opportunity to make sure that the main_name values are set right. */
/* We'll take this opportunity to set the main_nni values for duplicates. */
for (cnt = 1, nni = nno->list; nni->name; nni++, cnt++) {
if (nni->num == CSUM_gone)
continue;
if (nno->saw[nni->num])
nni->main_name = nno->list[nno->saw[nni->num]-1].name;
nni->main_nni = &nno->list[nno->saw[nni->num]-1];
else
nno->saw[nni->num] = cnt;
}
@@ -275,8 +295,8 @@ static int parse_nni_str(struct name_num_obj *nno, const char *from, char *tobuf
struct name_num_item *nni = get_nni_by_name(nno, tok, to - tok);
if (nni && !nno->saw[nni->num]) {
nno->saw[nni->num] = ++cnt;
if (nni->main_name) {
to = tok + strlcpy(tok, nni->main_name, tobuf_len - (tok - tobuf));
if (nni->main_nni) {
to = tok + strlcpy(tok, nni->main_nni->name, tobuf_len - (tok - tobuf));
if (to - tobuf >= tobuf_len) {
to = tok - 1;
break;
@@ -310,13 +330,44 @@ static int parse_nni_str(struct name_num_obj *nno, const char *from, char *tobuf
return to - tobuf;
}
static int parse_negotiate_str(struct name_num_obj *nno, char *tmpbuf)
{
struct name_num_item *nni, *ret = NULL;
int best = nno->saw_len; /* We want best == 1 from the client list, so start with a big number. */
char *space, *tok = tmpbuf;
while (tok) {
while (*tok == ' ') tok++; /* Should be unneeded... */
if (!*tok)
break;
if ((space = strchr(tok, ' ')) != NULL)
*space = '\0';
nni = get_nni_by_name(nno, tok, -1);
if (space) {
*space = ' ';
tok = space + 1;
} else
tok = NULL;
if (!nni || !nno->saw[nni->num] || best <= nno->saw[nni->num])
continue;
ret = nni;
best = nno->saw[nni->num];
if (best == 1 || am_server) /* The server side stops at the first acceptable client choice */
break;
}
if (ret) {
free(nno->saw);
nno->saw = NULL;
nno->negotiated_nni = ret->main_nni ? ret->main_nni : ret;
return 1;
}
return 0;
}
/* This routine is always called with a tmpbuf of MAX_NSTR_STRLEN length, but the
* buffer may be pre-populated with a "len" length string to use OR a len of -1
* to tell us to read a string from the fd. */
static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf, int len)
{
struct name_num_item *ret = NULL;
if (len < 0)
len = read_vstring(f_in, tmpbuf, MAX_NSTR_STRLEN);
@@ -327,37 +378,8 @@ static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf,
rprintf(FINFO, "Server %s list (on client): %s\n", nno->type, tmpbuf);
}
if (len > 0) {
struct name_num_item *nni;
int best = nno->saw_len; /* We want best == 1 from the client list, so start with a big number. */
char *space, *tok = tmpbuf;
while (tok) {
while (*tok == ' ') tok++; /* Should be unneeded... */
if (!*tok)
break;
if ((space = strchr(tok, ' ')) != NULL)
*space = '\0';
nni = get_nni_by_name(nno, tok, -1);
if (space) {
*space = ' ';
tok = space + 1;
} else
tok = NULL;
if (!nni || !nno->saw[nni->num] || best <= nno->saw[nni->num])
continue;
ret = nni;
best = nno->saw[nni->num];
if (best == 1 || am_server) /* The server side stops at the first acceptable client choice */
break;
}
if (ret) {
free(nno->saw);
nno->saw = NULL;
nno->negotiated_name = ret->main_name ? ret->main_name : ret->name;
nno->negotiated_num = ret->num;
return;
}
}
if (len > 0 && parse_negotiate_str(nno, tmpbuf))
return;
if (!am_server || !do_negotiated_strings) {
char *cp = tmpbuf;
@@ -389,11 +411,11 @@ static const char *getenv_nstr(int ntype)
const char *env_str = getenv(ntype == NSTR_COMPRESS ? "RSYNC_COMPRESS_LIST" : "RSYNC_CHECKSUM_LIST");
/* When writing a batch file, we always negotiate an old-style choice. */
if (write_batch)
if (write_batch)
env_str = ntype == NSTR_COMPRESS ? "zlib" : protocol_version >= 30 ? "md5" : "md4";
if (am_server && env_str) {
char *cp = strchr(env_str, '&');
const char *cp = strchr(env_str, '&');
if (cp)
env_str = cp + 1;
}
@@ -422,7 +444,7 @@ void validate_choice_vs_env(int ntype, int num1, int num2)
nno->saw[CSUM_MD4_ARCHAIC] = nno->saw[CSUM_MD4_BUSTED] = nno->saw[CSUM_MD4_OLD] = nno->saw[CSUM_MD4];
if (!nno->saw[num1] || (num2 >= 0 && !nno->saw[num2])) {
rprintf(FERROR, "Your --%s-choice value (%s) was refused by the server.\n",
rprintf(FERROR, "Your --%s-choice value (%s) was refused by the server.\n",
ntype == NSTR_COMPRESS ? "compress" : "checksum",
ntype == NSTR_COMPRESS ? compress_choice : checksum_choice);
exit_cleanup(RERR_UNSUPPORTED);
@@ -453,8 +475,10 @@ int get_default_nno_list(struct name_num_obj *nno, char *to_buf, int to_buf_len,
init_nno_saw(nno, 0);
for (nni = nno->list, len = 0; nni->name; nni++) {
if (nni->main_name) {
if (!dup_markup)
if (nni->num == CSUM_gone)
continue;
if (nni->main_nni) {
if (!dup_markup || nni->main_nni->num == CSUM_gone)
continue;
delim = dup_markup;
}
@@ -512,6 +536,8 @@ static void negotiate_the_strings(int f_in, int f_out)
{
/* We send all the negotiation strings before we start to read them to help avoid a slow startup. */
init_checksum_choices();
if (!checksum_choice)
send_negotiate_str(f_out, &valid_checksums, NSTR_CHECKSUM);
@@ -541,7 +567,7 @@ static void negotiate_the_strings(int f_in, int f_out)
/* If the other side is too old to negotiate, the above steps just made sure that
* the env didn't disallow the old algorithm. Mark things as non-negotiated. */
if (!do_negotiated_strings)
valid_checksums.negotiated_name = valid_compressions.negotiated_name = NULL;
valid_checksums.negotiated_nni = valid_compressions.negotiated_nni = NULL;
}
void setup_protocol(int f_out,int f_in)
@@ -553,7 +579,9 @@ void setup_protocol(int f_out,int f_in)
* aligned for direct int64-pointer memory access. */
if (preserve_atimes)
atimes_ndx = (file_extra_cnt += EXTRA64_CNT);
if (am_sender) /* This is most likely in the in64 union as well. */
if (preserve_crtimes)
crtimes_ndx = (file_extra_cnt += EXTRA64_CNT);
if (am_sender) /* This is most likely in the file_extras64 union as well. */
pathname_ndx = (file_extra_cnt += PTR_EXTRA_CNT);
else
depth_ndx = ++file_extra_cnt;
@@ -591,7 +619,7 @@ void setup_protocol(int f_out,int f_in)
if (remote_protocol < MIN_PROTOCOL_VERSION
|| remote_protocol > MAX_PROTOCOL_VERSION) {
rprintf(FERROR,"protocol version mismatch -- is your shell clean?\n");
rprintf(FERROR,"(see the rsync man page for an explanation)\n");
rprintf(FERROR,"(see the rsync manpage for an explanation)\n");
exit_cleanup(RERR_PROTOCOL);
}
if (remote_protocol < OLD_PROTOCOL_VERSION) {
@@ -611,6 +639,9 @@ void setup_protocol(int f_out,int f_in)
if (read_batch)
check_batch_flags();
if (!saw_stderr_opt && protocol_version <= 28 && am_server)
msgs2stderr = 0; /* The client side may not have stderr setup for us. */
#ifndef SUPPORT_PREALLOCATION
if (preallocate_files && !am_sender) {
rprintf(FERROR, "preallocation is not supported on this %s\n",
@@ -686,15 +717,17 @@ void setup_protocol(int f_out,int f_in)
#ifdef ICONV_OPTION
compat_flags |= CF_SYMLINK_ICONV;
#endif
if (local_server || strchr(client_info, 'f') != NULL)
if (strchr(client_info, 'f') != NULL)
compat_flags |= CF_SAFE_FLIST;
if (local_server || strchr(client_info, 'x') != NULL)
if (strchr(client_info, 'x') != NULL)
compat_flags |= CF_AVOID_XATTR_OPTIM;
if (local_server || strchr(client_info, 'C') != NULL)
if (strchr(client_info, 'C') != NULL)
compat_flags |= CF_CHKSUM_SEED_FIX;
if (local_server || strchr(client_info, 'I') != NULL)
if (strchr(client_info, 'I') != NULL)
compat_flags |= CF_INPLACE_PARTIAL_DIR;
if (local_server || strchr(client_info, 'v') != NULL) {
if (strchr(client_info, 'u') != NULL)
compat_flags |= CF_ID0_NAMES;
if (strchr(client_info, 'v') != NULL) {
do_negotiated_strings = 1;
compat_flags |= CF_VARINT_FLIST_FLAGS;
}
@@ -714,6 +747,11 @@ void setup_protocol(int f_out,int f_in)
want_xattr_optim = protocol_version >= 31 && !(compat_flags & CF_AVOID_XATTR_OPTIM);
proper_seed_order = compat_flags & CF_CHKSUM_SEED_FIX ? 1 : 0;
xfer_flags_as_varint = compat_flags & CF_VARINT_FLIST_FLAGS ? 1 : 0;
xmit_id0_names = compat_flags & CF_ID0_NAMES ? 1 : 0;
if (!xfer_flags_as_varint && preserve_crtimes) {
fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --crtimes.\n");
exit_cleanup(RERR_PROTOCOL);
}
if (am_sender) {
receiver_symlink_times = am_server
? strchr(client_info, 'L') != NULL
@@ -725,7 +763,7 @@ void setup_protocol(int f_out,int f_in)
#endif
#ifdef ICONV_OPTION
sender_symlink_iconv = iconv_opt && (am_server
? local_server || strchr(client_info, 's') != NULL
? strchr(client_info, 's') != NULL
: !!(compat_flags & CF_SYMLINK_ICONV));
#endif
if (inc_recurse && !allow_inc_recurse) {
@@ -778,11 +816,77 @@ void setup_protocol(int f_out,int f_in)
checksum_seed = read_int(f_in);
}
parse_checksum_choice(1); /* Sets checksum_type & xfersum_type */
parse_checksum_choice(1); /* Sets file_sum_nni & xfer_sum_nni */
parse_compress_choice(1); /* Sets do_compression */
/* TODO in the future allow this algorithm to be chosen somehow, but it can't get too
* long or the size starts to cause a problem in the xattr abbrev/non-abbrev code. */
xattr_sum_nni = parse_csum_name(NULL, 0);
xattr_sum_len = csum_len_for_type(xattr_sum_nni->num, 0);
if (write_batch && !am_server)
write_batch_shell_file();
init_flist();
}
void output_daemon_greeting(int f_out, int am_client)
{
char tmpbuf[MAX_NSTR_STRLEN];
int our_sub = get_subprotocol_version();
init_checksum_choices();
get_default_nno_list(&valid_auth_checksums, tmpbuf, MAX_NSTR_STRLEN, '\0');
io_printf(f_out, "@RSYNCD: %d.%d %s\n", protocol_version, our_sub, tmpbuf);
if (am_client && DEBUG_GTE(NSTR, 2))
rprintf(FINFO, "Client %s list (on client): %s\n", valid_auth_checksums.type, tmpbuf);
}
void negotiate_daemon_auth(int f_out, int am_client)
{
char tmpbuf[MAX_NSTR_STRLEN];
int save_am_server = am_server;
int md4_is_old = 0;
if (!am_client)
am_server = 1;
if (daemon_auth_choices)
strlcpy(tmpbuf, daemon_auth_choices, MAX_NSTR_STRLEN);
else {
strlcpy(tmpbuf, protocol_version >= 30 ? "md5" : "md4", MAX_NSTR_STRLEN);
md4_is_old = 1;
}
if (am_client) {
recv_negotiate_str(-1, &valid_auth_checksums, tmpbuf, strlen(tmpbuf));
if (DEBUG_GTE(NSTR, 1)) {
rprintf(FINFO, "Client negotiated %s: %s\n", valid_auth_checksums.type,
valid_auth_checksums.negotiated_nni->name);
}
} else {
if (!parse_negotiate_str(&valid_auth_checksums, tmpbuf)) {
get_default_nno_list(&valid_auth_checksums, tmpbuf, MAX_NSTR_STRLEN, '\0');
io_printf(f_out, "@ERROR: your client does not support one of our daemon-auth checksums: %s\n",
tmpbuf);
exit_cleanup(RERR_UNSUPPORTED);
}
}
am_server = save_am_server;
if (md4_is_old && valid_auth_checksums.negotiated_nni->num == CSUM_MD4) {
valid_auth_checksums.negotiated_nni->num = CSUM_MD4_OLD;
valid_auth_checksums.negotiated_nni->flags = 0;
}
}
int get_subprotocol_version()
{
#if SUBPROTOCOL_VERSION != 0
return protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;
#else
return 0;
#endif
}

1210
config.guess vendored
View File

File diff suppressed because it is too large Load Diff

676
config.sub vendored
View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -7,39 +7,54 @@ basically a summary of clientserver.c and authenticate.c.
This is the protocol used for rsync --daemon; i.e. connections to port
873 rather than invocations over a remote shell.
When the server accepts a connection, it prints a greeting
When the server accepts a connection, it prints a newline-terminated
greeting line:
@RSYNCD: <version>.<subprotocol>
@RSYNCD: <version>.<subprotocol> <digest1> <digestN>
where <version> is the numeric version (see PROTOCOL_VERSION in rsync.h)
'.' is a literal period, and <subprotocol> is the numeric subprotocol
version (see SUBPROTOCOL_VERSION -- it will be 0 for final releases).
Protocols prior to 30 only output <version> alone. The daemon expects
to see a similar greeting back from the client. For protocols prior to
30, an absent ".<subprotocol>" value is assumed to be 0. For protocol
30, an absent value is a fatal error. The daemon then follows this line
with a free-format text message-of-the-day (if any is defined).
The <version> is the numeric version (see PROTOCOL_VERSION in rsync.h)
The <subprotocol> is the numeric subprotocol version (which is 0 for a
final protocol version, as the SUBPROTOCOL_VERSION define discusses).
The <digestN> names are the authentication digest algorithms that the
daemon supports, listed in order of preference.
An rsync prior to 3.2.7 omits the digest names. An rsync prior to 3.0.0
also omits the period and the <subprotocol> value. Since a final
protocol has a subprotocol value of 0, a missing subprotocol value is
assumed to be 0 for any protocol prior to 30. It is considered a fatal
error for protocol 30 and above to omit it. It is considered a fatal
error for protocol 32 and above to omit the digest name list (currently
31 is the newest protocol).
The daemon expects to see a similar greeting line back from the client.
Once received, the daemon follows the opening line with a free-format
text message-of-the-day (if any is defined).
The server is now in the connected state. The client can either send
the command
the command:
#list
to get a listing of modules, or the name of a module. After this, the
(to get a listing of modules) or the name of a module. After this, the
connection is now bound to a particular module. Access per host for
this module is now checked, as is per-module connection limits.
If authentication is required to use this module, the server will say
If authentication is required to use this module, the server will say:
@RSYNCD: AUTHREQD <challenge>
where <challenge> is a random string of base64 characters. The client
must respond with
must respond with:
<user> <response>
where <user> is the username they claim to be, and <response> is the
base64 form of the MD4 hash of challenge+password.
The <user> is the username they claim to be. The <response> is the
base64 form of the digest hash of the challenge+password string. The
chosen digest method is the most preferred client method that is also in
the server's list. If no digest list was explicitly provided, the side
expecting a list assumes the other side provided either the single name
"md5" (for a negotiated protocol 30 or 31), or the single name "md4"
(for an older protocol).
At this point the server applies all remaining constraints before
handing control to the client, including switching uid/gid, setting up
@@ -76,6 +91,13 @@ stay tuned (or write it yourself!).
------------
Protocol version changes
31 (2013-09-28, 3.1.0)
Initial release of protocol 31 had no changes. Rsync 3.2.7
introduced the suffixed list of digest names on the greeting
line. The presence of the list is allowed even if the greeting
indicates an older protocol version number.
30 (2007-10-04, 3.0.0pre1)
The use of a ".<subprotocol>" number was added to

114
daemon-parm.awk Executable file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/awk -f
# The caller must pass arg: daemon-parm.txt
# The resulting code is output into daemon-parm.h
BEGIN {
heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from a list of values in " ARGV[1] "! */\n\n"
sect = psect = defines = accessors = prior_ptype = ""
parms = "\nstatic const struct parm_struct parm_table[] = {"
comment_fmt = "\n/********** %s **********/\n"
tdstruct = "typedef struct {"
}
/^\s*$/ { next }
/^#/ { next }
/^Globals:/ {
if (defines != "") {
print "The Globals section must come first!"
defines = ""
exit
}
defines = tdstruct
values = "\nstatic const all_vars Defaults = {\n { /* Globals: */\n"
exps = exp_values = sprintf(comment_fmt, "EXP")
sect = "GLOBAL"
psect = ", P_GLOBAL, &Vars.g."
next
}
/^Locals:/ {
if (sect == "") {
print "The Locals section must come after the Globals!"
exit
}
defines = defines exps "} global_vars;\n\n" tdstruct
values = values exp_values "\n }, { /* Locals: */\n"
exps = exp_values = sprintf(comment_fmt, "EXP")
sect = "LOCAL"
psect = ", P_LOCAL, &Vars.l."
next
}
/^(STRING|CHAR|PATH|INTEGER|ENUM|OCTAL|BOOL|BOOLREV|BOOL3)[ \t]/ {
ptype = $1
name = $2
$1 = $2 = ""
sub(/^[ \t]+/, "")
if (ptype != prior_ptype) {
comment = sprintf(comment_fmt, ptype)
defines = defines comment
values = values comment
parms = parms "\n"
accessors = accessors "\n"
prior_ptype = ptype
}
if (ptype == "STRING" || ptype == "PATH") {
atype = "STRING"
vtype = "char*"
} else if (ptype ~ /BOOL/) {
atype = vtype = "BOOL"
} else if (ptype == "CHAR") {
atype = "CHAR"
vtype = "char"
} else {
atype = "INTEGER"
vtype = "int"
}
# The name might be var_name|public_name
pubname = name
sub(/\|.*/, "", name)
sub(/.*\|/, "", pubname)
gsub(/_/, " ", pubname)
gsub(/-/, "", name)
if (ptype == "ENUM")
enum = "enum_" name
else
enum = "NULL"
defines = defines "\t" vtype " " name ";\n"
values = values "\t" $0 ", /* " name " */\n"
parms = parms " {\"" pubname "\", P_" ptype psect name ", " enum ", 0},\n"
accessors = accessors "FN_" sect "_" atype "(lp_" name ", " name ")\n"
if (vtype == "char*") {
exps = exps "\tBOOL " name "_EXP;\n"
exp_values = exp_values "\tFalse, /* " name "_EXP */\n"
}
next
}
/./ {
print "Extraneous line:" $0
defines = ""
exit
}
END {
if (sect != "" && defines != "") {
defines = defines exps "} local_vars;\n\n"
defines = defines tdstruct "\n\tglobal_vars g;\n\tlocal_vars l;\n} all_vars;\n"
values = values exp_values "\n }\n};\n\nstatic all_vars Vars;\n"
parms = parms "\n {NULL, P_BOOL, P_NONE, NULL, NULL, 0}\n};\n"
print heading defines values parms accessors > "daemon-parm.h"
} else {
print "Failed to parse the data in " ARGV[1]
exit 1
}
}

68
daemon-parm.txt Normal file
View File

@@ -0,0 +1,68 @@
Globals: ================================================================
STRING bind_address|address NULL
STRING daemon_chroot NULL
STRING daemon_gid NULL
STRING daemon_uid NULL
STRING motd_file NULL
STRING pid_file NULL
STRING socket_options NULL
INTEGER listen_backlog 5
INTEGER rsync_port|port 0
BOOL proxy_protocol False
Locals: =================================================================
STRING auth_users NULL
STRING charset NULL
STRING comment NULL
STRING dont_compress DEFAULT_DONT_COMPRESS
STRING early_exec NULL
STRING exclude NULL
STRING exclude_from NULL
STRING filter NULL
STRING gid NULL
STRING hosts_allow NULL
STRING hosts_deny NULL
STRING include NULL
STRING include_from NULL
STRING incoming_chmod NULL
STRING lock_file DEFAULT_LOCK_FILE
STRING log_file NULL
STRING log_format "%o %h [%a] %m (%u) %f %l"
STRING name NULL
STRING name_converter NULL
STRING outgoing_chmod NULL
STRING post-xfer_exec NULL
STRING pre-xfer_exec NULL
STRING refuse_options NULL
STRING secrets_file NULL
STRING syslog_tag "rsyncd"
STRING uid NULL
PATH path NULL
PATH temp_dir NULL
INTEGER max_connections 0
INTEGER max_verbosity 1
INTEGER timeout 0
ENUM syslog_facility LOG_DAEMON
BOOL fake_super False
BOOL forward_lookup True
BOOL ignore_errors False
BOOL ignore_nonreadable False
BOOL list True
BOOL read_only True
BOOL reverse_lookup True
BOOL strict_modes True
BOOL transfer_logging False
BOOL write_only False
BOOL3 munge_symlinks Unset
BOOL3 numeric_ids Unset
BOOL3 open_noatime Unset
BOOL3 use_chroot Unset

View File

@@ -4,7 +4,7 @@
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2024 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -98,7 +98,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
strlcpy(p, fp->basename, remainder);
if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
do_chmod(fname, fp->mode | S_IWUSR);
do_chmod_at(fname, fp->mode | S_IWUSR);
/* Save stack by recursing to ourself directly. */
if (S_ISDIR(fp->mode)) {
if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
@@ -139,7 +139,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
}
if (flags & DEL_NO_UID_WRITE)
do_chmod(fbuf, mode | S_IWUSR);
do_chmod_at(fbuf, mode | S_IWUSR);
if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
/* This only happens on the first call to delete_item() since
@@ -160,7 +160,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
if (S_ISDIR(mode)) {
what = "rmdir";
ok = do_rmdir(fbuf) == 0;
ok = do_rmdir_at(fbuf) == 0;
} else {
if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
what = "make_backup";
@@ -188,7 +188,7 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
stats.deleted_symlinks++;
#endif
else if (IS_DEVICE(mode))
stats.deleted_symlinks++;
stats.deleted_devices++;
else
stats.deleted_specials++;
}

View File

@@ -1,20 +0,0 @@
Handling the rsync SGML documentation
rsync documentation is now primarily in Docbook format. Docbook is an
SGML/XML documentation format that is becoming standard on free
operating systems. It's also used for Samba documentation.
The SGML files are source code that can be translated into various
useful output formats, primarily PDF, HTML, Postscript and plain text.
To do this transformation on Debian, you should install the
docbook-utils package. Having done that, you can say
docbook2pdf rsync.sgml
and so on.
On other systems you probably need James Clark's "sp" and "JadeTeX"
packages. Work it out for yourself and send a note to the mailing
list.

View File

@@ -1,42 +0,0 @@
Notes on rsync profiling
strlcpy is hot:
0.00 0.00 1/7735635 push_dir [68]
0.00 0.00 1/7735635 pop_dir [71]
0.00 0.00 1/7735635 send_file_list [15]
0.01 0.00 18857/7735635 send_files [4]
0.04 0.00 129260/7735635 send_file_entry [18]
0.04 0.00 129260/7735635 make_file [20]
0.04 0.00 141666/7735635 send_directory <cycle 1> [36]
2.29 0.00 7316589/7735635 f_name [13]
[14] 11.7 2.42 0.00 7735635 strlcpy [14]
Here's the top few functions:
46.23 9.57 9.57 13160929 0.00 0.00 mdfour64
14.78 12.63 3.06 13160929 0.00 0.00 copy64
11.69 15.05 2.42 7735635 0.00 0.00 strlcpy
10.05 17.13 2.08 41438 0.05 0.38 sum_update
4.11 17.98 0.85 13159996 0.00 0.00 mdfour_update
1.50 18.29 0.31 file_compare
1.45 18.59 0.30 129261 0.00 0.01 send_file_entry
1.23 18.84 0.26 2557585 0.00 0.00 f_name
1.11 19.07 0.23 1483750 0.00 0.00 u_strcmp
1.11 19.30 0.23 118129 0.00 0.00 writefd_unbuffered
0.92 19.50 0.19 1085011 0.00 0.00 writefd
0.43 19.59 0.09 156987 0.00 0.00 read_timeout
0.43 19.68 0.09 129261 0.00 0.00 clean_fname
0.39 19.75 0.08 32887 0.00 0.38 matched
0.34 19.82 0.07 1 70.00 16293.92 send_files
0.29 19.89 0.06 129260 0.00 0.00 make_file
0.29 19.95 0.06 75430 0.00 0.00 read_unbuffered
mdfour could perhaps be made faster:
/* NOTE: This code makes no attempt to be fast! */
There might be an optimized version somewhere that we can borrow.

View File

@@ -1,351 +0,0 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
<book id="rsync">
<bookinfo>
<title>rsync</title>
<copyright>
<year>1996 -- 2002</year>
<holder>Martin Pool</holder>
<holder>Andrew Tridgell</holder>
</copyright>
<author>
<firstname>Martin</firstname>
<surname>Pool</surname>
</author>
</bookinfo>
<chapter>
<title>Introduction</title>
<para>rsync is a flexible program for efficiently copying files or
directory trees.
<para>rsync has many options to select which files will be copied
and how they are to be transferred. It may be used as an
alternative to ftp, http, scp or rcp.
<para>The rsync remote-update protocol allows rsync to transfer just
the differences between two sets of files across the network link,
using an efficient checksum-search algorithm described in the
technical report that accompanies this package.</para>
<para>Some of the additional features of rsync are:</para>
<itemizedlist>
<listitem>
<para>support for copying links, devices, owners, groups and
permissions
</para>
</listitem>
<listitem>
<para>
exclude and exclude-from options similar to GNU tar
</para>
</listitem>
<listitem>
<para>
a CVS exclude mode for ignoring the same files that CVS would ignore
</listitem>
<listitem>
<para>
can use any transparent remote shell, including rsh or ssh
</listitem>
<listitem>
<para>
does not require root privileges
</listitem>
<listitem>
<para>
pipelining of file transfers to minimize latency costs
</listitem>
<listitem>
<para>
support for anonymous or authenticated rsync servers (ideal for
mirroring)
</para>
</listitem>
</itemizedlist>
</chapter>
<chapter>
<title>Using rsync</title>
<section>
<title>
Introductory example
</title>
<para>
Probably the most common case of rsync usage is to copy files
to or from a remote machine using
<application>ssh</application> as a network transport. In
this situation rsync is a good alternative to
<application>scp</application>.
</para>
<para>
The most commonly used arguments for rsync are
</para>
<variablelist>
<varlistentry>
<term><option>-v</option></term>
<listitem>
<para>Be verbose. Primarily, display the name of each file as it is copied.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-a</option></term>
<listitem>
<para>
Reproduce the structure and attributes of the origin files as exactly
as possible: this includes copying subdirectories, symlinks, special
files, ownership and permissions. (@xref{Attributes to
copy}.)
</para>
</listitem>
</varlistentry>
</variablelist>
<para><option>-v </option>
<para><option>-z</option>
Compress network traffic, using a modified version of the
@command{zlib} library.</para>
<para><option>-P</option>
Display a progress indicator while files are transferred. This should
normally be omitted if rsync is not run on a terminal.
</para>
</section>
<section>
<title>Local and remote</title>
<para>There are six different ways of using rsync. They
are:</para>
<!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE MSGSET PROCEDURE SIDEBAR QANDASET ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS ABSTRACT AUTHORBLURB EPIGRAPH INDEXTERM REFENTRY SECTION) -->
<orderedlist>
<listitem>
<para>
for copying local files. This is invoked when neither
source nor destination path contains a @code{:} separator
<listitem>
<para>
for copying from the local machine to a remote machine using
a remote shell program as the transport (such as rsh or
ssh). This is invoked when the destination path contains a
single @code{:} separator.
<listitem>
<para>
for copying from a remote machine to the local machine
using a remote shell program. This is invoked when the source
contains a @code{:} separator.
<listitem>
<para>
for copying from a remote rsync server to the local
machine. This is invoked when the source path contains a @code{::}
separator or a @code{rsync://} URL.
<listitem>
<para>
for copying from the local machine to a remote rsync
server. This is invoked when the destination path contains a @code{::}
separator.
<listitem>
<para>
for listing files on a remote machine. This is done the
same way as rsync transfers except that you leave off the
local destination.
</listitem>
</orderedlist>
<para>
Note that in all cases (other than listing) at least one of the source
and destination paths must be local.
<para>
Any one invocation of rsync makes a copy in a single direction. rsync
currently has no equivalent of @command{ftp}'s interactive mode.
@cindex @sc{nfs}
@cindex network filesystems
@cindex remote filesystems
<para>
rsync's network protocol is generally faster at copying files than
network filesystems such as @sc{nfs} or @sc{cifs}. It is better to
run rsync on the file server either as a daemon or over ssh than
running rsync giving the network directory.
</para>
</section>
</chapter>
<chapter>
<title>Frequently asked questions</title>
<!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE MSGSET PROCEDURE SIDEBAR QANDASET ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS ABSTRACT AUTHORBLURB EPIGRAPH INDEXTERM SECTION SIMPLESECT REFENTRY SECT1) -->
<qandaset>
<!-- one of (QANDADIV QANDAENTRY) -->
<qandaentry>
<question>
<!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST
SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE
TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO
SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS
CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS
DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA
ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT
MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE
INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE
PROCEDURE ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS INDEXTERM) -->
<para>Are there mailing lists for rsync?
</question>
<answer>
<para>Yes, and you can subscribe and unsubscribe through a
web interface at
<ulink
url="http://lists.samba.org/">http://lists.samba.org/</ulink>
</para>
<para>
If you are having trouble with the mailing list, please
send mail to the administrator
<email>rsync-admin@lists.samba.org</email>
not to the list itself.
</para>
<para>
The mailing list archives are searchable. Use
<ulink url="http://google.com/">Google</ulink> and prepend
the search with <userinput>site:lists.samba.org
rsync</userinput>, plus relevant keywords.
</para>
</answer>
</qandaentry>
<qandaentry>
<question>
<para>
Why is rsync so much bigger when I build it with
<command>gcc</command>?
</para>
</question>
<answer>
<para>
On gcc, rsync builds by default with debug symbols
included. If you strip both executables, they should end
up about the same size. (Use <command>make
install-strip</command>.)
</para>
</answer>
</qandaentry>
<qandaentry>
<question>
<para>Is rsync useful for a single large file like an ISO image?</para>
</question>
<answer>
<para>
Yes, but note the following:
<para>
Background: A common use of rsync is to update a file (or set of files) in one location from a more
correct or up-to-date copy in another location, taking advantage of portions of the files that are
identical to speed up the process. (Note that rsync will transfer a file in its entirety if no copy
exists at the destination.)
<para>
(This discussion is written in terms of updating a local copy of a file from a correct file in a
remote location, although rsync can work in either direction.)
<para>
The file to be updated (the local file) must be in a destination directory that has enough space for
two copies of the file. (In addition, keep an extra copy of the file to be updated in a different
location for safety -- see the discussion (below) about rsync's behavior when the rsync process is
interrupted before completion.)
<para>
The local file must have the same name as the remote file being sync'd to (I think?). If you are
trying to upgrade an iso from, for example, beta1 to beta2, rename the local file to the same name
as the beta2 file. *(This is a useful thing to do -- only the changed portions will be
transmitted.)*
<para>
The extra copy of the local file kept in a different location is because of rsync's behavior if
interrupted before completion:
<para>
* If you specify the --partial option and rsync is interrupted, rsync will save the partially
rsync'd file and throw away the original local copy. (The partially rsync'd file is correct but
truncated.) If rsync is restarted, it will not have a local copy of the file to check for duplicate
blocks beyond the section of the file that has already been rsync'd, thus the remainder of the rsync
process will be a "pure transfer" of the file rather than taking advantage of the rsync algorithm.
<para>
* If you don't specify the --partial option and rsync is interrupted, rsync will throw away the
partially rsync'd file, and, when rsync is restarted starts the rsync process over from the
beginning.
<para>
Which of these is most desirable depends on the degree of commonality between the local and remote
copies of the file *and how much progress was made before the interruption*.
<para>
The ideal approach after an interruption would be to create a new file by taking the original file
and deleting a portion equal in size to the portion already rsync'd and then appending *the
remaining* portion to the portion of the file that has already been rsync'd. (There has been some
discussion about creating an option to do this automatically.)
The --compare-dest option is useful when transferring multiple files, but is of no benefit in
transferring a single file. (AFAIK)
*Other potentially useful information can be found at:
-[3]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFile
This answer, formatted with "real" bullets, can be found at:
-[4]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFileFAQ*
</para>
</answer>
</qandaentry>
</qandaset>
</chapter>
<appendix>
<title>Other Resources</title>
<para><ulink url="http://www.ccp14.ac.uk/ccp14admin/rsync/"></ulink></para>
</appendix>
</book>

344
exclude.c
View File

@@ -4,7 +4,7 @@
* Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2024 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,21 +21,25 @@
*/
#include "rsync.h"
#include "default-cvsignore.h"
#include "ifuncs.h"
extern int am_server;
extern int am_sender;
extern int am_generator;
extern int eol_nulls;
extern int io_error;
extern int xfer_dirs;
extern int recurse;
extern int local_server;
extern int prune_empty_dirs;
extern int ignore_perishable;
extern int relative_paths;
extern int delete_mode;
extern int delete_excluded;
extern int cvs_exclude;
extern int sanitize_paths;
extern int protocol_version;
extern int trust_sender_args;
extern int module_id;
extern char curr_dir[MAXPATHLEN];
@@ -45,8 +49,11 @@ extern unsigned int module_dirlen;
filter_rule_list filter_list = { .debug_type = "" };
filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
filter_rule_list implied_filter_list = { .debug_type = " [implied]" };
int saw_xattr_filter = 0;
int trust_sender_args = 0;
int trust_sender_filter = 0;
/* Need room enough for ":MODS " prefix plus some room to grow. */
#define MAX_RULE_PREFIX (16)
@@ -71,6 +78,10 @@ static filter_rule **mergelist_parents;
static int mergelist_cnt = 0;
static int mergelist_size = 0;
#define LOCAL_RULE 1
#define REMOTE_RULE 2
static uchar cur_elide_value = REMOTE_RULE;
/* Each filter_list_struct describes a singly-linked list by keeping track
* of both the head and tail pointers. The list is slightly unusual in that
* a parent-dir's content can be appended to the end of the local list in a
@@ -153,13 +164,17 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
{
const char *cp;
unsigned int pre_len, suf_len, slash_cnt = 0;
char *mention_rule_suffix;
if (DEBUG_GTE(FILTER, 2)) {
rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s\n",
if (DEBUG_GTE(FILTER, 1) && pat_len && (pat[pat_len-1] == ' ' || pat[pat_len-1] == '\t'))
mention_rule_suffix = " -- CAUTION: trailing whitespace!";
else
mention_rule_suffix = DEBUG_GTE(FILTER, 2) ? "" : NULL;
if (mention_rule_suffix) {
rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s%s\n",
who_am_i(), get_rule_prefix(rule, pat, 0, NULL),
(int)pat_len, pat,
(rule->rflags & FILTRULE_DIRECTORY) ? "/" : "",
listp->debug_type);
(int)pat_len, pat, (rule->rflags & FILTRULE_DIRECTORY) ? "/" : "",
listp->debug_type, mention_rule_suffix);
}
/* These flags also indicate that we're reading a list that
@@ -209,6 +224,7 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
slash_cnt++;
}
}
rule->elide = 0;
strlcpy(rule->pattern + pre_len, pat, pat_len + 1);
pat_len += pre_len;
if (suf_len) {
@@ -289,6 +305,271 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
}
}
/* If the wildcards failed, the remote shell might give us a file matching the literal
* wildcards. Since "*" & "?" already match themselves, this just needs to deal with
* failed "[foo]" idioms.
*/
static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg_len)
{
filter_rule *rule;
const char *arg = based_on->pattern, *cp;
char *p;
int cnt = 0;
if (arg_len < 0)
arg_len = strlen(arg);
for (cp = arg; *cp; cp++) {
if (*cp == '\\' && cp[1]) {
cp++;
} else if (*cp == '[')
cnt++;
}
if (!cnt)
return;
rule = new0(filter_rule);
rule->rflags = based_on->rflags;
rule->u.slash_cnt = based_on->u.slash_cnt;
p = rule->pattern = new_array(char, arg_len + cnt + 1);
for (cp = arg; *cp; ) {
if (*cp == '\\' && cp[1]) {
*p++ = *cp++;
} else if (*cp == '[')
*p++ = '\\';
*p++ = *cp++;
}
*p++ = '\0';
rule->next = implied_filter_list.head;
implied_filter_list.head = rule;
if (DEBUG_GTE(FILTER, 3)) {
rprintf(FINFO, "[%s] add_implied_include(%s%s)\n", who_am_i(), rule->pattern,
rule->rflags & FILTRULE_DIRECTORY ? "/" : "");
}
}
static char *partial_string_buf = NULL;
static int partial_string_len = 0;
void implied_include_partial_string(const char *s_start, const char *s_end)
{
partial_string_len = s_end - s_start;
if (partial_string_len <= 0 || partial_string_len >= MAXPATHLEN) { /* too-large should be impossible... */
partial_string_len = 0;
return;
}
if (!partial_string_buf)
partial_string_buf = new_array(char, MAXPATHLEN);
memcpy(partial_string_buf, s_start, partial_string_len);
}
void free_implied_include_partial_string()
{
if (partial_string_buf) {
if (partial_string_len)
add_implied_include("", 0);
free(partial_string_buf);
partial_string_buf = NULL;
}
partial_string_len = 0; /* paranoia */
}
/* Each arg the client sends to the remote sender turns into an implied include
* that the receiver uses to validate the file list from the sender. */
void add_implied_include(const char *arg, int skip_daemon_module)
{
int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;
int slash_cnt = 0;
const char *cp;
char *p;
if (trust_sender_args)
return;
if (partial_string_len) {
arg_len = strlen(arg);
if (partial_string_len + arg_len >= MAXPATHLEN) {
partial_string_len = 0;
return; /* Should be impossible... */
}
memcpy(partial_string_buf + partial_string_len, arg, arg_len + 1);
partial_string_len = 0;
arg = partial_string_buf;
}
if (skip_daemon_module) {
if ((cp = strchr(arg, '/')) != NULL)
arg = cp + 1;
else
arg = "";
}
if (relative_paths) {
if ((cp = strstr(arg, "/./")) != NULL)
arg = cp + 3;
} else if ((cp = strrchr(arg, '/')) != NULL) {
arg = cp + 1;
}
if (*arg == '.' && arg[1] == '\0')
arg++;
arg_len = strlen(arg);
if (arg_len) {
char *new_pat;
if (strpbrk(arg, "*[?")) {
/* We need to add room to escape backslashes if wildcard chars are present. */
for (cp = arg; (cp = strchr(cp, '\\')) != NULL; cp++)
arg_len++;
saw_wild = 1;
}
arg_len++; /* Leave room for the prefixed slash */
p = new_pat = new_array(char, arg_len + 1);
*p++ = '/';
slash_cnt++;
for (cp = arg; *cp; ) {
switch (*cp) {
case '\\':
if (cp[1] == ']') {
if (!saw_wild)
cp++; /* A \] in a non-wild filter causes a problem, so drop the \ . */
} else if (!strchr("*[?", cp[1])) {
backslash_cnt++;
if (saw_wild)
*p++ = '\\';
}
*p++ = *cp++;
break;
case '/':
if (p[-1] == '/') { /* This is safe because of the initial slash. */
if (*++cp == '\0') {
slash_cnt--;
p--;
}
} else if (cp[1] == '\0') {
cp++;
} else {
slash_cnt++;
*p++ = *cp++;
}
break;
case '.':
if (p[-1] == '/') {
if (cp[1] == '/') {
cp += 2;
if (!*cp) {
slash_cnt--;
p--;
}
} else if (cp[1] == '\0') {
cp++;
slash_cnt--;
p--;
} else
*p++ = *cp++;
} else
*p++ = *cp++;
break;
case '[':
saw_live_open_brkt = 1;
*p++ = *cp++;
break;
default:
*p++ = *cp++;
break;
}
}
*p = '\0';
arg_len = p - new_pat;
if (!arg_len)
free(new_pat);
else {
filter_rule *rule = new0(filter_rule);
rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
rule->u.slash_cnt = slash_cnt;
arg = rule->pattern = new_pat;
if (!implied_filter_list.head)
implied_filter_list.head = implied_filter_list.tail = rule;
else {
rule->next = implied_filter_list.head;
implied_filter_list.head = rule;
}
if (DEBUG_GTE(FILTER, 3))
rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), arg);
if (saw_live_open_brkt)
maybe_add_literal_brackets_rule(rule, arg_len);
if (relative_paths && slash_cnt) {
int sub_slash_cnt = slash_cnt;
while ((p = strrchr(new_pat, '/')) != NULL && p != new_pat) {
filter_rule const *ent;
filter_rule *R_rule;
int found = 0;
*p = '\0';
for (ent = implied_filter_list.head; ent; ent = ent->next) {
if (ent != rule && strcmp(ent->pattern, new_pat) == 0) {
found = 1;
break;
}
}
if (found) {
*p = '/';
break; /* We added all parent dirs already */
}
R_rule = new0(filter_rule);
R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY;
/* Check if our sub-path has wildcards or escaped backslashes */
if (saw_wild && strpbrk(new_pat, "*[?\\"))
R_rule->rflags |= FILTRULE_WILD;
R_rule->pattern = strdup(new_pat);
R_rule->u.slash_cnt = --sub_slash_cnt;
R_rule->next = implied_filter_list.head;
implied_filter_list.head = R_rule;
if (DEBUG_GTE(FILTER, 3)) {
rprintf(FINFO, "[%s] add_implied_include(%s/)\n",
who_am_i(), R_rule->pattern);
}
if (saw_live_open_brkt)
maybe_add_literal_brackets_rule(R_rule, -1);
}
for (p = new_pat; sub_slash_cnt < slash_cnt; sub_slash_cnt++) {
p += strlen(p);
*p = '/';
}
}
}
}
if (recurse || xfer_dirs) {
/* Now create a rule with an added "/" & "**" or "*" at the end */
filter_rule *rule = new0(filter_rule);
rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
if (recurse)
rule->rflags |= FILTRULE_WILD2;
/* We must leave enough room for / * * \0. */
if (!saw_wild && backslash_cnt) {
/* We are appending a wildcard, so now the backslashes need to be escaped. */
p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
for (cp = arg; *cp; ) { /* Note that arg_len != 0 because backslash_cnt > 0 */
if (*cp == '\\')
*p++ = '\\';
*p++ = *cp++;
}
} else {
p = rule->pattern = new_array(char, arg_len + 3 + 1);
if (arg_len) {
memcpy(p, arg, arg_len);
p += arg_len;
}
}
*p++ = '/';
*p++ = '*';
if (recurse)
*p++ = '*';
*p = '\0';
rule->u.slash_cnt = slash_cnt + 1;
rule->next = implied_filter_list.head;
implied_filter_list.head = rule;
if (DEBUG_GTE(FILTER, 3))
rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern);
if (saw_live_open_brkt)
maybe_add_literal_brackets_rule(rule, p - rule->pattern);
}
}
/* This frees any non-inherited items, leaving just inherited items on the list. */
static void pop_filter_list(filter_rule_list *listp)
{
@@ -439,7 +720,8 @@ static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
parent_dirscan = True;
while (*y) {
char save[MAXPATHLEN];
strlcpy(save, y, MAXPATHLEN);
/* copylen is strlen(y) which is < MAXPATHLEN. +1 for \0 */
size_t copylen = strlcpy(save, y, MAXPATHLEN) + 1;
*y = '\0';
dirbuf_len = y - dirbuf;
strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
@@ -453,7 +735,7 @@ static BOOL setup_merge_file(int mergelist_num, filter_rule *ex,
lp->head = NULL;
}
lp->tail = NULL;
strlcpy(y, save, MAXPATHLEN);
strlcpy(y, save, copylen);
while ((*x++ = *y++) != '/') {}
}
parent_dirscan = False;
@@ -622,11 +904,11 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_flags)
{
int slash_handling, str_cnt = 0, anchored_match = 0;
int ret_match = ex->rflags & FILTRULE_NEGATE ? 0 : 1;
char *p, *pattern = ex->pattern;
const char *p, *pattern = ex->pattern;
const char *strings[16]; /* more than enough */
const char *name = fname + (*fname == '/');
if (!*name)
if (!*name || ex->elide == cur_elide_value)
return 0;
if (!(name_flags & NAME_IS_XATTR) ^ !(ex->rflags & FILTRULE_XATTR))
@@ -703,11 +985,12 @@ static void report_filter_result(enum logcode code, char const *name,
filter_rule const *ent,
int name_flags, const char *type)
{
int log_level = am_sender || am_generator ? 1 : 3;
/* If a trailing slash is present to match only directories,
* then it is stripped out by add_rule(). So as a special
* case we add it back in here. */
if (DEBUG_GTE(FILTER, 1)) {
* case we add it back in the log output. */
if (DEBUG_GTE(FILTER, log_level)) {
static char *actions[2][2]
= { {"show", "hid"}, {"risk", "protect"} };
const char *w = who_am_i();
@@ -715,7 +998,7 @@ static void report_filter_result(enum logcode code, char const *name,
: name_flags & NAME_IS_DIR ? "directory"
: "file";
rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n",
w, actions[*w!='s'][!(ent->rflags & FILTRULE_INCLUDE)],
w, actions[*w=='g'][!(ent->rflags & FILTRULE_INCLUDE)],
t, name, ent->pattern,
ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type);
}
@@ -741,6 +1024,15 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
return 0;
}
int check_server_filter(filter_rule_list *listp, enum logcode code, const char *name, int name_flags)
{
int ret;
cur_elide_value = LOCAL_RULE;
ret = check_filter(listp, code, name, name_flags);
cur_elide_value = REMOTE_RULE;
return ret;
}
/* Return -1 if file "name" is defined to be excluded by the specified
* exclude list, 1 if it is included, and 0 if it was not matched. */
int check_filter(filter_rule_list *listp, enum logcode code,
@@ -887,6 +1179,7 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
}
switch (ch) {
case ':':
trust_sender_filter = 1;
rule->rflags |= FILTRULE_PERDIR_MERGE
| FILTRULE_FINISH_SETUP;
/* FALL THROUGH */
@@ -1053,7 +1346,7 @@ static void get_cvs_excludes(uint32 rflags)
return;
initialized = 1;
parse_filter_str(&cvs_filter_list, DEFAULT_CVSIGNORE,
parse_filter_str(&cvs_filter_list, default_cvsignore(),
rule_template(rflags | (protocol_version >= 30 ? FILTRULE_PERISHABLE : 0)),
0);
@@ -1295,7 +1588,7 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
static void send_rules(int f_out, filter_rule_list *flp)
{
filter_rule *ent, *prev = NULL;
filter_rule *ent;
for (ent = flp->head; ent; ent = ent->next) {
unsigned int len, plen, dlen;
@@ -1310,21 +1603,15 @@ static void send_rules(int f_out, filter_rule_list *flp)
* merge files as an optimization (since they can only have
* include/exclude rules). */
if (ent->rflags & FILTRULE_SENDER_SIDE)
elide = am_sender ? 1 : -1;
elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
if (ent->rflags & FILTRULE_RECEIVER_SIDE)
elide = elide ? 0 : am_sender ? -1 : 1;
elide = elide ? 0 : am_sender ? REMOTE_RULE : LOCAL_RULE;
else if (delete_excluded && !elide
&& (!(ent->rflags & FILTRULE_PERDIR_MERGE)
|| ent->rflags & FILTRULE_NO_PREFIXES))
elide = am_sender ? 1 : -1;
if (elide < 0) {
if (prev)
prev->next = ent->next;
else
flp->head = ent->next;
} else
prev = ent;
if (elide > 0)
elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
ent->elide = elide;
if (elide == LOCAL_RULE)
continue;
if (ent->rflags & FILTRULE_CVS_IGNORE
&& !(ent->rflags & FILTRULE_MERGE_FILE)) {
@@ -1352,7 +1639,6 @@ static void send_rules(int f_out, filter_rule_list *flp)
if (dlen)
write_byte(f_out, '/');
}
flp->tail = prev;
}
/* This is only called by the client. */

View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1998 Andrew Tridgell
* Copyright (C) 2002 Martin Pool
* Copyright (C) 2004-2020 Wayne Davison
* Copyright (C) 2004-2023 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,30 +40,34 @@ OFF_T preallocated_len = 0;
static OFF_T sparse_seek = 0;
static OFF_T sparse_past_write = 0;
int sparse_end(int f, OFF_T size)
int sparse_end(int f, OFF_T size, int updating_basis_or_equiv)
{
int ret;
int ret = 0;
sparse_past_write = 0;
if (!sparse_seek)
return 0;
#ifdef HAVE_FTRUNCATE
ret = do_ftruncate(f, size);
#else
if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
ret = -1;
else {
do {
ret = write(f, "", 1);
} while (ret < 0 && errno == EINTR);
ret = ret <= 0 ? -1 : 0;
}
if (updating_basis_or_equiv) {
if (sparse_seek && do_punch_hole(f, sparse_past_write, sparse_seek) < 0)
ret = -1;
#ifdef HAVE_FTRUNCATE /* A compilation formality -- in-place requires ftruncate() */
else /* Just in case the original file was longer */
ret = do_ftruncate(f, size);
#endif
} else if (sparse_seek) {
#ifdef HAVE_FTRUNCATE
ret = do_ftruncate(f, size);
#else
if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
ret = -1;
else {
do {
ret = write(f, "", 1);
} while (ret < 0 && errno == EINTR);
sparse_seek = 0;
ret = ret <= 0 ? -1 : 0;
}
#endif
}
sparse_past_write = sparse_seek = 0;
return ret;
}

262
flist.c
View File

@@ -4,7 +4,7 @@
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2002-2020 Wayne Davison
* Copyright (C) 2002-2023 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,6 @@ extern int am_sender;
extern int am_generator;
extern int inc_recurse;
extern int always_checksum;
extern int checksum_type;
extern int module_id;
extern int ignore_errors;
extern int numeric_ids;
@@ -43,6 +42,7 @@ extern int use_qsort;
extern int xfer_dirs;
extern int filesfrom_fd;
extern int one_file_system;
extern int copy_devices;
extern int copy_dirlinks;
extern int preserve_uid;
extern int preserve_gid;
@@ -56,6 +56,7 @@ extern int delete_during;
extern int missing_args;
extern int eol_nulls;
extern int atimes_ndx;
extern int crtimes_ndx;
extern int relative_paths;
extern int implied_dirs;
extern int ignore_perishable;
@@ -71,18 +72,20 @@ extern int need_unsorted_flist;
extern int sender_symlink_iconv;
extern int output_needs_newline;
extern int sender_keeps_checksum;
extern int trust_sender_filter;
extern int unsort_ndx;
extern uid_t our_uid;
extern struct stats stats;
extern char *filesfrom_host;
extern char *usermap, *groupmap;
extern struct name_num_item *file_sum_nni;
extern char curr_dir[MAXPATHLEN];
extern struct chmod_mode_struct *chmod_modes;
extern filter_rule_list filter_list;
extern filter_rule_list daemon_filter_list;
extern filter_rule_list filter_list, implied_filter_list, daemon_filter_list;
#ifdef ICONV_OPTION
extern int filesfrom_convert;
@@ -129,10 +132,23 @@ static int64 tmp_dev = -1, tmp_ino;
#endif
static char tmp_sum[MAX_DIGEST_LEN];
#ifdef ST_MTIME_NSEC
/* Return st_mtim nsec if it is in the wire-valid range, else 0. */
static inline uint32 wire_mtime_nsec_from_stat(const STRUCT_STAT *stp)
{
unsigned long nsec = (unsigned long)stp->ST_MTIME_NSEC;
if (nsec > MAX_WIRE_NSEC)
return 0;
return (uint32)nsec;
}
#endif
static char empty_sum[MAX_DIGEST_LEN];
static int flist_count_offset; /* for --delete --progress */
static int show_filelist_progress;
static struct file_list *flist_new(int flags, const char *msg);
static void flist_sort_and_clean(struct file_list *flist, int strip_root);
static void output_flist(struct file_list *flist);
@@ -142,7 +158,8 @@ void init_flist(void)
rprintf(FINFO, "FILE_STRUCT_LEN=%d, EXTRA_LEN=%d\n",
(int)FILE_STRUCT_LEN, (int)EXTRA_LEN);
}
flist_csum_len = csum_len_for_type(checksum_type, 1);
/* Note that this isn't identical to file_sum_len in the case of CSUM_MD4_ARCHAIC: */
flist_csum_len = csum_len_for_type(file_sum_nni->num, 1);
show_filelist_progress = INFO_GTE(FLIST, 1) && xfer_dirs && !am_server && !inc_recurse;
}
@@ -293,6 +310,8 @@ static void flist_expand(struct file_list *flist, int extra)
flist->malloced = FLIST_START;
else if (flist->malloced >= FLIST_LINEAR)
flist->malloced += FLIST_LINEAR;
else if (flist->malloced < FLIST_START_LARGE/16)
flist->malloced *= 4;
else
flist->malloced *= 2;
@@ -303,7 +322,7 @@ static void flist_expand(struct file_list *flist, int extra)
new_ptr = realloc_array(flist->files, struct file_struct *, flist->malloced);
if (DEBUG_GTE(FLIST, 1) && flist->malloced != FLIST_START) {
if (DEBUG_GTE(FLIST, 1) && flist->files) {
rprintf(FCLIENT, "[%s] expand file_list pointer array to %s bytes, did%s move\n",
who_am_i(),
big_num(sizeof flist->files[0] * flist->malloced),
@@ -377,6 +396,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
int ndx, int first_ndx)
{
static time_t modtime, atime;
#ifdef SUPPORT_CRTIMES
static time_t crtime;
#endif
static mode_t mode;
#ifdef SUPPORT_HARD_LINKS
static int64 dev;
@@ -443,7 +465,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
if (protocol_version < 28)
xflags |= XMIT_SAME_RDEV_pre28;
else {
rdev = MAKEDEV(major(rdev), 0);
rdev = MAKEDEV(rdev_major, 0);
xflags |= XMIT_SAME_RDEV_MAJOR;
if (protocol_version < 30)
xflags |= XMIT_RDEV_MINOR_8_pre30;
@@ -482,6 +504,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
else
atime = F_ATIME(file);
}
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx) {
crtime = F_CRTIME(file);
if (crtime == modtime)
xflags |= XMIT_CRTIME_EQ_MTIME;
}
#endif
#ifdef SUPPORT_HARD_LINKS
if (tmp_dev != -1) {
@@ -569,6 +598,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
}
if (xflags & XMIT_MOD_NSEC)
write_varint(f, F_MOD_NSEC(file));
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx && !(xflags & XMIT_CRTIME_EQ_MTIME))
write_varlong(f, crtime, 4);
#endif
if (!(xflags & XMIT_SAME_MODE))
write_int(f, to_wire_mode(mode));
if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
@@ -661,6 +694,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
{
static int64 modtime, atime;
#ifdef SUPPORT_CRTIMES
static time_t crtime;
#endif
static mode_t mode;
#ifdef SUPPORT_HARD_LINKS
static int64 dev;
@@ -679,8 +715,11 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
int alloc_len, basename_len, linkname_len;
int extra_len = file_extra_cnt * EXTRA_LEN;
int first_hlink_ndx = -1;
char real_ISREG_entry;
int64 file_length;
#ifdef CAN_SET_NSEC
uint32 modtime_nsec;
#endif
const char *basename;
struct file_struct *file;
alloc_pool_t *pool;
@@ -729,7 +768,7 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
if (*thisname
&& (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) {
rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname);
exit_cleanup(RERR_PROTOCOL);
exit_cleanup(RERR_UNSUPPORTED);
}
if (sanitize_paths)
@@ -767,23 +806,31 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
file_length = F_LENGTH(first);
modtime = first->modtime;
#ifdef CAN_SET_NSEC
modtime_nsec = F_MOD_NSEC_or_0(first);
#endif
mode = first->mode;
if (atimes_ndx && !S_ISDIR(mode))
atime = F_ATIME(first);
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx)
crtime = F_CRTIME(first);
#endif
if (preserve_uid)
uid = F_OWNER(first);
if (preserve_gid)
gid = F_GROUP(first);
if (preserve_devices && IS_DEVICE(mode)) {
uint32 *devp = F_RDEV_P(first);
rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
rdev_major = DEV_MAJOR(devp);
rdev = MAKEDEV(rdev_major, DEV_MINOR(devp));
extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
}
if (preserve_links && S_ISLNK(mode))
linkname_len = strlen(F_SYMLINK(first)) + 1;
else
linkname_len = 0;
real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
goto create_object;
}
}
@@ -801,14 +848,49 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
}
#endif
} else
modtime = read_int(f);
modtime = read_uint(f);
}
if (xflags & XMIT_MOD_NSEC)
modtime_nsec = read_varint(f);
#ifndef CAN_SET_NSEC
(void)read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
#else
modtime_nsec = read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
else
modtime_nsec = 0;
if (!(xflags & XMIT_SAME_MODE))
#endif
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx) {
if (xflags & XMIT_CRTIME_EQ_MTIME)
crtime = modtime;
else
crtime = read_varlong(f, 4);
#if SIZEOF_TIME_T < SIZEOF_INT64
if (!am_generator && (int64)(time_t)crtime != crtime) {
rprintf(FERROR_XFER,
"Create time value of %s truncated on receiver.\n",
lastname);
}
#endif
}
#endif
if (!(xflags & XMIT_SAME_MODE)) {
mode = from_wire_mode(read_int(f));
/* Reject modes whose type bits are not one of the standard
* file types; otherwise garbage mode values propagate through
* the file-type checks below unpredictably. mode 0 is the one
* legitimate exception: --delete-missing-args (missing_args==2)
* sends a missing arg as a mode-0 entry (IS_MISSING_FILE), the
* generator's delete signal (#910). */
if (mode != 0 || missing_args != 2) {
if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)
&& !S_ISCHR(mode) && !S_ISBLK(mode)
&& !S_ISFIFO(mode) && !S_ISSOCK(mode)) {
rprintf(FERROR, "invalid file mode 0%o for %s [%s]\n",
(unsigned)mode, lastname, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
}
}
if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
atime = read_varlong(f, 4);
#if SIZEOF_TIME_T < SIZEOF_INT64
@@ -892,10 +974,20 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
#endif
linkname_len = 0;
if (copy_devices && IS_DEVICE(mode)) {
/* This is impossible in the official release, but some pre-release patches
* didn't convert the device into a file before sending, so we'll do it here
* (even though the length is typically 0 and any checksum data is zeros). */
mode = S_IFREG | (mode & ACCESSPERMS);
modtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
real_ISREG_entry = 0;
} else
real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
#ifdef SUPPORT_HARD_LINKS
create_object:
if (preserve_hard_links) {
if (protocol_version < 28 && S_ISREG(mode))
if (protocol_version < 28 && real_ISREG_entry)
xflags |= XMIT_HLINKED;
if (xflags & XMIT_HLINKED)
extra_len += (inc_recurse+1) * EXTRA_LEN;
@@ -924,6 +1016,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
exit_cleanup(RERR_UNSUPPORTED);
}
if (*thisname == '/' ? thisname[1] != '.' || thisname[2] != '\0' : *thisname != '.' || thisname[1] != '\0') {
int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
&& filter_list.head && check_server_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
exit_cleanup(RERR_UNSUPPORTED);
}
if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) {
rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname);
exit_cleanup(RERR_UNSUPPORTED);
}
}
if (inc_recurse && S_ISDIR(mode)) {
if (one_file_system) {
/* Room to save the dir's device for -x */
@@ -988,6 +1093,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
}
if (atimes_ndx && !S_ISDIR(mode))
F_ATIME(file) = atime;
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx)
F_CRTIME(file) = crtime;
#endif
if (unsort_ndx)
F_NDX(file) = flist->used + flist->ndx_start;
@@ -1107,8 +1216,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
}
#endif
if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
if (S_ISREG(mode))
if (always_checksum && (real_ISREG_entry || protocol_version < 28)) {
if (real_ISREG_entry)
bp = F_SUM(file);
else {
/* Prior to 28, we get a useless set of nulls. */
@@ -1158,7 +1267,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
int extra_len = file_extra_cnt * EXTRA_LEN;
const char *basename;
alloc_pool_t *pool;
STRUCT_STAT st;
STRUCT_STAT st = {0};
char *bp;
if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) {
@@ -1307,9 +1416,25 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
linkname_len = 0;
#endif
if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
if (st.st_size == 0) {
int fd = do_open_checklinks(fname);
if (fd >= 0) {
st.st_size = get_device_size(fd, fname);
close(fd);
}
}
st.st_mode = S_IFREG | (st.st_mode & ACCESSPERMS);
st.st_mtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
}
#ifdef ST_MTIME_NSEC
if (st.ST_MTIME_NSEC && protocol_version >= 31)
extra_len += EXTRA_LEN;
{
uint32 nsec = wire_mtime_nsec_from_stat(&st);
if (nsec && protocol_version >= 31)
extra_len += EXTRA_LEN;
}
#endif
#if SIZEOF_CAPITAL_OFF_T >= 8
if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
@@ -1364,9 +1489,13 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
file->flags = flags;
file->modtime = st.st_mtime;
#ifdef ST_MTIME_NSEC
if (st.ST_MTIME_NSEC && protocol_version >= 31) {
file->flags |= FLAG_MOD_NSEC;
F_MOD_NSEC(file) = st.ST_MTIME_NSEC;
{
uint32 nsec = wire_mtime_nsec_from_stat(&st);
if (nsec && protocol_version >= 31) {
file->flags |= FLAG_MOD_NSEC;
F_MOD_NSEC(file) = nsec;
}
}
#endif
file->len32 = (uint32)st.st_size;
@@ -1385,6 +1514,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
file->flags |= FLAG_OWNED_BY_US;
if (atimes_ndx && !S_ISDIR(file->mode))
F_ATIME(file) = st.st_atime;
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx)
F_CRTIME(file) = get_create_time(fname, &st);
#endif
if (basename != thisname)
file->dirname = lastdir;
@@ -1892,7 +2025,7 @@ static void send_implied_dirs(int f, struct file_list *flist, char *fname,
memcpy(F_DIR_RELNAMES_P(lastpath_struct), &relname_list, sizeof relname_list);
}
rnpp = EXPAND_ITEM_LIST(relname_list, relnamecache *, 32);
*rnpp = (relnamecache*)new_array(char, sizeof (relnamecache) + len);
*rnpp = (relnamecache*)new_array(char, RELNAMECACHE_LEN + len + 1);
(*rnpp)->name_type = name_type;
strlcpy((*rnpp)->fname, limit+1, len + 1);
@@ -1962,10 +2095,9 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
}
if (name_type != NORMAL_NAME) {
STRUCT_STAT st;
if (name_type == MISSING_NAME)
memset(&st, 0, sizeof st);
else if (link_stat(fbuf, &st, 1) != 0) {
STRUCT_STAT st = {0};
if (name_type != MISSING_NAME && link_stat(fbuf, &st, 1) != 0) {
interpret_stat_error(fbuf, True);
continue;
}
@@ -2051,8 +2183,7 @@ void send_extra_file_list(int f, int at_least)
if (need_unsorted_flist) {
flist->sorted = new_array(struct file_struct *, flist->used);
memcpy(flist->sorted, flist->files,
flist->used * sizeof (struct file_struct*));
memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
} else
flist->sorted = flist->files;
@@ -2098,7 +2229,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
static const char *lastdir;
static int lastdir_len = -1;
int len, dirlen;
STRUCT_STAT st;
STRUCT_STAT st = {0};
char *p, *dir;
struct file_list *flist;
struct timeval start_tv, end_tv;
@@ -2132,8 +2263,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
#endif
flist = cur_flist = flist_new(0, "send_file_list");
flist_expand(flist, FLIST_START_LARGE);
if (inc_recurse) {
dir_flist = flist_new(FLIST_TEMP, "send_file_list");
flist_expand(dir_flist, FLIST_START_LARGE);
flags |= FLAG_DIVERT_DIRS;
} else
dir_flist = cur_flist;
@@ -2269,7 +2402,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
}
dirlen = dir ? strlen(dir) : 0;
if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
if (dirlen != lastdir_len || (dirlen && memcmp(lastdir, dir, dirlen) != 0)) {
if (!change_pathname(NULL, dir, -dirlen))
goto bad_path;
lastdir = pathname;
@@ -2405,8 +2538,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
* send them together in a single file-list. */
if (need_unsorted_flist) {
flist->sorted = new_array(struct file_struct *, flist->used);
memcpy(flist->sorted, flist->files,
flist->used * sizeof (struct file_struct*));
memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
} else
flist->sorted = flist->files;
flist_sort_and_clean(flist, 0);
@@ -2414,7 +2546,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
file_old_total += flist->used;
if (numeric_ids <= 0 && !inc_recurse)
send_id_list(f);
send_id_lists(f);
/* send the io_error flag */
if (protocol_version < 30)
@@ -2487,11 +2619,27 @@ struct file_list *recv_file_list(int f, int dir_ndx)
init_hard_links();
#endif
if (inc_recurse && dir_ndx >= 0) {
if (dir_ndx >= dir_flist->used) {
rprintf(FERROR_XFER, "rsync: refusing invalid dir_ndx %u >= %u\n", dir_ndx, dir_flist->used);
exit_cleanup(RERR_PROTOCOL);
}
struct file_struct *file = dir_flist->files[dir_ndx];
if (file->flags & FLAG_GOT_DIR_FLIST) {
rprintf(FERROR_XFER, "rsync: refusing malicious duplicate flist for dir %d\n", dir_ndx);
exit_cleanup(RERR_PROTOCOL);
}
file->flags |= FLAG_GOT_DIR_FLIST;
}
flist = flist_new(0, "recv_file_list");
flist_expand(flist, FLIST_START_LARGE);
if (inc_recurse) {
if (flist->ndx_start == 1)
if (flist->ndx_start == 1) {
dir_flist = flist_new(FLIST_TEMP, "recv_file_list");
flist_expand(dir_flist, FLIST_START_LARGE);
}
dstart = dir_flist->used;
} else {
dir_flist = flist;
@@ -2542,7 +2690,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
rprintf(FERROR,
"ABORTING due to invalid path from sender: %s/%s\n",
cur_dir, file->basename);
exit_cleanup(RERR_PROTOCOL);
exit_cleanup(RERR_UNSUPPORTED);
}
good_dirname = cur_dir;
}
@@ -2559,7 +2707,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
} else if (S_ISLNK(file->mode))
stats.num_symlinks++;
else if (IS_DEVICE(file->mode))
stats.num_symlinks++;
stats.num_devices++;
else
stats.num_specials++;
@@ -2587,8 +2735,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
* list unsorted for our exchange of index numbers with the
* other side (since their names may not sort the same). */
flist->sorted = new_array(struct file_struct *, flist->used);
memcpy(flist->sorted, flist->files,
flist->used * sizeof (struct file_struct*));
memcpy(flist->sorted, flist->files, flist->used * PTR_SIZE);
if (inc_recurse && dir_flist->used > dstart) {
static int dir_flist_malloced = 0;
if (dir_flist_malloced < dir_flist->malloced) {
@@ -2598,7 +2745,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
dir_flist_malloced = dir_flist->malloced;
}
memcpy(dir_flist->sorted + dstart, dir_flist->files + dstart,
(dir_flist->used - dstart) * sizeof (struct file_struct*));
(dir_flist->used - dstart) * PTR_SIZE);
fsort(dir_flist->sorted + dstart, dir_flist->used - dstart);
}
} else {
@@ -2730,28 +2877,28 @@ int flist_find(struct file_list *flist, struct file_struct *f)
* 1=match directories, 0=match non-directories, or -1=match either. */
int flist_find_name(struct file_list *flist, const char *fname, int want_dir_match)
{
struct { /* We have to create a temporary file_struct for the search. */
struct file_struct f;
char name_space[MAXPATHLEN];
} t;
static struct file_struct *f;
char fbuf[MAXPATHLEN];
const char *slash = strrchr(fname, '/');
const char *basename = slash ? slash+1 : fname;
memset(&t.f, 0, FILE_STRUCT_LEN);
memcpy((void *)t.f.basename, basename, strlen(basename)+1);
if (!f)
f = (struct file_struct*)new_array(char, FILE_STRUCT_LEN + MAXPATHLEN + 1);
memset(f, 0, FILE_STRUCT_LEN);
memcpy((void*)f->basename, basename, strlen(basename)+1);
if (slash) {
strlcpy(fbuf, fname, slash - fname + 1);
t.f.dirname = fbuf;
f->dirname = fbuf;
} else
t.f.dirname = NULL;
f->dirname = NULL;
t.f.mode = want_dir_match > 0 ? S_IFDIR : S_IFREG;
f->mode = want_dir_match > 0 ? S_IFDIR : S_IFREG;
if (want_dir_match < 0)
return flist_find_ignore_dirness(flist, &t.f);
return flist_find(flist, &t.f);
return flist_find_ignore_dirness(flist, f);
return flist_find(flist, f);
}
/* Search for an identically-named item in the file list. Differs from
@@ -2792,25 +2939,20 @@ void clear_file(struct file_struct *file)
}
/* Allocate a new file list. */
struct file_list *flist_new(int flags, char *msg)
static struct file_list *flist_new(int flags, const char *msg)
{
struct file_list *flist;
flist = new0(struct file_list);
if (flags & FLIST_TEMP) {
if (!(flist->file_pool = pool_create(SMALL_EXTENT, 0,
out_of_memory,
POOL_INTERN)))
if (!(flist->file_pool = pool_create(SMALL_EXTENT, 0, _out_of_memory, POOL_INTERN)))
out_of_memory(msg);
} else {
/* This is a doubly linked list with prev looping back to
* the end of the list, but the last next pointer is NULL. */
if (!first_flist) {
flist->file_pool = pool_create(NORMAL_EXTENT, 0,
out_of_memory,
POOL_INTERN);
if (!flist->file_pool)
if (!(flist->file_pool = pool_create(NORMAL_EXTENT, 0, _out_of_memory, POOL_INTERN)))
out_of_memory(msg);
flist->ndx_start = flist->flist_num = inc_recurse ? 1 : 0;
@@ -3060,8 +3202,8 @@ static void output_flist(struct file_list *flist)
} else
*uidbuf = '\0';
if (gid_ndx) {
static char parens[] = "(\0)\0\0\0";
char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
static const char parens[] = "(\0)\0\0\0";
const char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
snprintf(gidbuf, sizeof gidbuf, " gid=%s%u%s",
pp, F_GROUP(file), pp + 2);
} else

View File

@@ -4,7 +4,7 @@
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2023 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,16 +35,18 @@ extern int inc_recurse;
extern int relative_paths;
extern int implied_dirs;
extern int keep_dirlinks;
extern int write_devices;
extern int preserve_acls;
extern int preserve_xattrs;
extern int preserve_links;
extern int preserve_devices;
extern int write_devices;
extern int preserve_specials;
extern int preserve_hard_links;
extern int preserve_executability;
extern int preserve_perms;
extern int preserve_times;
extern int preserve_mtimes;
extern int omit_dir_times;
extern int omit_link_times;
extern int delete_mode;
extern int delete_before;
extern int delete_during;
@@ -64,6 +66,7 @@ extern int inplace;
extern int append_mode;
extern int make_backups;
extern int csum_length;
extern int xfer_sum_len;
extern int ignore_times;
extern int size_only;
extern OFF_T max_size;
@@ -84,7 +87,7 @@ extern int list_only;
extern int read_batch;
extern int write_batch;
extern int safe_symlinks;
extern long block_size; /* "long" because popt can't set an int32. */
extern int32 block_size;
extern int unsort_ndx;
extern int max_delete;
extern int force_delete;
@@ -112,10 +115,6 @@ static int need_retouch_dir_times;
static int need_retouch_dir_perms;
static const char *solo_file = NULL;
enum nonregtype {
TYPE_DIR, TYPE_SPECIAL, TYPE_DEVICE, TYPE_SYMLINK
};
/* Forward declarations. */
#ifdef SUPPORT_HARD_LINKS
static void handle_skipped_hlink(struct file_struct *file, int itemizing,
@@ -186,7 +185,8 @@ static int remember_delete(struct file_struct *file, const char *fname, int flag
static int read_delay_line(char *buf, int *flags_p)
{
static int read_pos = 0;
int j, len, mode;
unsigned int mode;
int j, len;
char *bp, *past_space;
while (1) {
@@ -230,11 +230,13 @@ static int read_delay_line(char *buf, int *flags_p)
*flags_p = 0;
if (sscanf(bp, "%x ", &mode) != 1) {
invalid_data:
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
return -1;
goto invalid_data;
}
past_space = strchr(bp, ' ') + 1;
past_space = strchr(bp, ' ');
if (!past_space) {
goto invalid_data;
}
past_space++;
len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
read_pos = j + 1;
@@ -248,6 +250,10 @@ static int read_delay_line(char *buf, int *flags_p)
memcpy(buf, past_space, len);
return mode;
invalid_data:
rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
return -1;
}
static void do_delayed_deletions(char *delbuf)
@@ -270,7 +276,7 @@ static void do_delayed_deletions(char *delbuf)
* MAXPATHLEN buffer with the name of the directory in it (the functions we
* call will append names onto the end, but the old dir value will be restored
* on exit). */
static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t fs_dev)
{
static int already_warned = 0;
static struct hashtable *dev_tbl;
@@ -305,12 +311,12 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
if (!dev_tbl)
dev_tbl = hashtable_create(16, HT_KEY64);
if (file->flags & FLAG_TOP_DIR) {
hashtable_find(dev_tbl, *fs_dev+1, "");
filesystem_dev = *fs_dev;
} else if (filesystem_dev != *fs_dev) {
if (!hashtable_find(dev_tbl, *fs_dev+1, NULL))
hashtable_find(dev_tbl, fs_dev+1, "");
filesystem_dev = fs_dev;
} else if (filesystem_dev != fs_dev) {
if (!hashtable_find(dev_tbl, fs_dev+1, NULL))
return;
filesystem_dev = *fs_dev; /* it's a prior top-dir dev */
filesystem_dev = fs_dev; /* it's a prior top-dir dev */
}
}
@@ -379,9 +385,9 @@ static void do_delete_pass(void)
|| !S_ISDIR(st.st_mode))
continue;
delete_in_dir(fbuf, file, &st.st_dev);
delete_in_dir(fbuf, file, st.st_dev);
}
delete_in_dir(NULL, NULL, &dev_zero);
delete_in_dir(NULL, NULL, dev_zero);
if (INFO_GTE(FLIST, 2) && !am_server)
rprintf(FINFO, " \r");
@@ -396,6 +402,19 @@ static inline int mtime_differs(STRUCT_STAT *stp, struct file_struct *file)
#endif
}
static inline int any_time_differs(stat_x *sxp, struct file_struct *file, UNUSED(const char *fname))
{
int differs = mtime_differs(&sxp->st, file);
#ifdef SUPPORT_CRTIMES
if (!differs && crtimes_ndx) {
if (sxp->crtime == 0)
sxp->crtime = get_create_time(fname, &sxp->st);
differs = !same_time(sxp->crtime, 0, F_CRTIME(file), 0);
}
#endif
return differs;
}
static inline int perms_differ(struct file_struct *file, stat_x *sxp)
{
if (preserve_perms)
@@ -450,7 +469,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
{
if (S_ISLNK(file->mode)) {
#ifdef CAN_SET_SYMLINK_TIMES
if (preserve_times & PRESERVE_LINK_TIMES && mtime_differs(&sxp->st, file))
if (preserve_mtimes && !omit_link_times && any_time_differs(sxp, file, fname))
return 0;
#endif
#ifdef CAN_CHMOD_SYMLINK
@@ -470,7 +489,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
return 0;
#endif
} else {
if (preserve_times && mtime_differs(&sxp->st, file))
if (preserve_mtimes && any_time_differs(sxp, file, fname))
return 0;
if (perms_differ(file, sxp))
return 0;
@@ -494,9 +513,9 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
const char *xname)
{
if (statret >= 0) { /* A from-dest-dir statret can == 1! */
int keep_time = !preserve_times ? 0
: S_ISDIR(file->mode) ? preserve_times & PRESERVE_DIR_TIMES
: S_ISLNK(file->mode) ? preserve_times & PRESERVE_LINK_TIMES
int keep_time = !preserve_mtimes ? 0
: S_ISDIR(file->mode) ? !omit_dir_times
: S_ISLNK(file->mode) ? !omit_link_times
: 1;
if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
@@ -512,7 +531,15 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
&& !same_time(F_ATIME(file), 0, sxp->st.st_atime, 0))
iflags |= ITEM_REPORT_ATIME;
#if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
#ifdef SUPPORT_CRTIMES
if (crtimes_ndx) {
if (sxp->crtime == 0)
sxp->crtime = get_create_time(fnamecmp, &sxp->st);
if (!same_time(sxp->crtime, 0, F_CRTIME(file), 0))
iflags |= ITEM_REPORT_CRTIME;
}
#endif
#ifndef CAN_CHMOD_SYMLINK
if (S_ISLNK(file->mode)) {
;
} else
@@ -578,30 +605,77 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
}
}
/* Perform our quick-check heuristic for determining if a file is unchanged. */
int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
static enum filetype get_file_type(mode_t mode)
{
if (st->st_size != F_LENGTH(file))
return 0;
/* if always checksum is set then we use the checksum instead
of the file time to determine whether to sync */
if (always_checksum > 0 && S_ISREG(st->st_mode)) {
char sum[MAX_DIGEST_LEN];
file_checksum(fn, st, sum);
return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
}
if (size_only > 0)
return 1;
if (ignore_times)
return 0;
return !mtime_differs(st, file);
if (S_ISREG(mode))
return FT_REG;
if (S_ISLNK(mode))
return FT_SYMLINK;
if (S_ISDIR(mode))
return FT_DIR;
if (IS_SPECIAL(mode))
return FT_SPECIAL;
if (IS_DEVICE(mode))
return FT_DEVICE;
return FT_UNSUPPORTED;
}
/* Perform our quick-check heuristic for determining if a file is unchanged. */
int quick_check_ok(enum filetype ftype, const char *fn, struct file_struct *file, STRUCT_STAT *st)
{
switch (ftype) {
case FT_REG:
if (st->st_size != F_LENGTH(file))
return 0;
/* If always_checksum is set then we use the checksum instead
* of the file mtime to determine whether to sync. */
if (always_checksum > 0) {
char sum[MAX_DIGEST_LEN];
file_checksum(fn, st, sum);
return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
}
if (size_only > 0)
return 1;
if (ignore_times)
return 0;
if (mtime_differs(st, file))
return 0;
break;
case FT_DIR:
break;
case FT_SYMLINK: {
#ifdef SUPPORT_LINKS
char lnk[MAXPATHLEN];
int len = do_readlink(fn, lnk, MAXPATHLEN-1);
if (len <= 0)
return 0;
lnk[len] = '\0';
if (strcmp(lnk, F_SYMLINK(file)) != 0)
return 0;
break;
#else
return -1;
#endif
}
case FT_SPECIAL:
if (!BITS_EQUAL(file->mode, st->st_mode, _S_IFMT))
return 0;
break;
case FT_DEVICE: {
uint32 *devp = F_RDEV_P(file);
if (st->st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
return 0;
break;
}
case FT_UNSUPPORTED:
return -1;
}
return 1;
}
/*
* set (initialize) the size entries in the per-file sum_struct
@@ -624,6 +698,11 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
{
int32 blength;
int s2length;
/* The strong sum can be no longer than the negotiated checksum digest:
* a short checksum (e.g. xxh64 = 8 bytes, when xxh128/xxh3 are absent)
* makes xfer_sum_len < SUM_LENGTH, and the sender rejects an s2length
* larger than xfer_sum_len (io.c). */
int max_s2length = MIN(SUM_LENGTH, xfer_sum_len);
int64 l;
if (len < 0) {
@@ -658,7 +737,7 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
if (protocol_version < 27) {
s2length = csum_length;
} else if (csum_length == SUM_LENGTH) {
s2length = SUM_LENGTH;
s2length = max_s2length;
} else {
int32 c;
int b = BLOCKSUM_BIAS;
@@ -667,7 +746,7 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
/* add a bit, subtract rollsum, round up. */
s2length = (b + 1 - 32 + 7) / 8; /* --optimize in compiler-- */
s2length = MAX(s2length, csum_length);
s2length = MIN(s2length, SUM_LENGTH);
s2length = MIN(s2length, max_s2length);
}
sum->flength = len;
@@ -716,7 +795,7 @@ static int generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy)
for (i = 0; i < sum.count; i++) {
int32 n1 = (int32)MIN(len, (OFF_T)sum.blength);
char *map = map_ptr(mapbuf, offset, n1);
char sum2[SUM_LENGTH];
char sum2[MAX_DIGEST_LEN];
uint32 sum1;
len -= n1;
@@ -808,9 +887,12 @@ static struct file_struct *find_fuzzy(struct file_struct *file, struct file_list
len = strlen(name);
suf = find_filename_suffix(name, len, &suf_len);
dist = fuzzy_distance(name, len, fname, fname_len);
/* Add some extra weight to how well the suffixes match. */
dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len) * 10;
dist = fuzzy_distance(name, len, fname, fname_len, lowest_dist);
/* Add some extra weight to how well the suffixes match unless we've already disqualified
* this file based on a heuristic. */
if (dist < 0xFFFF0000U) {
dist += fuzzy_distance(suf, suf_len, fname_suf, fname_suf_len, 0xFFFF0000U) * 10;
}
if (DEBUG_GTE(FUZZY, 2)) {
rprintf(FINFO, "fuzzy distance for %s = %d.%05d\n",
f_name(fp, NULL), (int)(dist>>16), (int)(dist&0xFFFF));
@@ -886,7 +968,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
best_match = j;
match_level = 1;
}
if (!unchanged_file(cmpbuf, file, &sxp->st))
if (!quick_check_ok(FT_REG, cmpbuf, file, &sxp->st))
continue;
if (match_level == 1) {
best_match = j;
@@ -914,7 +996,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
if (find_exact_for_existing) {
if (alt_dest_type == LINK_DEST && real_st.st_dev == sxp->st.st_dev && real_st.st_ino == sxp->st.st_ino)
return -1;
if (do_unlink(fname) < 0 && errno != ENOENT)
if (do_unlink_at(fname) < 0 && errno != ENOENT)
goto got_nothing_for_ya;
}
#ifdef SUPPORT_HARD_LINKS
@@ -985,29 +1067,14 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
{
int best_match = -1;
int match_level = 0;
enum nonregtype type;
uint32 *devp;
#ifdef SUPPORT_LINKS
char lnk[MAXPATHLEN];
int len;
#endif
enum filetype ftype = get_file_type(file->mode);
int j = 0;
#ifndef SUPPORT_LINKS
if (S_ISLNK(file->mode))
if (ftype == FT_SYMLINK)
return -1;
#endif
if (S_ISDIR(file->mode)) {
type = TYPE_DIR;
} else if (IS_SPECIAL(file->mode))
type = TYPE_SPECIAL;
else if (IS_DEVICE(file->mode))
type = TYPE_DEVICE;
#ifdef SUPPORT_LINKS
else if (S_ISLNK(file->mode))
type = TYPE_SYMLINK;
#endif
else {
if (ftype == FT_REG || ftype == FT_UNSUPPORTED) {
rprintf(FERROR,
"internal: try_dests_non() called with invalid mode (%o)\n",
(int)file->mode);
@@ -1018,53 +1085,14 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
if (link_stat(cmpbuf, &sxp->st, 0) < 0)
continue;
switch (type) {
case TYPE_DIR:
if (!S_ISDIR(sxp->st.st_mode))
continue;
break;
case TYPE_SPECIAL:
if (!IS_SPECIAL(sxp->st.st_mode))
continue;
break;
case TYPE_DEVICE:
if (!IS_DEVICE(sxp->st.st_mode))
continue;
break;
case TYPE_SYMLINK:
#ifdef SUPPORT_LINKS
if (!S_ISLNK(sxp->st.st_mode))
continue;
break;
#else
return -1;
#endif
}
if (ftype != get_file_type(sxp->st.st_mode))
continue;
if (match_level < 1) {
match_level = 1;
best_match = j;
}
switch (type) {
case TYPE_DIR:
case TYPE_SPECIAL:
break;
case TYPE_DEVICE:
devp = F_RDEV_P(file);
if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
continue;
break;
case TYPE_SYMLINK:
#ifdef SUPPORT_LINKS
if ((len = do_readlink(cmpbuf, lnk, MAXPATHLEN-1)) <= 0)
continue;
lnk[len] = '\0';
if (strcmp(lnk, F_SYMLINK(file)) != 0)
continue;
break;
#else
return -1;
#endif
}
if (!quick_check_ok(ftype, cmpbuf, file, &sxp->st))
continue;
if (match_level < 2) {
match_level = 2;
best_match = j;
@@ -1096,7 +1124,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
&& !IS_SPECIAL(file->mode) && !IS_DEVICE(file->mode)
#endif
&& !S_ISDIR(file->mode)) {
if (do_link(cmpbuf, fname) < 0) {
if (do_link_at(cmpbuf, fname) < 0) {
rsyserr(FERROR_XFER, errno,
"failed to hard-link %s with %s",
cmpbuf, fname);
@@ -1109,14 +1137,14 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
match_level = 2;
if (itemizing && stdout_format_has_i
&& (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
int chg = alt_dest_type == COMPARE_DEST && type != TYPE_DIR ? 0
int chg = alt_dest_type == COMPARE_DEST && ftype != FT_DIR ? 0
: ITEM_LOCAL_CHANGE + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
char *lp = match_level == 3 ? "" : NULL;
itemize(cmpbuf, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
}
if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT) {
rprintf(FCLIENT, "%s%s is uptodate\n",
fname, type == TYPE_DIR ? "/" : "");
fname, ftype == FT_DIR ? "/" : "");
}
return -2;
}
@@ -1131,6 +1159,7 @@ static void list_file_entry(struct file_struct *f)
int size_width = human_readable ? 14 : 11;
int mtime_width = 1 + strlen(mtime_str);
int atime_width = atimes_ndx ? mtime_width : 0;
int crtime_width = crtimes_ndx ? mtime_width : 0;
if (!F_IS_ACTIVE(f)) {
/* this can happen if duplicate names were removed */
@@ -1141,10 +1170,11 @@ static void list_file_entry(struct file_struct *f)
if (missing_args == 2 && f->mode == 0) {
rprintf(FINFO, "%-*s %s\n",
10 + 1 + size_width + mtime_width + atime_width, "*missing",
10 + 1 + size_width + mtime_width + atime_width + crtime_width, "*missing",
f_name(f, NULL));
} else {
const char *atime_str = atimes_ndx && !S_ISDIR(f->mode) ? timestring(F_ATIME(f)) : "";
const char *crtime_str = crtimes_ndx ? timestring(F_CRTIME(f)) : "";
const char *arrow, *lnk;
permstring(permbuf, f->mode);
@@ -1157,9 +1187,9 @@ static void list_file_entry(struct file_struct *f)
#endif
arrow = lnk = "";
rprintf(FINFO, "%s %*s %s%*s %s%s%s\n",
rprintf(FINFO, "%s %*s %s%*s%*s %s%s%s\n",
permbuf, size_width, human_num(F_LENGTH(f)),
timestring(f->modtime), atime_width, atime_str,
timestring(f->modtime), atime_width, atime_str, crtime_width, crtime_str,
f_name(f, NULL), arrow, lnk);
}
}
@@ -1208,7 +1238,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
char fnamecmpbuf[MAXPATHLEN];
uchar fnamecmp_type;
int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
int is_dir = !S_ISDIR(file->mode) ? 0
enum filetype stype, ftype = get_file_type(file->mode);
int is_dir = ftype != FT_DIR ? 0
: inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
: 1;
@@ -1277,21 +1308,26 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
* this function was asked to process in the file list. */
if (!inc_recurse
&& (*dn != '.' || dn[1]) /* Avoid an issue with --relative and the "." dir. */
&& (!prior_dir_file || strcmp(dn, f_name(prior_dir_file, NULL)) != 0)
&& flist_find_name(cur_flist, dn, 1) < 0) {
&& (!prior_dir_file || strcmp(dn, f_name(prior_dir_file, NULL)) != 0)) {
int ok = 0, j = flist_find_name(cur_flist, dn, -1);
if (j >= 0) {
struct file_struct *f = cur_flist->sorted[j];
if (S_ISDIR(f->mode) || (missing_args == 2 && !file->mode && !f->mode))
ok = 1;
}
/* The --delete-missing-args option can actually put invalid entries into
* the file list, so if that option was specified, we'll just complain about
* it and allow it. */
if (missing_args == 2 && file->mode == 0)
if (!ok && missing_args == 2 && file->mode == 0 && j < 0)
rprintf(FERROR, "WARNING: parent dir is absent in the file list: %s\n", dn);
else {
else if (!ok) {
rprintf(FERROR, "ABORTING due to invalid path from sender: %s/%s\n",
dn, file->basename);
exit_cleanup(RERR_PROTOCOL);
}
}
if (relative_paths && !implied_dirs
&& do_stat(dn, &sx.st) < 0) {
if (relative_paths && !implied_dirs && file->mode != 0
&& do_stat_at(dn, &sx.st) < 0) {
if (dry_run)
goto parent_is_dry_missing;
if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) {
@@ -1351,10 +1387,27 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
&& !am_root && sx.st.st_uid == our_uid)
del_opts |= DEL_NO_UID_WRITE;
if (statret == 0)
stype = get_file_type(sx.st.st_mode);
else
stype = FT_UNSUPPORTED;
if (ignore_existing > 0 && statret == 0
&& (!is_dir || !S_ISDIR(sx.st.st_mode))) {
if (INFO_GTE(SKIP, 1) && is_dir >= 0)
rprintf(FINFO, "%s exists\n", fname);
&& (!is_dir || stype != FT_DIR)) {
if (INFO_GTE(SKIP, 1) && is_dir >= 0) {
const char *suf = "";
if (INFO_GTE(SKIP, 2)) {
if (ftype != stype)
suf = " (type change)";
else if (!quick_check_ok(ftype, fname, file, &sx.st))
suf = always_checksum ? " (sum change)" : " (file change)";
else if (!unchanged_attrs(fname, file, &sx))
suf = " (attr change)";
else
suf = " (uptodate)";
}
rprintf(FINFO, "%s exists%s\n", fname, suf);
}
#ifdef SUPPORT_HARD_LINKS
if (F_IS_HLINKED(file))
handle_skipped_hlink(file, itemizing, code, f_out);
@@ -1375,7 +1428,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
} else
added_perms = 0;
if (is_dir < 0) {
if (!(preserve_times & PRESERVE_DIR_TIMES))
if (!preserve_mtimes || omit_dir_times)
goto cleanup;
/* In inc_recurse mode we want to make sure any missing
* directories get created while we're still processing
@@ -1383,10 +1436,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
* dir's mtime right away). We will handle the dir in
* full later (right before we handle its contents). */
if (statret == 0
&& (S_ISDIR(sx.st.st_mode)
&& (stype == FT_DIR
|| delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0))
goto cleanup; /* Any errors get reported later. */
if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0)
if (do_mkdir_at(fname, (file->mode|added_perms) & 0700) == 0)
file->flags |= FLAG_DIR_CREATED;
goto cleanup;
}
@@ -1395,7 +1448,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
* file of that name and it is *not* a directory, then
* we need to delete it. If it doesn't exist, then
* (perhaps recursively) create it. */
if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
if (statret == 0 && stype != FT_DIR) {
if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0)
goto skipping_dir_contents;
statret = -1;
@@ -1428,10 +1481,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
itemize(fnamecmp, file, ndx, statret, &sx,
statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
}
if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
if (real_ret != 0 && do_mkdir_at(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
if (!relative_paths || errno != ENOENT
|| make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0
|| (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
|| (do_mkdir_at(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
rsyserr(FERROR_XFER, errno,
"recv_generator: mkdir %s failed",
full_fname(fname));
@@ -1458,7 +1511,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
#ifdef HAVE_CHMOD
if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
mode_t mode = file->mode | S_IRWXU;
if (do_chmod(fname, mode) < 0) {
if (do_chmod_at(fname, mode) < 0) {
rsyserr(FERROR_XFER, errno,
"failed to modify permissions on %s",
full_fname(fname));
@@ -1479,7 +1532,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
else if (delete_during && f_out != -1 && !phase
&& !(file->flags & FLAG_MISSING_DIR)) {
if (file->flags & FLAG_CONTENT_DIR)
delete_in_dir(fname, file, &real_sx.st.st_dev);
delete_in_dir(fname, file, real_sx.st.st_dev);
else
change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
}
@@ -1490,7 +1543,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
/* If we're not preserving permissions, change the file-list's
* mode based on the local permissions and some heuristics. */
if (!preserve_perms) {
int exists = statret == 0 && !S_ISDIR(sx.st.st_mode);
int exists = statret == 0 && stype != FT_DIR;
file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, exists);
}
@@ -1500,7 +1553,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
#endif
if (preserve_links && S_ISLNK(file->mode)) {
if (preserve_links && ftype == FT_SYMLINK) {
#ifdef SUPPORT_LINKS
const char *sl = F_SYMLINK(file);
if (safe_symlinks && unsafe_symlink(sl, fname)) {
@@ -1517,12 +1570,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
}
if (statret == 0) {
char lnk[MAXPATHLEN];
int len;
if (S_ISLNK(sx.st.st_mode)
&& (len = do_readlink(fname, lnk, MAXPATHLEN-1)) > 0
&& strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
if (stype == FT_SYMLINK && quick_check_ok(stype, fname, file, &sx.st)) {
/* The link is pointing to the right place. */
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
if (itemizing)
@@ -1555,7 +1603,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
if (atomic_create(file, fname, sl, NULL, MAKEDEV(0, 0), &sx, statret == 0 ? DEL_FOR_SYMLINK : 0)) {
set_file_attrs(fname, file, NULL, NULL, 0);
if (itemizing) {
if (statret == 0 && !S_ISLNK(sx.st.st_mode))
if (statret == 0 && stype != FT_SYMLINK)
statret = -1;
itemize(fnamecmp, file, ndx, statret, &sx,
ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
@@ -1576,28 +1624,22 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
}
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|| (preserve_specials && IS_SPECIAL(file->mode))) {
if ((am_root && preserve_devices && ftype == FT_DEVICE)
|| (preserve_specials && ftype == FT_SPECIAL)) {
dev_t rdev;
int del_for_flag = 0;
if (IS_DEVICE(file->mode)) {
int del_for_flag;
if (ftype == FT_DEVICE) {
uint32 *devp = F_RDEV_P(file);
rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
} else
del_for_flag = DEL_FOR_DEVICE;
} else {
rdev = 0;
del_for_flag = DEL_FOR_SPECIAL;
}
if (statret == 0) {
if (IS_DEVICE(file->mode)) {
if (!IS_DEVICE(sx.st.st_mode))
statret = -1;
del_for_flag = DEL_FOR_DEVICE;
} else {
if (!IS_SPECIAL(sx.st.st_mode))
statret = -1;
del_for_flag = DEL_FOR_SPECIAL;
}
if (statret == 0
&& BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
&& (IS_SPECIAL(sx.st.st_mode) || sx.st.st_rdev == rdev)) {
if (ftype != stype)
statret = -1;
else if (quick_check_ok(ftype, fname, file, &sx.st)) {
/* The device or special file is identical. */
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
if (itemizing)
@@ -1650,10 +1692,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
}
if (!S_ISREG(file->mode)) {
if (solo_file)
fname = f_name(file, NULL);
rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
if (ftype != FT_REG) {
if (INFO_GTE(NONREG, 1)) {
if (solo_file)
fname = f_name(file, NULL);
rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
}
goto cleanup;
}
@@ -1674,7 +1718,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
}
if (update_only > 0 && statret == 0 && file->modtime - sx.st.st_mtime <= modify_window) {
if (update_only > 0 && statret == 0 && stype == ftype
&& file->modtime - sx.st.st_mtime < modify_window) {
if (INFO_GTE(SKIP, 1))
rprintf(FINFO, "%s is newer\n", fname);
#ifdef SUPPORT_HARD_LINKS
@@ -1686,7 +1731,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
fnamecmp_type = FNAMECMP_FNAME;
if (statret == 0 && !(S_ISREG(sx.st.st_mode) || (write_devices && IS_DEVICE(sx.st.st_mode)))) {
if (statret == 0 && !(stype == FT_REG || (write_devices && stype == FT_DEVICE))) {
if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_FILE) != 0)
goto cleanup;
statret = -1;
@@ -1720,7 +1765,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
partialptr = NULL;
if (statret != 0 && fuzzy_basis) {
if (need_fuzzy_dirlist && S_ISREG(file->mode)) {
if (need_fuzzy_dirlist) {
const char *dn = file->dirname ? file->dirname : ".";
int i;
strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
@@ -1764,13 +1809,19 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
}
if (write_devices && IS_DEVICE(sx.st.st_mode) && sx.st.st_size == 0) {
/* This early open into fd skips the regular open below. */
if ((fd = do_open_nofollow(fnamecmp, O_RDONLY)) >= 0)
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
}
if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
;
else if (fnamecmp_type >= FNAMECMP_FUZZY)
;
else if (unchanged_file(fnamecmp, file, &sx.st)) {
else if (quick_check_ok(FT_REG, fnamecmp, file, &sx.st)) {
if (partialptr) {
do_unlink(partialptr);
do_unlink_at(partialptr);
handle_partial_dir(partialptr, PDIR_DELETE);
}
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT | maybe_ATTRS_ACCURATE_TIME);
@@ -1784,7 +1835,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
return_with_success:
if (!dry_run)
send_msg_int(MSG_SUCCESS, ndx);
send_msg_success(fname, ndx);
goto cleanup;
}
@@ -1829,7 +1880,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
/* open the file */
if ((fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
if (fd < 0 && (fd = do_open_checklinks(fnamecmp)) < 0) {
rsyserr(FERROR, errno, "failed to open %s, continuing",
full_fname(fnamecmp));
pretend_missing:
@@ -1846,11 +1897,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
if (!(backupptr = get_backup_name(fname))) {
close(fd);
goto cleanup;
}
if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
close(fd);
goto pretend_missing;
}
if (robust_unlink(backupptr) && errno != ENOENT) {
@@ -1858,14 +1907,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
full_fname(backupptr));
unmake_file(back_file);
back_file = NULL;
close(fd);
goto cleanup;
}
if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
if ((f_copy = do_open_at(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
rsyserr(FERROR_XFER, errno, "open %s", full_fname(backupptr));
unmake_file(back_file);
back_file = NULL;
close(fd);
goto cleanup;
}
fnamecmp_type = FNAMECMP_BACKUP;
@@ -1916,7 +1963,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
write_sum_head(f_out, NULL);
else if (sx.st.st_size <= 0) {
write_sum_head(f_out, NULL);
close(fd);
} else {
if (generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy) < 0) {
rprintf(FWARNING,
@@ -1924,10 +1970,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
fnamecmp);
write_sum_head(f_out, NULL);
}
close(fd);
}
cleanup:
if (fd >= 0)
close(fd);
if (back_file) {
int save_preserve_xattrs = preserve_xattrs;
if (f_copy >= 0)
@@ -1982,7 +2029,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
if (slnk) {
#ifdef SUPPORT_LINKS
if (do_symlink(slnk, create_name) < 0) {
if (do_symlink_at(slnk, create_name) < 0) {
rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
full_fname(create_name), slnk);
return 0;
@@ -1998,7 +2045,7 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
return 0;
#endif
} else {
if (do_mknod(create_name, file->mode, rdev) < 0) {
if (do_mknod_at(create_name, file->mode, rdev) < 0) {
rsyserr(FERROR_XFER, errno, "mknod %s failed",
full_fname(create_name));
return 0;
@@ -2006,10 +2053,14 @@ int atomic_create(struct file_struct *file, char *fname, const char *slnk, const
}
if (!skip_atomic) {
if (do_rename(tmpname, fname) < 0) {
if (do_rename_at(tmpname, fname) < 0) {
char *full_tmpname = strdup(full_fname(tmpname));
if (full_tmpname == NULL)
out_of_memory("atomic_create");
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed",
full_fname(tmpname), full_fname(fname));
do_unlink(tmpname);
full_tmpname, full_fname(fname));
free(full_tmpname);
do_unlink_at(tmpname);
return 0;
}
}
@@ -2073,7 +2124,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
continue;
fname = f_name(file, NULL);
if (fix_dir_perms)
do_chmod(fname, file->mode);
do_chmod_at(fname, file->mode);
if (need_retouch_dir_times) {
STRUCT_STAT st;
if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
@@ -2108,6 +2159,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
if (send_failed)
ndx = get_hlink_num();
flist = flist_for_ndx(ndx, "check_for_finished_files.1");
if (ndx < flist->ndx_start)
exit_cleanup(RERR_PROTOCOL);
file = flist->files[ndx - flist->ndx_start];
assert(file->flags & FLAG_HLINKED);
if (send_failed)
@@ -2136,6 +2189,8 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
flist = cur_flist;
cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2");
if (ndx < cur_flist->ndx_start)
exit_cleanup(RERR_PROTOCOL);
file = cur_flist->files[ndx - cur_flist->ndx_start];
if (solo_file)
@@ -2213,7 +2268,7 @@ void generate_files(int f_out, const char *local_name)
}
solo_file = local_name;
dir_tweaking = !(list_only || solo_file || dry_run);
need_retouch_dir_times = preserve_times & PRESERVE_DIR_TIMES;
need_retouch_dir_times = preserve_mtimes && !omit_dir_times;
loopchk_limit = allowed_lull ? allowed_lull * 5 : 200;
symlink_timeset_failed_flags = ITEM_REPORT_TIME
| (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0);
@@ -2266,7 +2321,7 @@ void generate_files(int f_out, const char *local_name)
dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
} else
dirdev = MAKEDEV(0, 0);
delete_in_dir(fbuf, fp, &dirdev);
delete_in_dir(fbuf, fp, dirdev);
} else
change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
}
@@ -2313,7 +2368,7 @@ void generate_files(int f_out, const char *local_name)
} while ((cur_flist = cur_flist->next) != NULL);
if (delete_during)
delete_in_dir(NULL, NULL, &dev_zero);
delete_in_dir(NULL, NULL, dev_zero);
phase++;
if (DEBUG_GTE(GENR, 1))
rprintf(FINFO, "generate_files phase=%d\n", phase);
@@ -2336,7 +2391,7 @@ void generate_files(int f_out, const char *local_name)
write_ndx(f_out, NDX_DONE);
if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG()) {
if ((INFO_GTE(STATS, 2) && (delete_mode || force_delete)) || read_batch)
if (delete_mode || force_delete || read_batch)
write_del_stats(f_out);
if (EARLY_DELAY_DONE_MSG()) /* Can't send this before delay */
write_ndx(f_out, NDX_DONE);
@@ -2381,7 +2436,7 @@ void generate_files(int f_out, const char *local_name)
if (protocol_version >= 31) {
if (!EARLY_DELETE_DONE_MSG()) {
if (INFO_GTE(STATS, 2) || read_batch)
if (delete_mode || force_delete || read_batch)
write_del_stats(f_out);
write_ndx(f_out, NDX_DONE);
}

View File

@@ -1,7 +1,7 @@
/*
* Routines to provide a memory-efficient hashtable.
*
* Copyright (C) 2007-2020 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -350,6 +350,9 @@ void *hashtable_find(struct hashtable *tbl, int64 key, void *data_when_new)
-------------------------------------------------------------------------------
*/
#define NON_ZERO_32(x) ((x) ? (x) : (uint32_t)1)
#define NON_ZERO_64(x, y) ((x) || (y) ? (y) | (int64)(x) << 32 | (y) : (int64)1)
uint32_t hashlittle(const void *key, size_t length)
{
uint32_t a,b,c; /* internal state */
@@ -390,7 +393,7 @@ uint32_t hashlittle(const void *key, size_t length)
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
case 1 : a+=k8[0]; break;
case 0 : return c;
case 0 : return NON_ZERO_32(c);
}
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
@@ -436,7 +439,7 @@ uint32_t hashlittle(const void *key, size_t length)
break;
case 1 : a+=k8[0];
break;
case 0 : return c; /* zero length requires no mixing */
case 0 : return NON_ZERO_32(c); /* zero length requires no mixing */
}
} else { /* need to read the key one byte at a time */
@@ -489,10 +492,171 @@ uint32_t hashlittle(const void *key, size_t length)
/* FALLTHROUGH */
case 1 : a+=k[0];
break;
case 0 : return c;
case 0 : return NON_ZERO_32(c);
}
}
final(a,b,c);
return c;
return NON_ZERO_32(c);
}
#if SIZEOF_INT64 >= 8
/*
* hashlittle2: return 2 32-bit hash values joined into an int64.
*
* This is identical to hashlittle(), except it returns two 32-bit hash
* values instead of just one. This is good enough for hash table
* lookup with 2^^64 buckets, or if you want a second hash if you're not
* happy with the first, or if you want a probably-unique 64-bit ID for
* the key. *pc is better mixed than *pb, so use *pc first. If you want
* a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
*/
int64 hashlittle2(const void *key, size_t length)
{
uint32_t a,b,c; /* internal state */
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length);
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
const uint8_t *k8;
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
b += k[1];
c += k[2];
mix(a,b,c);
length -= 12;
k += 3;
}
/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]; break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
case 1 : a+=k8[0]; break;
case 0 : return NON_ZERO_64(b, c);
}
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
const uint8_t *k8;
/*--------------- all but last block: aligned reads and different mixing */
while (length > 12)
{
a += k[0] + (((uint32_t)k[1])<<16);
b += k[2] + (((uint32_t)k[3])<<16);
c += k[4] + (((uint32_t)k[5])<<16);
mix(a,b,c);
length -= 12;
k += 6;
}
/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=k[4];
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=k[2];
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=k[0];
break;
case 1 : a+=k8[0];
break;
case 0 : return NON_ZERO_64(b, c); /* zero length strings require no mixing */
}
} else { /* need to read the key one byte at a time */
const uint8_t *k = (const uint8_t *)key;
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
a += ((uint32_t)k[1])<<8;
a += ((uint32_t)k[2])<<16;
a += ((uint32_t)k[3])<<24;
b += k[4];
b += ((uint32_t)k[5])<<8;
b += ((uint32_t)k[6])<<16;
b += ((uint32_t)k[7])<<24;
c += k[8];
c += ((uint32_t)k[9])<<8;
c += ((uint32_t)k[10])<<16;
c += ((uint32_t)k[11])<<24;
mix(a,b,c);
length -= 12;
k += 12;
}
/*-------------------------------- last block: affect all 32 bits of (c) */
switch(length) /* all the case statements fall through */
{
case 12: c+=((uint32_t)k[11])<<24;
/* FALLTHROUGH */
case 11: c+=((uint32_t)k[10])<<16;
/* FALLTHROUGH */
case 10: c+=((uint32_t)k[9])<<8;
/* FALLTHROUGH */
case 9 : c+=k[8];
/* FALLTHROUGH */
case 8 : b+=((uint32_t)k[7])<<24;
/* FALLTHROUGH */
case 7 : b+=((uint32_t)k[6])<<16;
/* FALLTHROUGH */
case 6 : b+=((uint32_t)k[5])<<8;
/* FALLTHROUGH */
case 5 : b+=k[4];
/* FALLTHROUGH */
case 4 : a+=((uint32_t)k[3])<<24;
/* FALLTHROUGH */
case 3 : a+=((uint32_t)k[2])<<16;
/* FALLTHROUGH */
case 2 : a+=((uint32_t)k[1])<<8;
/* FALLTHROUGH */
case 1 : a+=k[0];
break;
case 0 : return NON_ZERO_64(b, c);
}
}
final(a,b,c);
return NON_ZERO_64(b, c);
}
#else
#define hashlittle2(key, len) hashlittle(key, len)
#endif

12
hlink.c
View File

@@ -4,7 +4,7 @@
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2004-2020 Wayne Davison
* Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -117,7 +117,7 @@ static void match_gnums(int32 *ndx_list, int ndx_count)
struct ht_int32_node *node = NULL;
int32 gnum, gnum_next;
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)()) hlink_compare_gnum);
qsort(ndx_list, ndx_count, sizeof ndx_list[0], (int (*)(const void*, const void*))hlink_compare_gnum);
for (from = 0; from < ndx_count; from++) {
file = hlink_flist->sorted[ndx_list[from]];
@@ -406,7 +406,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
}
break;
}
if (!unchanged_file(cmpbuf, file, &alt_sx.st))
if (!quick_check_ok(FT_REG, cmpbuf, file, &alt_sx.st))
continue;
statret = 1;
if (unchanged_attrs(cmpbuf, file, &alt_sx))
@@ -446,7 +446,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
return -1;
if (remove_source_files == 1 && do_xfers)
send_msg_int(MSG_SUCCESS, ndx);
send_msg_success(fname, ndx);
return 1;
}
@@ -454,7 +454,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
int hard_link_one(struct file_struct *file, const char *fname,
const char *oldname, int terse)
{
if (do_link(oldname, fname) < 0) {
if (do_link_at(oldname, fname) < 0) {
enum logcode code;
if (terse) {
if (!INFO_GTE(NAME, 1))
@@ -519,7 +519,7 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
if (val < 0)
continue;
if (remove_source_files == 1 && do_xfers)
send_msg_int(MSG_SUCCESS, ndx);
send_msg_success(fname, ndx);
}
if (inc_recurse) {

View File

@@ -1,6 +1,6 @@
/* Inline functions for rsync.
*
* Copyright (C) 2007-2020 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -75,6 +75,7 @@ d_name(struct dirent *di)
static inline void
init_stat_x(stat_x *sx_p)
{
sx_p->crtime = 0;
#ifdef SUPPORT_ACLS
sx_p->acc_acl = sx_p->def_acl = NULL;
#endif
@@ -105,7 +106,7 @@ free_stat_x(stat_x *sx_p)
static inline char *my_strdup(const char *str, const char *file, int line)
{
int len = strlen(str)+1;
char *buf = my_alloc(do_malloc, len, 1, file, line);
char *buf = my_alloc(NULL, len, 1, file, line);
memcpy(buf, str, len);
return buf;
}

View File

@@ -115,7 +115,7 @@ else
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
if [ -f $src ] || [ -d $src ]
then
true
else

294
io.c
View File

@@ -4,7 +4,7 @@
* Copyright (C) 1996-2001 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,6 +41,7 @@ extern int am_server;
extern int am_sender;
extern int am_receiver;
extern int am_generator;
extern int local_server;
extern int msgs2stderr;
extern int inc_recurse;
extern int io_error;
@@ -54,12 +55,15 @@ extern int read_batch;
extern int compat_flags;
extern int protect_args;
extern int checksum_seed;
extern int xfer_sum_len;
extern int daemon_connection;
extern int protocol_version;
extern int remove_source_files;
extern int preserve_hard_links;
extern BOOL extra_flist_sending_enabled;
extern BOOL flush_ok_after_signal;
extern struct stats stats;
extern time_t stop_at_utime;
extern struct file_list *cur_flist;
#ifdef ICONV_OPTION
extern int filesfrom_convert;
@@ -82,6 +86,8 @@ int sock_f_out = -1;
int64 total_data_read = 0;
int64 total_data_written = 0;
char num_dev_ino_buf[4 + 8 + 8];
static struct {
xbuf in, out, msg;
int in_fd;
@@ -111,7 +117,7 @@ static int active_filecnt = 0;
static OFF_T active_bytecnt = 0;
static int first_message = 1;
static char int_byte_extra[64] = {
static const char int_byte_extra[64] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (00 - 3F)/4 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (40 - 7F)/4 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (80 - BF)/4 */
@@ -262,15 +268,18 @@ static size_t safe_read(int fd, char *buf, size_t len)
rprintf(FINFO, "select exception on fd %d\n", fd); */
if (FD_ISSET(fd, &r_fds)) {
int n = read(fd, buf + got, len - got);
if (DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] safe_read(%d)=%ld\n", who_am_i(), fd, (long)n);
ssize_t n = read(fd, buf + got, len - got);
if (DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] safe_read(%d)=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), fd, (SIZE_T_FMT_CAST)n);
}
if (n == 0)
break;
if (n < 0) {
if (errno == EINTR)
continue;
rsyserr(FERROR, errno, "safe_read failed to read %ld bytes", (long)len);
rsyserr(FERROR, errno, "safe_read failed to read %" SIZE_T_FMT_MOD "d bytes",
(SIZE_T_FMT_CAST)len);
exit_cleanup(RERR_STREAMIO);
}
if ((got += (size_t)n) == len)
@@ -302,7 +311,7 @@ static const char *what_fd_is(int fd)
* is not used on the socket except very early in the transfer. */
static void safe_write(int fd, const char *buf, size_t len)
{
int n;
ssize_t n;
assert(fd != iobuf.out_fd);
@@ -313,8 +322,8 @@ static void safe_write(int fd, const char *buf, size_t len)
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
write_failed:
rsyserr(FERROR, errno,
"safe_write failed to write %ld bytes to %s",
(long)len, what_fd_is(fd));
"safe_write failed to write %" SIZE_T_FMT_MOD "d bytes to %s",
(SIZE_T_FMT_CAST)len, what_fd_is(fd));
exit_cleanup(RERR_STREAMIO);
}
} else {
@@ -360,7 +369,7 @@ static void safe_write(int fd, const char *buf, size_t len)
* a chunk of data and put it into the output buffer. */
static void forward_filesfrom_data(void)
{
int len;
ssize_t len;
len = read(ff_forward_fd, ff_xb.buf + ff_xb.len, ff_xb.size - ff_xb.len);
if (len <= 0) {
@@ -371,12 +380,15 @@ static void forward_filesfrom_data(void)
free_xbuf(&ff_xb);
if (ff_reenable_multiplex >= 0)
io_start_multiplex_out(ff_reenable_multiplex);
free_implied_include_partial_string();
}
return;
}
if (DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] files-from read=%ld\n", who_am_i(), (long)len);
if (DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] files-from read=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)len);
}
#ifdef ICONV_OPTION
len += ff_xb.len;
@@ -412,6 +424,7 @@ static void forward_filesfrom_data(void)
while (s != eob) {
if (*s++ == '\0') {
ff_xb.len = s - sob - 1;
add_implied_include(sob, 0);
if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
exit_cleanup(RERR_PROTOCOL); /* impossible? */
write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
@@ -427,6 +440,7 @@ static void forward_filesfrom_data(void)
ff_lastchar = '\0';
else {
/* Handle a partial string specially, saving any incomplete chars. */
implied_include_partial_string(sob, s);
flags &= ~ICB_INCLUDE_INCOMPLETE;
if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0) {
if (errno == E2BIG)
@@ -443,13 +457,17 @@ static void forward_filesfrom_data(void)
char *f = ff_xb.buf + ff_xb.pos;
char *t = ff_xb.buf;
char *eob = f + len;
char *cur = t;
/* Eliminate any multi-'\0' runs. */
while (f != eob) {
if (!(*t++ = *f++)) {
add_implied_include(cur, 0);
cur = t;
while (f != eob && *f == '\0')
f++;
}
}
implied_include_partial_string(cur, t);
ff_lastchar = f[-1];
if ((len = t - ff_xb.buf) != 0) {
/* This will not circle back to perform_io() because we only get
@@ -463,7 +481,7 @@ void reduce_iobuf_size(xbuf *out, size_t new_size)
{
if (new_size < out->size) {
/* Avoid weird buffer interactions by only outputting this to stderr. */
if (msgs2stderr && DEBUG_GTE(IO, 4)) {
if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
const char *name = out == &iobuf.out ? "iobuf.out"
: out == &iobuf.msg ? "iobuf.msg"
: NULL;
@@ -481,7 +499,7 @@ void restore_iobuf_size(xbuf *out)
if (IOBUF_WAS_REDUCED(out->size)) {
size_t new_size = IOBUF_RESTORE_SIZE(out->size);
/* Avoid weird buffer interactions by only outputting this to stderr. */
if (msgs2stderr && DEBUG_GTE(IO, 4)) {
if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
const char *name = out == &iobuf.out ? "iobuf.out"
: out == &iobuf.msg ? "iobuf.msg"
: NULL;
@@ -560,52 +578,59 @@ static char *perform_io(size_t needed, int flags)
case PIO_NEED_INPUT:
/* We never resize the circular input buffer. */
if (iobuf.in.size < needed) {
rprintf(FERROR, "need to read %ld bytes, iobuf.in.buf is only %ld bytes.\n",
(long)needed, (long)iobuf.in.size);
rprintf(FERROR, "need to read %" SIZE_T_FMT_MOD "d bytes,"
" iobuf.in.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.in.size);
exit_cleanup(RERR_PROTOCOL);
}
if (msgs2stderr && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%ld, %sinput)\n",
who_am_i(), (long)needed, flags & PIO_CONSUME_INPUT ? "consume&" : "");
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %sinput)\n",
who_am_i(), (SIZE_T_FMT_CAST)needed, flags & PIO_CONSUME_INPUT ? "consume&" : "");
}
break;
case PIO_NEED_OUTROOM:
/* We never resize the circular output buffer. */
if (iobuf.out.size - iobuf.out_empty_len < needed) {
fprintf(stderr, "need to write %ld bytes, iobuf.out.buf is only %ld bytes.\n",
(long)needed, (long)(iobuf.out.size - iobuf.out_empty_len));
fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
" iobuf.out.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)(iobuf.out.size - iobuf.out_empty_len));
exit_cleanup(RERR_PROTOCOL);
}
if (msgs2stderr && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%ld, outroom) needs to flush %ld\n",
who_am_i(), (long)needed,
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
" outroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)needed,
iobuf.out.len + needed > iobuf.out.size
? (long)(iobuf.out.len + needed - iobuf.out.size) : 0L);
? (SIZE_T_FMT_CAST)(iobuf.out.len + needed - iobuf.out.size) : (SIZE_T_FMT_CAST)0);
}
break;
case PIO_NEED_MSGROOM:
/* We never resize the circular message buffer. */
if (iobuf.msg.size < needed) {
fprintf(stderr, "need to write %ld bytes, iobuf.msg.buf is only %ld bytes.\n",
(long)needed, (long)iobuf.msg.size);
fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
" iobuf.msg.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.msg.size);
exit_cleanup(RERR_PROTOCOL);
}
if (msgs2stderr && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%ld, msgroom) needs to flush %ld\n",
who_am_i(), (long)needed,
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
" msgroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)needed,
iobuf.msg.len + needed > iobuf.msg.size
? (long)(iobuf.msg.len + needed - iobuf.msg.size) : 0L);
? (SIZE_T_FMT_CAST)(iobuf.msg.len + needed - iobuf.msg.size) : (SIZE_T_FMT_CAST)0);
}
break;
case 0:
if (msgs2stderr && DEBUG_GTE(IO, 3))
rprintf(FINFO, "[%s] perform_io(%ld, %d)\n", who_am_i(), (long)needed, flags);
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %d)\n",
who_am_i(), (SIZE_T_FMT_CAST)needed, flags);
}
break;
default:
@@ -662,9 +687,9 @@ static char *perform_io(size_t needed, int flags)
SIVAL(iobuf.out.buf + iobuf.raw_data_header_pos, 0,
((MPLEX_BASE + (int)MSG_DATA)<<24) + iobuf.out.len - 4);
if (msgs2stderr && DEBUG_GTE(IO, 1)) {
rprintf(FINFO, "[%s] send_msg(%d, %ld)\n",
who_am_i(), (int)MSG_DATA, (long)iobuf.out.len - 4);
if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
who_am_i(), (int)MSG_DATA, (SIZE_T_FMT_CAST)iobuf.out.len - 4);
}
/* reserve room for the next MSG_DATA header */
@@ -755,7 +780,7 @@ static char *perform_io(size_t needed, int flags)
if (iobuf.in_fd >= 0 && FD_ISSET(iobuf.in_fd, &r_fds)) {
size_t len, pos = iobuf.in.pos + iobuf.in.len;
int n;
ssize_t n;
if (pos >= iobuf.in.size) {
pos -= iobuf.in.size;
len = iobuf.in.size - iobuf.in.len;
@@ -782,12 +807,14 @@ static char *perform_io(size_t needed, int flags)
exit_cleanup(RERR_SOCKETIO);
}
}
if (msgs2stderr && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] recv=%ld\n", who_am_i(), (long)n);
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] recv=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)n);
}
if (io_timeout) {
last_io_in = time(NULL);
if (flags & PIO_NEED_INPUT)
if (io_timeout && flags & PIO_NEED_INPUT)
maybe_send_keepalive(last_io_in, 0);
}
stats.total_read += n;
@@ -795,9 +822,14 @@ static char *perform_io(size_t needed, int flags)
iobuf.in.len += n;
}
if (stop_at_utime && time(NULL) >= stop_at_utime) {
rprintf(FERROR, "stopping at requested limit\n");
exit_cleanup(RERR_TIMEOUT);
}
if (out && FD_ISSET(iobuf.out_fd, &w_fds)) {
size_t len = iobuf.raw_flushing_ends_before ? iobuf.raw_flushing_ends_before - out->pos : out->len;
int n;
ssize_t n;
if (bwlimit_writemax && len > bwlimit_writemax)
len = bwlimit_writemax;
@@ -817,9 +849,9 @@ static char *perform_io(size_t needed, int flags)
exit_cleanup(RERR_SOCKETIO);
}
}
if (msgs2stderr && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] %s sent=%ld\n",
who_am_i(), out == &iobuf.out ? "out" : "msg", (long)n);
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] %s sent=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), out == &iobuf.out ? "out" : "msg", (SIZE_T_FMT_CAST)n);
}
if (io_timeout)
@@ -912,7 +944,11 @@ void noop_io_until_death(void)
{
char buf[1024];
if (!iobuf.in.buf || !iobuf.out.buf || iobuf.in_fd < 0 || iobuf.out_fd < 0 || kluge_around_eof || msgs2stderr)
if (!iobuf.in.buf || !iobuf.out.buf || iobuf.in_fd < 0 || iobuf.out_fd < 0 || kluge_around_eof)
return;
/* If we're talking to a daemon over a socket, don't short-circuit this logic */
if (msgs2stderr && daemon_connection >= 0)
return;
kluge_around_eof = 2;
@@ -930,13 +966,15 @@ int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
{
char *hdr;
size_t needed, pos;
BOOL want_debug = DEBUG_GTE(IO, 1) && convert >= 0 && (msgs2stderr || code != MSG_INFO);
BOOL want_debug = DEBUG_GTE(IO, 1) && convert >= 0 && (msgs2stderr == 1 || code != MSG_INFO);
if (!OUT_MULTIPLEXED)
return 0;
if (want_debug)
rprintf(FINFO, "[%s] send_msg(%d, %ld)\n", who_am_i(), (int)code, (long)len);
if (want_debug) {
rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
who_am_i(), (int)code, (SIZE_T_FMT_CAST)len);
}
/* When checking for enough free space for this message, we need to
* make sure that there is space for the 4-byte header, plus we'll
@@ -952,9 +990,9 @@ int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
#endif
needed = len + 4 + 3;
if (iobuf.msg.len + needed > iobuf.msg.size) {
if (!am_receiver)
if (am_sender)
perform_io(needed, PIO_NEED_MSGROOM);
else { /* We allow the receiver to increase their iobuf.msg size to avoid a deadlock. */
else { /* We sometimes allow the iobuf.msg size to increase to avoid a deadlock. */
size_t old_size = iobuf.msg.size;
restore_iobuf_size(&iobuf.msg);
realloc_xbuf(&iobuf.msg, iobuf.msg.size * 2);
@@ -1011,8 +1049,10 @@ int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
SIVAL(hdr, 0, ((MPLEX_BASE + (int)code)<<24) + len);
if (want_debug && convert > 0)
rprintf(FINFO, "[%s] converted msg len=%ld\n", who_am_i(), (long)len);
if (want_debug && convert > 0) {
rprintf(FINFO, "[%s] converted msg len=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)len);
}
return 1;
}
@@ -1028,10 +1068,31 @@ void send_msg_int(enum msgcode code, int num)
send_msg(code, numbuf, 4, -1);
}
void send_msg_success(const char *fname, int num)
{
if (local_server) {
STRUCT_STAT st;
if (DEBUG_GTE(IO, 1))
rprintf(FINFO, "[%s] send_msg_success(%d)\n", who_am_i(), num);
if (stat(fname, &st) < 0)
memset(&st, 0, sizeof (STRUCT_STAT));
SIVAL(num_dev_ino_buf, 0, num);
SIVAL64(num_dev_ino_buf, 4, st.st_dev);
SIVAL64(num_dev_ino_buf, 4+8, st.st_ino);
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
} else
send_msg_int(MSG_SUCCESS, num);
}
static void got_flist_entry_status(enum festatus status, int ndx)
{
struct file_list *flist = flist_for_ndx(ndx, "got_flist_entry_status");
if (ndx < flist->ndx_start)
exit_cleanup(RERR_PROTOCOL);
if (remove_source_files) {
active_filecnt--;
active_bytecnt -= F_LENGTH(flist->files[ndx - flist->ndx_start]);
@@ -1042,8 +1103,12 @@ static void got_flist_entry_status(enum festatus status, int ndx)
switch (status) {
case FES_SUCCESS:
if (remove_source_files)
send_msg_int(MSG_SUCCESS, ndx);
if (remove_source_files) {
if (local_server)
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
else
send_msg_int(MSG_SUCCESS, ndx);
}
/* FALL THROUGH */
case FES_NO_SEND:
#ifdef SUPPORT_HARD_LINKS
@@ -1096,8 +1161,8 @@ void set_io_timeout(int secs)
static void check_for_d_option_error(const char *msg)
{
static char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
char *colon;
static const char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
const char *colon;
int saw_d = 0;
if (*msg != 'r'
@@ -1227,8 +1292,21 @@ int read_line(int fd, char *buf, size_t bufsiz, int flags)
return s - buf;
}
/* Reverse safe_arg()'s backslash escaping of a daemon option arg, the way a
* remote shell un-escapes args for the ssh transport. In place; \X -> X. */
static void unbackslash_arg(char *s)
{
char *f = s, *t = s;
while (*f) {
if (*f == '\\' && f[1])
f++;
*t++ = *f++;
}
*t = '\0';
}
void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
char ***argv_p, int *argc_p, char **request_p)
int unescape, char ***argv_p, int *argc_p, char **request_p)
{
int maxargs = MAX_ARGS;
int dot_pos = 0, argc = 0, request_len = 0;
@@ -1270,6 +1348,11 @@ void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
glob_expand(buf, &argv, &argc, &maxargs);
} else {
p = strdup(buf);
/* An option arg the client escaped with safe_arg() (no
* remote shell un-escapes it for a daemon). File args
* after the dot are handled by glob_expand() below. */
if (unescape)
unbackslash_arg(p);
argv[argc++] = p;
if (*p == '.' && p[1] == '\0')
dot_pos = argc;
@@ -1285,7 +1368,7 @@ void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
BOOL io_start_buffering_out(int f_out)
{
if (msgs2stderr && DEBUG_GTE(IO, 2))
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_buffering_out(%d)\n", who_am_i(), f_out);
if (iobuf.out.buf) {
@@ -1304,7 +1387,7 @@ BOOL io_start_buffering_out(int f_out)
BOOL io_start_buffering_in(int f_in)
{
if (msgs2stderr && DEBUG_GTE(IO, 2))
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_buffering_in(%d)\n", who_am_i(), f_in);
if (iobuf.in.buf) {
@@ -1323,7 +1406,7 @@ BOOL io_start_buffering_in(int f_in)
void io_end_buffering_in(BOOL free_buffers)
{
if (msgs2stderr && DEBUG_GTE(IO, 2)) {
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] io_end_buffering_in(IOBUF_%s_BUFS)\n",
who_am_i(), free_buffers ? "FREE" : "KEEP");
}
@@ -1338,7 +1421,7 @@ void io_end_buffering_in(BOOL free_buffers)
void io_end_buffering_out(BOOL free_buffers)
{
if (msgs2stderr && DEBUG_GTE(IO, 2)) {
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] io_end_buffering_out(IOBUF_%s_BUFS)\n",
who_am_i(), free_buffers ? "FREE" : "KEEP");
}
@@ -1426,8 +1509,10 @@ static void read_a_msg(void)
msg_bytes = tag & 0xFFFFFF;
tag = (tag >> 24) - MPLEX_BASE;
if (DEBUG_GTE(IO, 1) && msgs2stderr)
rprintf(FINFO, "[%s] got msg=%d, len=%ld\n", who_am_i(), (int)tag, (long)msg_bytes);
if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
rprintf(FINFO, "[%s] got msg=%d, len=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (int)tag, (SIZE_T_FMT_CAST)msg_bytes);
}
switch (tag) {
case MSG_DATA:
@@ -1536,14 +1621,15 @@ static void read_a_msg(void)
}
break;
case MSG_SUCCESS:
if (msg_bytes != 4) {
if (msg_bytes != (local_server ? 4+8+8 : 4)) {
invalid_msg:
rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
tag, (unsigned long)msg_bytes, who_am_i(),
inc_recurse ? "/inc" : "");
exit_cleanup(RERR_STREAMIO);
}
val = raw_read_int();
raw_read_buf(num_dev_ino_buf, msg_bytes);
val = IVAL(num_dev_ino_buf, 0);
iobuf.in_multiplexed = 1;
if (am_generator)
got_flist_entry_status(FES_SUCCESS, val);
@@ -1603,8 +1689,10 @@ static void read_a_msg(void)
else
goto invalid_msg;
iobuf.in_multiplexed = 1;
if (DEBUG_GTE(EXIT, 3))
rprintf(FINFO, "[%s] got MSG_ERROR_EXIT with %ld bytes\n", who_am_i(), (long)msg_bytes);
if (DEBUG_GTE(EXIT, 3)) {
rprintf(FINFO, "[%s] got MSG_ERROR_EXIT with %" SIZE_T_FMT_MOD "d bytes\n",
who_am_i(), (SIZE_T_FMT_CAST)msg_bytes);
}
if (msg_bytes == 0) {
if (!am_sender && !am_generator) {
if (DEBUG_GTE(EXIT, 3)) {
@@ -1718,6 +1806,13 @@ int32 read_int(int f)
return num;
}
uint32 read_uint(int f)
{
char b[4];
read_buf(f, b, 4);
return IVAL(b, 0);
}
int32 read_varint(int f)
{
union {
@@ -1791,6 +1886,45 @@ int64 read_varlong(int f, uchar min_bytes)
return u.x;
}
/* Read an int32 and verify lo <= v <= hi. On out-of-range, abort with a
* protocol error naming "what". The bound is co-located with the read so it
* cannot be forgotten by a downstream user. */
int32 read_int_bounded(int f, int32 lo, int32 hi, const char *what)
{
int32 v = read_int(f);
if (v < lo || v > hi) {
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
what, (long)v, (long)lo, (long)hi, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
return v;
}
/* As read_int_bounded but for varint-encoded values. */
int32 read_varint_bounded(int f, int32 lo, int32 hi, const char *what)
{
int32 v = read_varint(f);
if (v < lo || v > hi) {
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
what, (long)v, (long)lo, (long)hi, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
return v;
}
/* Read a varint that will be used as a size_t. Rejects negative values
* (which would wrap to ~SIZE_MAX) and values exceeding the supplied max. */
size_t read_varint_size(int f, size_t max, const char *what)
{
int32 v = read_varint(f);
if (v < 0 || (size_t)v > max) {
rprintf(FERROR, "wire size %s out of range: %ld > %lu [%s]\n",
what, (long)v, (unsigned long)max, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
return (size_t)v;
}
int64 read_longint(int f)
{
#if SIZEOF_INT64 >= 8
@@ -1810,6 +1944,7 @@ int64 read_longint(int f)
#endif
}
/* Debugging note: this will be named read_buf_() when using an external zlib. */
void read_buf(int f, char *buf, size_t len)
{
if (f != iobuf.in_fd) {
@@ -1896,6 +2031,21 @@ void read_sum_head(int f, struct sum_struct *sum)
(long)sum->count, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
/* Guard against integer overflow in downstream allocations sized by
* count*element_size. my_alloc uses divide-not-multiply so it is
* already wraparound-safe, but checking here gives a clearer error
* and also covers the (size_t)count * xfer_sum_len arithmetic that
* is performed *before* reaching my_alloc. */
if (xfer_sum_len > 0 && (size_t)sum->count > SIZE_MAX / (size_t)xfer_sum_len) {
rprintf(FERROR, "Invalid checksum count %ld (too large) [%s]\n",
(long)sum->count, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
if ((size_t)sum->count > SIZE_MAX / sizeof(struct sum_buf)) {
rprintf(FERROR, "Invalid checksum count %ld (sum_buf overflow) [%s]\n",
(long)sum->count, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
sum->blength = read_int(f);
if (sum->blength < 0 || sum->blength > max_blength) {
rprintf(FERROR, "Invalid block length %ld [%s]\n",
@@ -1903,7 +2053,7 @@ void read_sum_head(int f, struct sum_struct *sum)
exit_cleanup(RERR_PROTOCOL);
}
sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
if (sum->s2length < 0 || sum->s2length > MAX_DIGEST_LEN) {
if (sum->s2length < 0 || sum->s2length > xfer_sum_len) {
rprintf(FERROR, "Invalid checksum length %d [%s]\n",
sum->s2length, who_am_i());
exit_cleanup(RERR_PROTOCOL);
@@ -2298,7 +2448,7 @@ void io_start_multiplex_out(int fd)
{
io_flush(FULL_FLUSH);
if (msgs2stderr && DEBUG_GTE(IO, 2))
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_multiplex_out(%d)\n", who_am_i(), fd);
if (!iobuf.msg.buf)
@@ -2315,7 +2465,7 @@ void io_start_multiplex_out(int fd)
/* Setup for multiplexing a MSG_* stream with the data stream. */
void io_start_multiplex_in(int fd)
{
if (msgs2stderr && DEBUG_GTE(IO, 2))
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_multiplex_in(%d)\n", who_am_i(), fd);
iobuf.in_multiplexed = 1; /* See also IN_MULTIPLEXED */
@@ -2326,7 +2476,7 @@ int io_end_multiplex_in(int mode)
{
int ret = iobuf.in_multiplexed ? iobuf.in_fd : -1;
if (msgs2stderr && DEBUG_GTE(IO, 2))
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_end_multiplex_in(mode=%d)\n", who_am_i(), mode);
iobuf.in_multiplexed = 0;
@@ -2344,7 +2494,7 @@ int io_end_multiplex_out(int mode)
{
int ret = iobuf.out_empty_len ? iobuf.out_fd : -1;
if (msgs2stderr && DEBUG_GTE(IO, 2))
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_end_multiplex_out(mode=%d)\n", who_am_i(), mode);
if (mode != MPLX_TO_BUFFERED)

View File

@@ -1,6 +1,6 @@
/* Inline functions for rsync.
*
* Copyright (C) 2007-2020 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -40,6 +40,12 @@ isSpace(const char *ptr)
return isspace(*(unsigned char *)ptr);
}
static inline int
isAlNum(const char *ptr)
{
return isalnum(*(unsigned char *)ptr);
}
static inline int
isLower(const char *ptr)
{

View File

@@ -1 +1 @@
#define LATEST_YEAR "2020"
#define LATEST_YEAR "2026"

View File

@@ -24,16 +24,24 @@
static char number_separator;
#ifndef HAVE_STRDUP
char *strdup(char *s)
char get_number_separator(void)
{
int len = strlen(s) + 1;
char *ret = (char *)malloc(len);
if (ret)
memcpy(ret, s, len);
return ret;
if (!number_separator) {
char buf[32];
snprintf(buf, sizeof buf, "%f", 3.14);
if (strchr(buf, '.') != NULL)
number_separator = ',';
else
number_separator = '.';
}
return number_separator;
}
char get_decimal_point(void)
{
return get_number_separator() == ',' ? '.' : ',';
}
#endif
#ifndef HAVE_GETCWD
char *getcwd(char *buf, int size)
@@ -155,30 +163,6 @@ int sys_gettimeofday(struct timeval *tv)
#endif
}
#define HUMANIFY(mult) \
do { \
if (num >= mult || num <= -mult) { \
double dnum = (double)num / mult; \
char units; \
if (num < 0) \
dnum = -dnum; \
if (dnum < mult) \
units = 'K'; \
else if ((dnum /= mult) < mult) \
units = 'M'; \
else if ((dnum /= mult) < mult) \
units = 'G'; \
else { \
dnum /= mult; \
units = 'T'; \
} \
if (num < 0) \
dnum = -dnum; \
snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); \
return bufs[n]; \
} \
} while (0)
/* Return the int64 number as a string. If the human_flag arg is non-zero,
* we may output the number in K, M, G, or T units. If we don't add a unit
* suffix, we will append the fract string, if it is non-NULL. We can
@@ -190,22 +174,35 @@ char *do_big_num(int64 num, int human_flag, const char *fract)
char *s;
int len, negated;
if (human_flag && !number_separator) {
char buf[32];
snprintf(buf, sizeof buf, "%f", 3.14);
if (strchr(buf, '.') != NULL)
number_separator = ',';
else
number_separator = '.';
}
if (human_flag && !number_separator)
(void)get_number_separator();
n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
if (human_flag > 1) {
if (human_flag == 2)
HUMANIFY(1000);
else
HUMANIFY(1024);
int mult = human_flag == 2 ? 1000 : 1024;
if (num >= mult || num <= -mult) {
double dnum = (double)num / mult;
char units;
if (num < 0)
dnum = -dnum;
if (dnum < mult)
units = 'K';
else if ((dnum /= mult) < mult)
units = 'M';
else if ((dnum /= mult) < mult)
units = 'G';
else if ((dnum /= mult) < mult)
units = 'T';
else {
dnum /= mult;
units = 'P';
}
if (num < 0)
dnum = -dnum;
snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units);
return bufs[n];
}
}
s = bufs[n] + sizeof bufs[0] - 1;

View File

@@ -1,11 +1,28 @@
/* Keep this simple so both C and ASM can use it */
/* These allow something like CFLAGS=-DDISABLE_SHA512_DIGEST */
#ifdef DISABLE_SHA256_DIGEST
#undef SHA256_DIGEST_LENGTH
#endif
#ifdef DISABLE_SHA512_DIGEST
#undef SHA512_DIGEST_LENGTH
#endif
#define MD4_DIGEST_LEN 16
#define MD5_DIGEST_LEN 16
#if defined SHA512_DIGEST_LENGTH
#define MAX_DIGEST_LEN SHA512_DIGEST_LENGTH
#elif defined SHA256_DIGEST_LENGTH
#define MAX_DIGEST_LEN SHA256_DIGEST_LENGTH
#elif defined SHA_DIGEST_LENGTH
#define MAX_DIGEST_LEN SHA_DIGEST_LENGTH
#else
#define MAX_DIGEST_LEN MD5_DIGEST_LEN
#endif
#define CSUM_CHUNK 64
#define CSUM_gone -1
#define CSUM_NONE 0
#define CSUM_MD4_ARCHAIC 1
#define CSUM_MD4_BUSTED 2
@@ -15,3 +32,6 @@
#define CSUM_XXH64 6
#define CSUM_XXH3_64 7
#define CSUM_XXH3_128 8
#define CSUM_SHA1 9
#define CSUM_SHA256 10
#define CSUM_SHA512 11

View File

@@ -27,7 +27,7 @@
#include "config.h"
#include "md-defines.h"
#if !defined USE_OPENSSL && CSUM_CHUNK == 64
#ifdef USE_MD5_ASM /* { */
#ifdef __APPLE__
#define md5_process_asm _md5_process_asm
@@ -698,4 +698,4 @@ md5_process_asm:
pop %rbp
ret
#endif /* !USE_OPENSSL ... */
#endif /* } USE_MD5_ASM */

View File

@@ -2,7 +2,7 @@
* RFC 1321 compliant MD5 implementation
*
* Copyright (C) 2001-2003 Christophe Devine
* Copyright (C) 2007-2020 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
#include "rsync.h"
#ifndef USE_OPENSSL
void md5_begin(md_context *ctx)
{
ctx->A = 0x67452301;
@@ -148,7 +147,10 @@ static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
ctx->D += D;
}
#if defined HAVE_ASM && CSUM_CHUNK == 64
#ifdef USE_MD5_ASM
#if CSUM_CHUNK != 64
#error The MD5 ASM code does not support CSUM_CHUNK != 64
#endif
extern void md5_process_asm(md_context *ctx, const void *data, size_t num);
#endif
@@ -176,26 +178,26 @@ void md5_update(md_context *ctx, const uchar *input, uint32 length)
left = 0;
}
#if defined HAVE_ASM && CSUM_CHUNK == 64
#ifdef USE_MD5_ASM /* { */
if (length >= CSUM_CHUNK) {
uint32 chunks = length / CSUM_CHUNK;
md5_process_asm(ctx, input, chunks);
length -= chunks * CSUM_CHUNK;
input += chunks * CSUM_CHUNK;
}
#else
#else /* } { */
while (length >= CSUM_CHUNK) {
md5_process(ctx, input);
length -= CSUM_CHUNK;
input += CSUM_CHUNK;
}
#endif
#endif /* } */
if (length)
memcpy(ctx->buffer + left, input, length);
}
static uchar md5_padding[CSUM_CHUNK] = { 0x80 };
static const uchar md5_padding[CSUM_CHUNK] = { 0x80 };
void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
{
@@ -221,9 +223,8 @@ void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
SIVALu(digest, 8, ctx->C);
SIVALu(digest, 12, ctx->D);
}
#endif
#ifdef TEST_MD5
#ifdef TEST_MD5 /* { */
void get_md5(uchar *out, const uchar *input, int n)
{
@@ -317,4 +318,4 @@ int main(int argc, char *argv[])
return 0;
}
#endif
#endif /* } */

View File

@@ -1,8 +1,8 @@
/* The include file for both the MD4 and MD5 routines. */
#ifdef USE_OPENSSL
#include "openssl/md4.h"
#include "openssl/md5.h"
#include <openssl/sha.h>
#include <openssl/evp.h>
#endif
#include "md-defines.h"
@@ -17,13 +17,6 @@ void mdfour_begin(md_context *md);
void mdfour_update(md_context *md, const uchar *in, uint32 length);
void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN]);
#ifndef USE_OPENSSL
#define MD5_CTX md_context
#define MD5_Init md5_begin
#define MD5_Update md5_update
#define MD5_Final(digest, cptr) md5_result(cptr, digest)
void md5_begin(md_context *ctx);
void md5_update(md_context *ctx, const uchar *input, uint32 length);
void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN]);
#endif

View File

@@ -33,7 +33,7 @@ pool_alloc, pool_free, pool_free_old, pool_talloc, pool_tfree, pool_create, pool
.SH SYNOPSIS
.B #include "pool_alloc.h"
\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char *), int \fIflags\fB);
\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char*,char*,int), int \fIflags\fB);
\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);

View File

@@ -9,8 +9,7 @@ struct alloc_pool
size_t size; /* extent size */
size_t quantum; /* allocation quantum */
struct pool_extent *extents; /* top extent is "live" */
void (*bomb)(); /* function to call if
* malloc fails */
void (*bomb)(const char*, const char*, int); /* called if malloc fails */
int flags;
/* statistical data */
@@ -43,15 +42,16 @@ struct align_test {
/* Temporarily cast a void* var into a char* var when adding an offset (to
* keep some compilers from complaining about the pointer arithmetic). */
#define PTR_ADD(b,o) ( (void*) ((char*)(b) + (o)) )
#define PTR_SUB(b,o) ( (void*) ((char*)(b) - (o)) )
alloc_pool_t
pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags)
pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags)
{
struct alloc_pool *pool;
if ((MINALIGN & (MINALIGN - 1)) != 0) {
if ((MINALIGN & (MINALIGN - 1)) != (0)) {
if (bomb)
(*bomb)("Compiler error: MINALIGN is not a power of 2\n");
(*bomb)("Compiler error: MINALIGN is not a power of 2", __FILE__, __LINE__);
return NULL;
}
@@ -101,7 +101,7 @@ pool_destroy(alloc_pool_t p)
for (cur = pool->extents; cur; cur = next) {
next = cur->next;
if (pool->flags & POOL_PREPEND)
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
else {
free(cur->start);
free(cur);
@@ -169,7 +169,7 @@ pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
bomb_out:
if (pool->bomb)
(*pool->bomb)(bomb_msg);
(*pool->bomb)(bomb_msg, __FILE__, __LINE__);
return NULL;
}
@@ -236,7 +236,7 @@ pool_free(alloc_pool_t p, size_t len, void *addr)
if (cur->free + cur->bound >= pool->size) {
prev->next = cur->next;
if (pool->flags & POOL_PREPEND)
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
else {
free(cur->start);
free(cur);
@@ -293,7 +293,7 @@ pool_free_old(alloc_pool_t p, void *addr)
while ((cur = next) != NULL) {
next = cur->next;
if (pool->flags & POOL_PREPEND)
free(PTR_ADD(cur->start, -sizeof (struct pool_extent)));
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
else {
free(cur->start);
free(cur);

View File

@@ -7,7 +7,7 @@
typedef void *alloc_pool_t;
alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags);
alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags);
void pool_destroy(alloc_pool_t pool);
void *pool_alloc(alloc_pool_t pool, size_t size, const char *bomb_msg);
void pool_free(alloc_pool_t pool, size_t size, void *addr);

View File

@@ -20,7 +20,7 @@
* for string length. This covers a nasty loophole.
*
* The other functions are there to prevent NULL pointers from
* causing nast effects.
* causing nasty effects.
*
* More Recently:
* Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43

View File

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
* Version 2.2.x
* Portable SMB ACL interface
* Copyright (C) Jeremy Allison 2000
* Copyright (C) 2007-2019 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -139,7 +139,9 @@ typedef struct acl *SMB_ACL_ENTRY_T;
/* Based on the Solaris & UnixWare code. */
#ifndef __TANDEM
#undef GROUP
#endif
#include <sys/aclv.h>
/* SVR4.2 ES/MP ACLs */
@@ -230,7 +232,7 @@ struct new_acl_entry{
#define SMB_ACL_ENTRY_T struct new_acl_entry*
#define SMB_ACL_T struct acl_entry_link*
#define SMB_ACL_TAG_T unsigned short
#define SMB_ACL_TYPE_T int

View File

@@ -2,7 +2,7 @@
* Extended attribute support for rsync.
*
* Copyright (C) 2004 Red Hat, Inc.
* Copyright (C) 2003-2019 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
* Written by Jay Fenlason.
*
* This program is free software; you can redistribute it and/or modify
@@ -67,7 +67,7 @@ ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t si
u_int32_t offset = len;
size_t data_retrieved = len;
while (data_retrieved < size) {
len = getxattr(path, name, value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
len = getxattr(path, name, (char*)value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
if (len <= 0)
break;
data_retrieved += len;
@@ -126,9 +126,18 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
unsigned char keylen;
ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
if (len <= 0 || (size_t)len > size)
if (len <= 0 || size == 0)
return len;
if ((size_t)len >= size) {
/* FreeBSD extattr_list_xx() returns 'size' as 'len' in case there are
more data available, truncating the output, we solve this by signalling
ERANGE in case len == size so that the code in xattrs.c will retry with
a bigger buffer */
errno = ERANGE;
return -1;
}
/* FreeBSD puts a single-byte length before each string, with no '\0'
* terminator. We need to change this into a series of null-terminted
* strings. Since the size is the same, we can simply transform the
@@ -136,7 +145,7 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
for (off = 0; off < len; off += keylen + 1) {
keylen = ((unsigned char*)list)[off];
if (off + keylen >= len) {
/* Should be impossible, but kernel bugs happen! */
/* Should be impossible, but bugs happen! */
errno = EINVAL;
return -1;
}
@@ -167,7 +176,7 @@ static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
} else {
size_t bufpos;
for (bufpos = 0; bufpos < sb.st_size; ) {
ssize_t cnt = read(attrfd, buf + bufpos, sb.st_size - bufpos);
ssize_t cnt = read(attrfd, (char*)buf + bufpos, sb.st_size - bufpos);
if (cnt <= 0) {
if (cnt < 0 && errno == EINTR)
continue;
@@ -218,7 +227,7 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
return -1;
for (bufpos = 0; bufpos < size; ) {
ssize_t cnt = write(attrfd, value+bufpos, size);
ssize_t cnt = write(attrfd, (char*)value + bufpos, size);
if (cnt <= 0) {
if (cnt < 0 && errno == EINTR)
continue;
@@ -274,7 +283,8 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
&& (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
continue;
if ((ret += len+1) > size) {
ret += len + 1;
if ((size_t)ret > size) {
if (size == 0)
continue;
ret = -1;

View File

@@ -48,7 +48,6 @@
extern item_list dparam_list;
#define strequal(a, b) (strcasecmp(a, b)==0)
#define BOOLSTR(b) ((b) ? "Yes" : "No")
#ifndef LOG_DAEMON
#define LOG_DAEMON 0
@@ -56,7 +55,7 @@ extern item_list dparam_list;
/* the following are used by loadparm for option lists */
typedef enum {
P_BOOL, P_BOOLREV, P_CHAR, P_INTEGER,
P_BOOL, P_BOOLREV, P_BOOL3, P_CHAR, P_INTEGER,
P_OCTAL, P_PATH, P_STRING, P_ENUM
} parm_type;
@@ -66,7 +65,7 @@ typedef enum {
struct enum_list {
int value;
char *name;
const char *name;
};
struct parm_struct {
@@ -74,7 +73,7 @@ struct parm_struct {
parm_type type;
parm_class class;
void *ptr;
struct enum_list *enum_list;
const struct enum_list *enum_list;
unsigned flags;
};
@@ -87,234 +86,6 @@ struct parm_struct {
#define LP_SNUM_OK(i) ((i) >= 0 && (i) < (int)section_list.count)
#define SECTION_PTR(s, p) (((char*)(s)) + (ptrdiff_t)(((char*)(p))-(char*)&Vars.l))
/* This structure describes global (ie., server-wide) parameters. */
typedef struct {
char *bind_address;
char *daemon_chroot;
char *daemon_gid;
char *daemon_uid;
char *motd_file;
char *pid_file;
char *socket_options;
/* Each _EXP var tracks if the associated char* var has been expanded yet or not. */
BOOL bind_address_EXP;
BOOL daemon_chroot_EXP;
BOOL daemon_gid_EXP;
BOOL daemon_uid_EXP;
BOOL motd_file_EXP;
BOOL pid_file_EXP;
BOOL socket_options_EXP;
int listen_backlog;
int rsync_port;
BOOL proxy_protocol;
} global_vars;
/* This structure describes a single section. Their order must match the
* initializers below, which you can accomplish by keeping each sub-section
* sorted. (e.g. in vim, just visually select each subsection and use !sort.)
* NOTE: the char* variables MUST all remain at the start of the struct! */
typedef struct {
char *auth_users;
char *charset;
char *comment;
char *dont_compress;
char *early_exec;
char *exclude;
char *exclude_from;
char *filter;
char *gid;
char *hosts_allow;
char *hosts_deny;
char *include;
char *include_from;
char *incoming_chmod;
char *lock_file;
char *log_file;
char *log_format;
char *name;
char *outgoing_chmod;
char *path;
char *postxfer_exec;
char *prexfer_exec;
char *refuse_options;
char *secrets_file;
char *syslog_tag;
char *temp_dir;
char *uid;
/* Each _EXP var tracks if the associated char* var has been expanded yet or not. */
BOOL auth_users_EXP;
BOOL charset_EXP;
BOOL comment_EXP;
BOOL dont_compress_EXP;
BOOL early_exec_EXP;
BOOL exclude_EXP;
BOOL exclude_from_EXP;
BOOL filter_EXP;
BOOL gid_EXP;
BOOL hosts_allow_EXP;
BOOL hosts_deny_EXP;
BOOL include_EXP;
BOOL include_from_EXP;
BOOL incoming_chmod_EXP;
BOOL lock_file_EXP;
BOOL log_file_EXP;
BOOL log_format_EXP;
BOOL name_EXP;
BOOL outgoing_chmod_EXP;
BOOL path_EXP;
BOOL postxfer_exec_EXP;
BOOL prexfer_exec_EXP;
BOOL refuse_options_EXP;
BOOL secrets_file_EXP;
BOOL syslog_tag_EXP;
BOOL temp_dir_EXP;
BOOL uid_EXP;
int max_connections;
int max_verbosity;
int syslog_facility;
int timeout;
BOOL fake_super;
BOOL forward_lookup;
BOOL ignore_errors;
BOOL ignore_nonreadable;
BOOL list;
BOOL munge_symlinks;
BOOL numeric_ids;
BOOL read_only;
BOOL reverse_lookup;
BOOL strict_modes;
BOOL transfer_logging;
BOOL use_chroot;
BOOL write_only;
} local_vars;
/* This structure describes the global variables (g) as well as the globally
* specified values of the local variables (l), which are used when modules
* don't specify their own values. */
typedef struct {
global_vars g;
local_vars l;
} all_vars;
/* The application defaults for all the variables. "Defaults" is
* used to re-initialize "Vars" before each config-file read.
*
* In order to keep these sorted in the same way as the structure
* above, use the variable name in the leading comment, including a
* trailing ';' (to avoid a sorting problem with trailing digits). */
static const all_vars Defaults = {
/* ==== global_vars ==== */
{
/* bind_address; */ NULL,
/* daemon_chroot; */ NULL,
/* daemon_gid; */ NULL,
/* daemon_uid; */ NULL,
/* motd_file; */ NULL,
/* pid_file; */ NULL,
/* socket_options; */ NULL,
/* bind_address_EXP; */ False,
/* daemon_chroot_EXP; */ False,
/* daemon_gid_EXP; */ False,
/* daemon_uid_EXP; */ False,
/* motd_file_EXP; */ False,
/* pid_file_EXP; */ False,
/* socket_options_EXP; */ False,
/* listen_backlog; */ 5,
/* rsync_port; */ 0,
/* proxy_protocol; */ False,
},
/* ==== local_vars ==== */
{
/* auth_users; */ NULL,
/* charset; */ NULL,
/* comment; */ NULL,
/* dont_compress; */ DEFAULT_DONT_COMPRESS,
/* early_exec; */ NULL,
/* exclude; */ NULL,
/* exclude_from; */ NULL,
/* filter; */ NULL,
/* gid; */ NULL,
/* hosts_allow; */ NULL,
/* hosts_deny; */ NULL,
/* include; */ NULL,
/* include_from; */ NULL,
/* incoming_chmod; */ NULL,
/* lock_file; */ DEFAULT_LOCK_FILE,
/* log_file; */ NULL,
/* log_format; */ "%o %h [%a] %m (%u) %f %l",
/* name; */ NULL,
/* outgoing_chmod; */ NULL,
/* path; */ NULL,
/* postxfer_exec; */ NULL,
/* prexfer_exec; */ NULL,
/* refuse_options; */ NULL,
/* secrets_file; */ NULL,
/* syslog_tag; */ "rsyncd",
/* temp_dir; */ NULL,
/* uid; */ NULL,
/* auth_users_EXP; */ False,
/* charset_EXP; */ False,
/* comment_EXP; */ False,
/* dont_compress_EXP; */ False,
/* early_exec_EXP; */ False,
/* exclude_EXP; */ False,
/* exclude_from_EXP; */ False,
/* filter_EXP; */ False,
/* gid_EXP; */ False,
/* hosts_allow_EXP; */ False,
/* hosts_deny_EXP; */ False,
/* include_EXP; */ False,
/* include_from_EXP; */ False,
/* incoming_chmod_EXP; */ False,
/* lock_file_EXP; */ False,
/* log_file_EXP; */ False,
/* log_format_EXP; */ False,
/* name_EXP; */ False,
/* outgoing_chmod_EXP; */ False,
/* path_EXP; */ False,
/* postxfer_exec_EXP; */ False,
/* prexfer_exec_EXP; */ False,
/* refuse_options_EXP; */ False,
/* secrets_file_EXP; */ False,
/* syslog_tag_EXP; */ False,
/* temp_dir_EXP; */ False,
/* uid_EXP; */ False,
/* max_connections; */ 0,
/* max_verbosity; */ 1,
/* syslog_facility; */ LOG_DAEMON,
/* timeout; */ 0,
/* fake_super; */ False,
/* forward_lookup; */ True,
/* ignore_errors; */ False,
/* ignore_nonreadable; */ False,
/* list; */ True,
/* munge_symlinks; */ (BOOL)-1,
/* numeric_ids; */ (BOOL)-1,
/* read_only; */ True,
/* reverse_lookup; */ True,
/* strict_modes; */ True,
/* transfer_logging; */ False,
/* use_chroot; */ True,
/* write_only; */ False,
}
};
/* The currently configured values for all the variables. */
static all_vars Vars;
/* Stack of "Vars" values used by the &include directive. */
static item_list Vars_stack = EMPTY_ITEM_LIST;
@@ -324,9 +95,7 @@ static item_list section_list = EMPTY_ITEM_LIST;
static int iSectionIndex = -1;
static BOOL bInGlobalSection = True;
#define NUMPARAMETERS (sizeof (parm_table) / sizeof (struct parm_struct))
static struct enum_list enum_facilities[] = {
static const struct enum_list enum_syslog_facility[] = {
#ifdef LOG_AUTH
{ LOG_AUTH, "auth" },
#endif
@@ -393,95 +162,27 @@ static struct enum_list enum_facilities[] = {
{ -1, NULL }
};
static struct parm_struct parm_table[] =
{
{"address", P_STRING, P_GLOBAL,&Vars.g.bind_address, NULL,0},
{"daemon chroot", P_STRING, P_GLOBAL,&Vars.g.daemon_chroot, NULL,0},
{"daemon gid", P_STRING, P_GLOBAL,&Vars.g.daemon_gid, NULL,0},
{"daemon uid", P_STRING, P_GLOBAL,&Vars.g.daemon_uid, NULL,0},
{"listen backlog", P_INTEGER,P_GLOBAL,&Vars.g.listen_backlog, NULL,0},
{"motd file", P_STRING, P_GLOBAL,&Vars.g.motd_file, NULL,0},
{"pid file", P_STRING, P_GLOBAL,&Vars.g.pid_file, NULL,0},
{"port", P_INTEGER,P_GLOBAL,&Vars.g.rsync_port, NULL,0},
{"proxy protocol", P_BOOL, P_LOCAL, &Vars.g.proxy_protocol, NULL,0},
{"socket options", P_STRING, P_GLOBAL,&Vars.g.socket_options, NULL,0},
{"auth users", P_STRING, P_LOCAL, &Vars.l.auth_users, NULL,0},
{"charset", P_STRING, P_LOCAL, &Vars.l.charset, NULL,0},
{"comment", P_STRING, P_LOCAL, &Vars.l.comment, NULL,0},
{"dont compress", P_STRING, P_LOCAL, &Vars.l.dont_compress, NULL,0},
{"early exec", P_STRING, P_LOCAL, &Vars.l.early_exec, NULL,0},
{"exclude from", P_STRING, P_LOCAL, &Vars.l.exclude_from, NULL,0},
{"exclude", P_STRING, P_LOCAL, &Vars.l.exclude, NULL,0},
{"fake super", P_BOOL, P_LOCAL, &Vars.l.fake_super, NULL,0},
{"filter", P_STRING, P_LOCAL, &Vars.l.filter, NULL,0},
{"forward lookup", P_BOOL, P_LOCAL, &Vars.l.forward_lookup, NULL,0},
{"gid", P_STRING, P_LOCAL, &Vars.l.gid, NULL,0},
{"hosts allow", P_STRING, P_LOCAL, &Vars.l.hosts_allow, NULL,0},
{"hosts deny", P_STRING, P_LOCAL, &Vars.l.hosts_deny, NULL,0},
{"ignore errors", P_BOOL, P_LOCAL, &Vars.l.ignore_errors, NULL,0},
{"ignore nonreadable",P_BOOL, P_LOCAL, &Vars.l.ignore_nonreadable, NULL,0},
{"include from", P_STRING, P_LOCAL, &Vars.l.include_from, NULL,0},
{"include", P_STRING, P_LOCAL, &Vars.l.include, NULL,0},
{"incoming chmod", P_STRING, P_LOCAL, &Vars.l.incoming_chmod, NULL,0},
{"list", P_BOOL, P_LOCAL, &Vars.l.list, NULL,0},
{"lock file", P_STRING, P_LOCAL, &Vars.l.lock_file, NULL,0},
{"log file", P_STRING, P_LOCAL, &Vars.l.log_file, NULL,0},
{"log format", P_STRING, P_LOCAL, &Vars.l.log_format, NULL,0},
{"max connections", P_INTEGER,P_LOCAL, &Vars.l.max_connections, NULL,0},
{"max verbosity", P_INTEGER,P_LOCAL, &Vars.l.max_verbosity, NULL,0},
{"munge symlinks", P_BOOL, P_LOCAL, &Vars.l.munge_symlinks, NULL,0},
{"name", P_STRING, P_LOCAL, &Vars.l.name, NULL,0},
{"numeric ids", P_BOOL, P_LOCAL, &Vars.l.numeric_ids, NULL,0},
{"outgoing chmod", P_STRING, P_LOCAL, &Vars.l.outgoing_chmod, NULL,0},
{"path", P_PATH, P_LOCAL, &Vars.l.path, NULL,0},
#ifdef HAVE_PUTENV
{"post-xfer exec", P_STRING, P_LOCAL, &Vars.l.postxfer_exec, NULL,0},
{"pre-xfer exec", P_STRING, P_LOCAL, &Vars.l.prexfer_exec, NULL,0},
#endif
{"read only", P_BOOL, P_LOCAL, &Vars.l.read_only, NULL,0},
{"refuse options", P_STRING, P_LOCAL, &Vars.l.refuse_options, NULL,0},
{"reverse lookup", P_BOOL, P_LOCAL, &Vars.l.reverse_lookup, NULL,0},
{"secrets file", P_STRING, P_LOCAL, &Vars.l.secrets_file, NULL,0},
{"strict modes", P_BOOL, P_LOCAL, &Vars.l.strict_modes, NULL,0},
{"syslog facility", P_ENUM, P_LOCAL, &Vars.l.syslog_facility, enum_facilities,0},
{"syslog tag", P_STRING, P_LOCAL, &Vars.l.syslog_tag, NULL,0},
{"temp dir", P_PATH, P_LOCAL, &Vars.l.temp_dir, NULL,0},
{"timeout", P_INTEGER,P_LOCAL, &Vars.l.timeout, NULL,0},
{"transfer logging", P_BOOL, P_LOCAL, &Vars.l.transfer_logging, NULL,0},
{"uid", P_STRING, P_LOCAL, &Vars.l.uid, NULL,0},
{"use chroot", P_BOOL, P_LOCAL, &Vars.l.use_chroot, NULL,0},
{"write only", P_BOOL, P_LOCAL, &Vars.l.write_only, NULL,0},
{NULL, P_BOOL, P_NONE, NULL, NULL,0}
};
/* Initialise the Default all_vars structure. */
void reset_daemon_vars(void)
{
memcpy(&Vars, &Defaults, sizeof Vars);
}
/* Expand %VAR% references. Any unknown vars or unrecognized
* syntax leaves the raw chars unchanged. */
static char *expand_vars(char *str)
static char *expand_vars(const char *str)
{
char *buf, *t, *f;
char *buf, *t;
const char *f;
int bufsize;
if (!str || !strchr(str, '%'))
return str;
return (char *)str; /* TODO change return value to const char* at some point. */
bufsize = strlen(str) + 2048;
buf = new_array(char, bufsize+1); /* +1 for trailing '\0' */
for (t = buf, f = str; bufsize && *f; ) {
if (*f == '%' && *++f != '%') {
char *percent = strchr(f, '%');
if (percent) {
if (*f == '%' && isUpper(f+1)) {
const char *percent = strchr(f+1, '%');
if (percent && percent - f < bufsize) {
char *val;
*percent = '\0';
val = getenv(f);
*percent = '%';
strlcpy(t, f+1, percent - f);
val = getenv(t);
if (val) {
int len = strlcpy(t, val, bufsize+1);
if (len > bufsize)
@@ -492,7 +193,6 @@ static char *expand_vars(char *str)
continue;
}
}
f--;
}
*t++ = *f++;
bufsize--;
@@ -510,6 +210,8 @@ static char *expand_vars(char *str)
return buf;
}
/* Each "char* foo" has an associated "BOOL foo_EXP" that tracks if the string has been expanded yet or not. */
/* NOTE: use this function and all the FN_{GLOBAL,LOCAL} ones WITHOUT a trailing semicolon! */
#define RETURN_EXPANDED(val) {if (!val ## _EXP) {val = expand_vars(val); val ## _EXP = True;} return val ? val : "";}
@@ -534,65 +236,24 @@ static char *expand_vars(char *str)
#define FN_LOCAL_INTEGER(fn_name, val) \
int fn_name(int i) {return LP_SNUM_OK(i)? iSECTION(i).val : Vars.l.val;}
FN_GLOBAL_STRING(lp_bind_address, bind_address)
FN_GLOBAL_STRING(lp_daemon_chroot, daemon_chroot)
FN_GLOBAL_STRING(lp_daemon_gid, daemon_gid)
FN_GLOBAL_STRING(lp_daemon_uid, daemon_uid)
FN_GLOBAL_STRING(lp_motd_file, motd_file)
FN_GLOBAL_STRING(lp_pid_file, pid_file)
FN_GLOBAL_STRING(lp_socket_options, socket_options)
/* The following include file contains:
*
* typedef global_vars - describes global (ie., server-wide) parameters.
* typedef local_vars - describes a single section.
* typedef all_vars - a combination of global_vars & local_vars.
* all_vars Defaults - the default values for all the variables.
* all_vars Vars - the currently configured values for all the variables.
* struct parm_struct parm_table - the strings & variables for the parser.
* FN_{LOCAL,GLOBAL}_{TYPE}() definition for all the lp_var_name() accessors.
*/
FN_GLOBAL_INTEGER(lp_listen_backlog, listen_backlog)
FN_GLOBAL_INTEGER(lp_rsync_port, rsync_port)
#include "daemon-parm.h"
FN_GLOBAL_BOOL(lp_proxy_protocol, proxy_protocol)
FN_LOCAL_STRING(lp_auth_users, auth_users)
FN_LOCAL_STRING(lp_charset, charset)
FN_LOCAL_STRING(lp_comment, comment)
FN_LOCAL_STRING(lp_dont_compress, dont_compress)
FN_LOCAL_STRING(lp_early_exec, early_exec)
FN_LOCAL_STRING(lp_exclude, exclude)
FN_LOCAL_STRING(lp_exclude_from, exclude_from)
FN_LOCAL_STRING(lp_filter, filter)
FN_LOCAL_STRING(lp_gid, gid)
FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
FN_LOCAL_STRING(lp_hosts_deny, hosts_deny)
FN_LOCAL_STRING(lp_include, include)
FN_LOCAL_STRING(lp_include_from, include_from)
FN_LOCAL_STRING(lp_incoming_chmod, incoming_chmod)
FN_LOCAL_STRING(lp_lock_file, lock_file)
FN_LOCAL_STRING(lp_log_file, log_file)
FN_LOCAL_STRING(lp_log_format, log_format)
FN_LOCAL_STRING(lp_name, name)
FN_LOCAL_STRING(lp_outgoing_chmod, outgoing_chmod)
FN_LOCAL_STRING(lp_path, path)
FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec)
FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
FN_LOCAL_STRING(lp_refuse_options, refuse_options)
FN_LOCAL_STRING(lp_secrets_file, secrets_file)
FN_LOCAL_STRING(lp_syslog_tag, syslog_tag)
FN_LOCAL_STRING(lp_temp_dir, temp_dir)
FN_LOCAL_STRING(lp_uid, uid)
FN_LOCAL_INTEGER(lp_max_connections, max_connections)
FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
FN_LOCAL_INTEGER(lp_timeout, timeout)
FN_LOCAL_BOOL(lp_fake_super, fake_super)
FN_LOCAL_BOOL(lp_forward_lookup, forward_lookup)
FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
FN_LOCAL_BOOL(lp_list, list)
FN_LOCAL_BOOL(lp_munge_symlinks, munge_symlinks)
FN_LOCAL_BOOL(lp_numeric_ids, numeric_ids)
FN_LOCAL_BOOL(lp_read_only, read_only)
FN_LOCAL_BOOL(lp_reverse_lookup, reverse_lookup)
FN_LOCAL_BOOL(lp_strict_modes, strict_modes)
FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging)
FN_LOCAL_BOOL(lp_use_chroot, use_chroot)
FN_LOCAL_BOOL(lp_write_only, write_only)
/* Initialise the Default all_vars structure. */
void reset_daemon_vars(void)
{
memcpy(&Vars, &Defaults, sizeof Vars);
}
/* Assign a copy of v to *s. Handles NULL strings. We don't worry
* about overwriting a malloc'd string because the long-running
@@ -617,19 +278,14 @@ static void init_section(local_vars *psection)
copy_section(psection, &Vars.l);
}
/* Do a case-insensitive, whitespace-ignoring string compare. */
static int strwicmp(char *psz1, char *psz2)
/* Do a case-insensitive, whitespace-ignoring string equality check. */
static int strwiEQ(char *psz1, char *psz2)
{
/* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
/* appropriate value. */
/* If one or both strings are NULL, we return equality right away. */
if (psz1 == psz2)
return 0;
if (psz1 == NULL)
return -1;
if (psz2 == NULL)
return 1;
if (psz1 == NULL || psz2 == NULL)
return 0;
/* sync the strings on first non-whitespace */
while (1) {
@@ -637,12 +293,14 @@ static int strwicmp(char *psz1, char *psz2)
psz1++;
while (isSpace(psz2))
psz2++;
if (toUpper(psz1) != toUpper(psz2) || *psz1 == '\0' || *psz2 == '\0')
if (*psz1 == '\0' || *psz2 == '\0')
break;
if (toUpper(psz1) != toUpper(psz2))
break;
psz1++;
psz2++;
}
return *psz1 - *psz2;
return *psz1 == *psz2;
}
/* Find a section by name. Otherwise works like get_section. */
@@ -651,7 +309,7 @@ static int getsectionbyname(char *name)
int i;
for (i = section_list.count - 1; i >= 0; i--) {
if (strwicmp(iSECTION(i).name, name) == 0)
if (strwiEQ(iSECTION(i).name, name))
break;
}
@@ -691,7 +349,7 @@ static int map_parameter(char *parmname)
return -1;
for (iIndex = 0; parm_table[iIndex].label; iIndex++) {
if (strwicmp(parm_table[iIndex].label, parmname) == 0)
if (strwiEQ(parm_table[iIndex].label, parmname))
return iIndex;
}
@@ -702,16 +360,14 @@ static int map_parameter(char *parmname)
/* Set a boolean variable from the text value stored in the passed string.
* Returns True in success, False if the passed string does not correctly
* represent a boolean. */
static BOOL set_boolean(BOOL *pb, char *parmvalue)
static BOOL set_boolean(BOOL *pb, char *parmvalue, int allow_unset)
{
if (strwicmp(parmvalue, "yes") == 0
|| strwicmp(parmvalue, "true") == 0
|| strwicmp(parmvalue, "1") == 0)
if (strwiEQ(parmvalue, "yes") || strwiEQ(parmvalue, "true") || strwiEQ(parmvalue, "1"))
*pb = True;
else if (strwicmp(parmvalue, "no") == 0
|| strwicmp(parmvalue, "False") == 0
|| strwicmp(parmvalue, "0") == 0)
else if (strwiEQ(parmvalue, "no") || strwiEQ(parmvalue, "false") || strwiEQ(parmvalue, "0"))
*pb = False;
else if (allow_unset && (strwiEQ(parmvalue, "unset") || strwiEQ(parmvalue, "-1")))
*pb = Unset;
else {
rprintf(FLOG, "Badly formed boolean in configuration file: \"%s\".\n", parmvalue);
return False;
@@ -760,11 +416,15 @@ static BOOL do_parameter(char *parmname, char *parmvalue)
switch (parm_table[parmnum].type) {
case P_BOOL:
set_boolean(parm_ptr, parmvalue);
set_boolean(parm_ptr, parmvalue, False);
break;
case P_BOOL3:
set_boolean(parm_ptr, parmvalue, True);
break;
case P_BOOLREV:
set_boolean(parm_ptr, parmvalue);
set_boolean(parm_ptr, parmvalue, False);
*(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
break;
@@ -777,7 +437,7 @@ static BOOL do_parameter(char *parmname, char *parmvalue)
break;
case P_OCTAL:
sscanf(parmvalue, "%o", (int *)parm_ptr);
sscanf(parmvalue, "%o", (unsigned int *)parm_ptr);
break;
case P_PATH:
@@ -834,7 +494,7 @@ static BOOL do_section(char *sectionname)
return True;
}
isglobal = strwicmp(sectionname, GLOBAL_NAME) == 0;
isglobal = strwiEQ(sectionname, GLOBAL_NAME);
/* At the end of the global section, add any --dparam items. */
if (bInGlobalSection && !isglobal) {

79
log.c
View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2000-2001 Martin Pool <mbp@samba.org>
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,10 +34,8 @@ extern int module_id;
extern int allow_8bit_chars;
extern int protocol_version;
extern int always_checksum;
extern int preserve_times;
extern int preserve_mtimes;
extern int msgs2stderr;
extern int xfersum_type;
extern int checksum_type;
extern int stdout_format_has_i;
extern int stdout_format_has_o_or_i;
extern int logfile_format_has_i;
@@ -62,6 +60,8 @@ extern unsigned int module_dirlen;
extern char sender_file_sum[MAX_DIGEST_LEN];
extern const char undetermined_hostname[];
extern struct name_num_item *xfer_sum_nni, *file_sum_nni;
static int log_initialised;
static int logfile_was_closed;
static FILE *logfile_fp;
@@ -251,7 +251,7 @@ static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isp
void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
{
char trailing_CR_or_NL;
FILE *f = msgs2stderr ? stderr : stdout;
FILE *f = msgs2stderr == 1 ? stderr : stdout;
#ifdef ICONV_OPTION
iconv_t ic = is_utf8 && ic_recv != (iconv_t)-1 ? ic_recv : ic_chck;
#else
@@ -263,7 +263,7 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
if (len < 0)
exit_cleanup(RERR_MESSAGEIO);
if (msgs2stderr) {
if (msgs2stderr == 1) {
/* A normal daemon can get msgs2stderr set if the socket is busted, so we
* change the message destination into an FLOG message in order to try to
* get some info about an abnormal-exit into the log file. An rsh daemon
@@ -327,7 +327,7 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
exit_cleanup(RERR_MESSAGEIO);
}
if (am_server && !msgs2stderr) {
if (am_server && msgs2stderr != 1 && (msgs2stderr != 2 || f != stderr)) {
enum msgcode msg = (enum msgcode)code;
if (protocol_version < 30) {
if (msg == MSG_ERROR)
@@ -350,8 +350,7 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
output_needs_newline = 0;
}
trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r')
? buf[--len] : 0;
trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r') ? buf[--len] : '\0';
if (len && buf[0] == '\r') {
fputc('\r', f);
@@ -372,7 +371,12 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
iconvbufs(ic, &inbuf, &outbuf, inbuf.pos ? 0 : ICB_INIT);
ierrno = errno;
if (outbuf.len) {
filtered_fwrite(f, convbuf, outbuf.len, 0, 0);
char trailing = inbuf.len ? '\0' : trailing_CR_or_NL;
filtered_fwrite(f, convbuf, outbuf.len, 0, trailing);
if (trailing) {
trailing_CR_or_NL = '\0';
fflush(f);
}
outbuf.len = 0;
}
/* Log one byte of illegal/incomplete sequence and continue with
@@ -452,11 +456,17 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...)
char buf[BIGPATHBUFLEN];
size_t len;
/* snprintf returns the would-have-been length on truncation, so
* each cumulative call must be guarded; if not, sizeof buf - len
* can underflow when promoted to size_t and the next call writes
* past the buffer. */
len = snprintf(buf, sizeof buf, RSYNC_NAME ": [%s] ", who_am_i());
va_start(ap, format);
len += vsnprintf(buf + len, sizeof buf - len, format, ap);
va_end(ap);
if (len < sizeof buf) {
va_start(ap, format);
len += vsnprintf(buf + len, sizeof buf - len, format, ap);
va_end(ap);
}
if (len < sizeof buf) {
len += snprintf(buf + len, sizeof buf - len,
@@ -676,12 +686,12 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
n = NULL;
if (S_ISREG(file->mode)) {
if (always_checksum)
n = sum_as_hex(checksum_type, F_SUM(file), 1);
n = sum_as_hex(file_sum_nni->num, F_SUM(file), 1);
else if (iflags & ITEM_TRANSFER)
n = sum_as_hex(xfersum_type, sender_file_sum, 0);
n = sum_as_hex(xfer_sum_nni->num, sender_file_sum, 0);
}
if (!n) {
int sum_len = csum_len_for_type(always_checksum ? checksum_type : xfersum_type,
int sum_len = csum_len_for_type(always_checksum ? file_sum_nni->num : xfer_sum_nni->num,
always_checksum);
memset(buf2, ' ', sum_len*2);
buf2[sum_len*2] = '\0';
@@ -702,7 +712,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
c[1] = 'L';
c[3] = '.';
c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
: !preserve_times || !receiver_symlink_times
: !preserve_mtimes || !receiver_symlink_times
|| (iflags & ITEM_REPORT_TIMEFAIL) ? 'T' : 't';
} else {
c[1] = S_ISDIR(file->mode) ? 'd'
@@ -710,14 +720,15 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
: IS_DEVICE(file->mode) ? 'D' : 'f';
c[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's';
c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
: !preserve_times ? 'T' : 't';
: !preserve_mtimes ? 'T' : 't';
}
c[2] = !(iflags & ITEM_REPORT_CHANGE) ? '.' : 'c';
c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
: S_ISLNK(file->mode) ? 'U' : 'u';
c[8] = !(iflags & (ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME)) ? '.'
: BITS_SET(iflags, ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME) ? 'b'
: iflags & ITEM_REPORT_ATIME ? 'u' : 'n';
c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
c[11] = '\0';
@@ -833,14 +844,24 @@ void maybe_log_item(struct file_struct *file, int iflags, int itemizing, const c
void log_delete(const char *fname, int mode)
{
static struct {
union file_extras ex[4]; /* just in case... */
struct file_struct file;
} x; /* Zero-initialized due to static declaration. */
static struct file_struct *file = NULL;
int len = strlen(fname);
const char *fmt;
x.file.mode = mode;
if (!file) {
int extra_len = (file_extra_cnt + 2) * EXTRA_LEN;
char *bp;
#if EXTRA_ROUNDING > 0
if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
#endif
bp = new_array0(char, FILE_STRUCT_LEN + extra_len + 1);
bp += extra_len;
file = (struct file_struct *)bp;
}
file->mode = mode;
if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
if (S_ISDIR(mode))
@@ -850,14 +871,14 @@ void log_delete(const char *fname, int mode)
;
else {
fmt = stdout_format_has_o_or_i ? stdout_format : "deleting %n";
log_formatted(FCLIENT, fmt, "del.", &x.file, fname, ITEM_DELETED, NULL);
log_formatted(FCLIENT, fmt, "del.", file, fname, ITEM_DELETED, NULL);
}
if (!logfile_name || dry_run || !logfile_format)
return;
fmt = logfile_format_has_o_or_i ? logfile_format : "deleting %n";
log_formatted(FLOG, fmt, "del.", &x.file, fname, ITEM_DELETED, NULL);
log_formatted(FLOG, fmt, "del.", file, fname, ITEM_DELETED, NULL);
}
/*
@@ -886,10 +907,10 @@ void log_exit(int code, const char *file, int line)
/* VANISHED is not an error, only a warning */
if (code == RERR_VANISHED) {
rprintf(FWARNING, "rsync warning: %s (code %d) at %s(%d) [%s=%s]\n",
name, code, file, line, who_am_i(), RSYNC_VERSION);
name, code, src_file(file), line, who_am_i(), rsync_version());
} else {
rprintf(FERROR, "rsync error: %s (code %d) at %s(%d) [%s=%s]\n",
name, code, file, line, who_am_i(), RSYNC_VERSION);
name, code, src_file(file), line, who_am_i(), rsync_version());
}
}
}

View File

@@ -1,6 +1,5 @@
dnl AC_HAVE_TYPE(TYPE,INCLUDES)
AC_DEFUN([AC_HAVE_TYPE], [
AC_REQUIRE([AC_HEADER_STDC])
cv=`echo "$1" | sed 'y%./+- %__p__%'`
AC_MSG_CHECKING(for $1)
AC_CACHE_VAL([ac_cv_type_$cv],

285
main.c
View File

@@ -4,7 +4,7 @@
* Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,6 +28,9 @@
#include <locale.h>
#endif
#include <popt.h>
#ifdef __TANDEM
#include <floss.h(floss_execlp)>
#endif
extern int dry_run;
extern int list_only;
@@ -45,6 +48,7 @@ extern int called_from_signal_handler;
extern int need_messages_from_generator;
extern int kluge_around_eof;
extern int got_xfer_error;
extern int old_style_args;
extern int msgs2stderr;
extern int module_id;
extern int read_only;
@@ -54,6 +58,7 @@ extern int copy_unsafe_links;
extern int keep_dirlinks;
extern int preserve_hard_links;
extern int protocol_version;
extern int mkpath_dest_arg;
extern int file_total;
extern int recurse;
extern int xfer_dirs;
@@ -61,7 +66,7 @@ extern int protect_args;
extern int relative_paths;
extern int sanitize_paths;
extern int curr_dir_depth;
extern int curr_dir_len;
extern unsigned int curr_dir_len;
extern int module_id;
extern int rsync_port;
extern int whole_file;
@@ -83,6 +88,9 @@ extern BOOL shutting_down;
extern int backup_dir_len;
extern int basis_dir_cnt;
extern int default_af_hint;
extern int stdout_format_has_i;
extern int trust_sender_filter;
extern int trust_sender_args;
extern struct stats stats;
extern char *stdout_format;
extern char *logfile_format;
@@ -93,18 +101,19 @@ extern char *shell_cmd;
extern char *password_file;
extern char *backup_dir;
extern char *copy_as;
extern char *tmpdir;
extern char curr_dir[MAXPATHLEN];
extern char backup_dir_buf[MAXPATHLEN];
extern char *basis_dir[MAX_BASIS_DIRS+1];
extern struct file_list *first_flist;
extern filter_rule_list daemon_filter_list;
extern filter_rule_list daemon_filter_list, implied_filter_list;
uid_t our_uid;
gid_t our_gid;
int am_receiver = 0; /* Only set to 1 after the receiver/generator fork. */
int am_generator = 0; /* Only set to 1 after the receiver/generator fork. */
int local_server = 0;
int daemon_over_rsh = 0;
int daemon_connection = 0; /* 0 = no daemon, 1 = daemon via remote shell, -1 = daemon via socket */
mode_t orig_umask = 0;
int batch_gen_fd = -1;
int sender_keeps_checksum = 0;
@@ -230,11 +239,11 @@ void write_del_stats(int f)
void read_del_stats(int f)
{
stats.deleted_files = read_varint(f);
stats.deleted_files += stats.deleted_dirs = read_varint(f);
stats.deleted_files += stats.deleted_symlinks = read_varint(f);
stats.deleted_files += stats.deleted_devices = read_varint(f);
stats.deleted_files += stats.deleted_specials = read_varint(f);
stats.deleted_files = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_files");
stats.deleted_files += stats.deleted_dirs = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_dirs");
stats.deleted_files += stats.deleted_symlinks = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_symlinks");
stats.deleted_files += stats.deleted_devices = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_devices");
stats.deleted_files += stats.deleted_specials = read_varint_bounded(f, 0, MAX_WIRE_DEL_STAT, "deleted_specials");
}
static void become_copy_as_user()
@@ -299,7 +308,7 @@ static void become_copy_as_user()
our_uid = MY_UID();
our_gid = MY_GID();
am_root = (our_uid == 0);
am_root = (our_uid == ROOT_UID);
if (gname)
gname[-1] = ':';
@@ -377,7 +386,7 @@ static void handle_stats(int f)
static void output_itemized_counts(const char *prefix, int *counts)
{
static char *labels[] = { "reg", "dir", "link", "dev", "special" };
static char *const labels[] = { "reg", "dir", "link", "dev", "special" };
char buf[1024], *pre = " (";
int j, len = 0;
int total = counts[0];
@@ -385,9 +394,18 @@ static void output_itemized_counts(const char *prefix, int *counts)
counts[0] -= counts[1] + counts[2] + counts[3] + counts[4];
for (j = 0; j < 5; j++) {
if (counts[j]) {
/* snprintf can return more than its size arg
* on truncation; keep len <= sizeof buf - 2 so
* the closing ')' and trailing NUL always
* have room and the next iteration's
* sizeof buf - len - 2 cannot underflow. */
if (len >= (int)sizeof buf - 2)
break;
len += snprintf(buf+len, sizeof buf - len - 2,
"%s%s: %s",
pre, labels[j], comma_num(counts[j]));
if (len > (int)sizeof buf - 2)
len = (int)sizeof buf - 2;
pre = ", ";
}
}
@@ -461,38 +479,33 @@ static void output_summary(void)
**/
static void show_malloc_stats(void)
{
#ifdef HAVE_MALLINFO
struct mallinfo mi;
mi = mallinfo();
#ifdef MEM_ALLOC_INFO
struct MEM_ALLOC_INFO mi = MEM_ALLOC_INFO(); /* mallinfo or mallinfo2 */
rprintf(FCLIENT, "\n");
rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n",
(int)getpid(), am_server ? "server " : "",
am_daemon ? "daemon " : "", who_am_i());
rprintf(FINFO, " arena: %10ld (bytes from sbrk)\n",
(long)mi.arena);
rprintf(FINFO, " ordblks: %10ld (chunks not in use)\n",
(long)mi.ordblks);
rprintf(FINFO, " smblks: %10ld\n",
(long)mi.smblks);
rprintf(FINFO, " hblks: %10ld (chunks from mmap)\n",
(long)mi.hblks);
rprintf(FINFO, " hblkhd: %10ld (bytes from mmap)\n",
(long)mi.hblkhd);
rprintf(FINFO, " allmem: %10ld (bytes from sbrk + mmap)\n",
(long)mi.arena + mi.hblkhd);
rprintf(FINFO, " usmblks: %10ld\n",
(long)mi.usmblks);
rprintf(FINFO, " fsmblks: %10ld\n",
(long)mi.fsmblks);
rprintf(FINFO, " uordblks: %10ld (bytes used)\n",
(long)mi.uordblks);
rprintf(FINFO, " fordblks: %10ld (bytes free)\n",
(long)mi.fordblks);
rprintf(FINFO, " keepcost: %10ld (bytes in releasable chunk)\n",
(long)mi.keepcost);
#endif /* HAVE_MALLINFO */
#define PRINT_ALLOC_NUM(title, descr, num) \
rprintf(FINFO, " %-11s%10" SIZE_T_FMT_MOD "d (" descr ")\n", \
title ":", (SIZE_T_FMT_CAST)(num));
PRINT_ALLOC_NUM("arena", "bytes from sbrk", mi.arena);
PRINT_ALLOC_NUM("ordblks", "chunks not in use", mi.ordblks);
PRINT_ALLOC_NUM("smblks", "free fastbin blocks", mi.smblks);
PRINT_ALLOC_NUM("hblks", "chunks from mmap", mi.hblks);
PRINT_ALLOC_NUM("hblkhd", "bytes from mmap", mi.hblkhd);
PRINT_ALLOC_NUM("allmem", "bytes from sbrk + mmap", mi.arena + mi.hblkhd);
PRINT_ALLOC_NUM("usmblks", "always 0", mi.usmblks);
PRINT_ALLOC_NUM("fsmblks", "bytes in freed fastbin blocks", mi.fsmblks);
PRINT_ALLOC_NUM("uordblks", "bytes used", mi.uordblks);
PRINT_ALLOC_NUM("fordblks", "bytes free", mi.fordblks);
PRINT_ALLOC_NUM("keepcost", "bytes in releasable chunk", mi.keepcost);
#undef PRINT_ALLOC_NUM
#endif /* MEM_ALLOC_INFO */
}
@@ -562,12 +575,12 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
#ifdef HAVE_REMSH
/* remsh (on HPUX) takes the arguments the other way around */
args[argc++] = machine;
if (user && !(daemon_over_rsh && dash_l_set)) {
if (user && !(daemon_connection && dash_l_set)) {
args[argc++] = "-l";
args[argc++] = user;
}
#else
if (user && !(daemon_over_rsh && dash_l_set)) {
if (user && !(daemon_connection && dash_l_set)) {
args[argc++] = "-l";
args[argc++] = user;
}
@@ -587,7 +600,11 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
if (blocking_io < 0 && (strcmp(t, "rsh") == 0 || strcmp(t, "remsh") == 0))
blocking_io = 1;
server_options(args, &argc);
if (daemon_connection > 0) {
args[argc++] = "--server";
args[argc++] = "--daemon";
} else
server_options(args, &argc);
if (argc >= MAX_ARGS - 2)
goto arg_overflow;
@@ -595,18 +612,14 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
args[argc++] = ".";
if (!daemon_over_rsh) {
if (!daemon_connection) {
while (remote_argc > 0) {
if (argc >= MAX_ARGS - 1) {
arg_overflow:
rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
exit_cleanup(RERR_SYNTAX);
}
if (**remote_argv == '-') {
if (asprintf(args + argc++, "./%s", *remote_argv++) < 0)
out_of_memory("do_cmd");
} else
args[argc++] = *remote_argv++;
args[argc++] = safe_arg(NULL, *remote_argv++);
remote_argc--;
}
}
@@ -648,7 +661,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
#ifdef ICONV_CONST
setup_iconv();
#endif
if (protect_args && !daemon_over_rsh)
if (protect_args && !daemon_connection)
send_protected_args(*f_out_p, args);
}
@@ -658,6 +671,16 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
return pid;
}
/* Older versions turn an empty string as a reference to the current directory.
* We now treat this as an error unless --old-args was used. */
static char *dot_dir_or_error()
{
if (old_style_args || am_server)
return ".";
rprintf(FERROR, "Empty destination arg specified (use \".\" or see --old-args).\n");
exit_cleanup(RERR_SYNTAX);
}
/* The receiving side operates in one of two modes:
*
* 1. it receives any number of files into a destination directory,
@@ -674,7 +697,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
static char *get_local_name(struct file_list *flist, char *dest_path)
{
STRUCT_STAT st;
int statret;
int statret, trailing_slash;
char *cp;
if (DEBUG_GTE(RECV, 1)) {
@@ -685,9 +708,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
if (!dest_path || list_only)
return NULL;
/* Treat an empty string as a copy into the current directory. */
if (!*dest_path)
dest_path = ".";
dest_path = dot_dir_or_error();
if (daemon_filter_list.head) {
char *slash = strrchr(dest_path, '/');
@@ -707,7 +729,29 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
}
/* See what currently exists at the destination. */
if ((statret = do_stat(dest_path, &st)) == 0) {
statret = do_stat(dest_path, &st);
cp = strrchr(dest_path, '/');
trailing_slash = cp && !cp[1];
if (mkpath_dest_arg && statret < 0 && (cp || file_total > 1)) {
int save_errno = errno;
int ret = make_path(dest_path, file_total > 1 && !trailing_slash ? 0 : MKP_DROP_NAME);
if (ret < 0)
goto mkdir_error;
if (ret && (INFO_GTE(NAME, 1) || stdout_format_has_i)) {
if (file_total == 1 || trailing_slash)
*cp = '\0';
rprintf(FINFO, "created %d director%s for %s\n", ret, ret == 1 ? "y" : "ies", dest_path);
if (file_total == 1 || trailing_slash)
*cp = '/';
}
if (ret)
statret = do_stat(dest_path, &st);
else
errno = save_errno;
}
if (statret == 0) {
/* If the destination is a dir, enter it and use mode 1. */
if (S_ISDIR(st.st_mode)) {
if (!change_dir(dest_path, CD_NORMAL)) {
@@ -737,15 +781,12 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
exit_cleanup(RERR_FILESELECT);
}
cp = strrchr(dest_path, '/');
/* If we need a destination directory because the transfer is not
* of a single non-directory or the user has requested one via a
* destination path ending in a slash, create one and use mode 1. */
if (file_total > 1 || (cp && !cp[1])) {
/* Lop off the final slash (if any). */
if (cp && !cp[1])
*cp = '\0';
if (file_total > 1 || trailing_slash) {
if (trailing_slash)
*cp = '\0'; /* Lop off the final slash (if any). */
if (statret == 0) {
rprintf(FERROR, "ERROR: destination path is not a directory\n");
@@ -753,6 +794,7 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
}
if (do_mkdir(dest_path, ACCESSPERMS) != 0) {
mkdir_error:
rsyserr(FERROR, errno, "mkdir %s failed",
full_fname(dest_path));
exit_cleanup(RERR_FILEIO);
@@ -762,7 +804,7 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
&& strcmp(flist->files[flist->low]->basename, ".") == 0)
flist->files[0]->flags |= FLAG_DIR_CREATED;
if (INFO_GTE(NAME, 1))
if (INFO_GTE(NAME, 1) || stdout_format_has_i)
rprintf(FINFO, "created directory %s\n", dest_path);
if (dry_run) {
@@ -790,7 +832,16 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
dest_path = "/";
*cp = '\0';
if (!change_dir(dest_path, CD_NORMAL)) {
if (dry_run && mkpath_dest_arg && do_stat(dest_path, &st) < 0) {
/* --mkpath would have created this parent dir, but a dry run did
* not, so don't chdir into it; flag the destination as not yet
* present (as the dir-creation path above does) so the generator
* doesn't try to compare against the missing tree (#880). Only
* the missing-parent case is touched, so an ordinary file-to-file
* dry run still itemizes against an existing destination. */
dry_run++;
change_dir(dest_path, CD_SKIP_CHDIR);
} else if (!change_dir(dest_path, CD_NORMAL)) {
rsyserr(FERROR, errno, "change_dir#3 %s failed",
full_fname(dest_path));
exit_cleanup(RERR_FILESELECT);
@@ -977,6 +1028,23 @@ static int do_recv(int f_in, int f_out, char *local_name)
backup_dir_buf[backup_dir_len-1] = '/';
}
if (tmpdir) {
STRUCT_STAT st;
int ret = do_stat(tmpdir, &st);
if (ret < 0 || !S_ISDIR(st.st_mode)) {
if (ret == 0) {
rprintf(FERROR, "The temp-dir is not a directory: %s\n", tmpdir);
exit_cleanup(RERR_SYNTAX);
}
if (errno == ENOENT) {
rprintf(FERROR, "The temp-dir does not exist: %s\n", tmpdir);
exit_cleanup(RERR_SYNTAX);
}
rprintf(FERROR, "Failed to stat temp-dir %s: %s\n", tmpdir, strerror(errno));
exit_cleanup(RERR_FILEIO);
}
}
io_flush(FULL_FLUSH);
if ((pid = do_fork()) == -1) {
@@ -1037,6 +1105,7 @@ static int do_recv(int f_in, int f_out, char *local_name)
}
am_generator = 1;
implied_filter_list.head = implied_filter_list.tail = NULL;
flist_receiving_enabled = True;
io_end_multiplex_in(MPLX_SWITCHING);
@@ -1083,7 +1152,7 @@ static void do_server_recv(int f_in, int f_out, int argc, char *argv[])
char *local_name = NULL;
int negated_levels;
if (filesfrom_fd >= 0 && !msgs2stderr && protocol_version < 31) {
if (filesfrom_fd >= 0 && msgs2stderr != 1 && protocol_version < 31) {
/* We can't mix messages with files-from data on the socket,
* so temporarily turn off info/debug messages. */
negate_output_levels();
@@ -1332,15 +1401,6 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
return MAX(exit_code, exit_code2);
}
static void dup_argv(char *argv[])
{
int i;
for (i = 0; argv[i]; i++)
argv[i] = strdup(argv[i]);
}
/* Start a client for either type of remote connection. Work out
* whether the arguments request a remote shell or rsyncd connection,
* and call the appropriate connection function, then run_client.
@@ -1356,10 +1416,6 @@ static int start_client(int argc, char *argv[])
int ret;
pid_t pid;
/* Don't clobber argv[] so that ps(1) can still show the right
* command line. */
dup_argv(argv);
if (!read_batch) { /* for read_batch, NO source is specified */
char *path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
if (path) { /* source is remote */
@@ -1386,12 +1442,14 @@ static int start_client(int argc, char *argv[])
}
am_sender = 0;
if (rsync_port)
daemon_over_rsh = shell_cmd ? 1 : -1;
daemon_connection = shell_cmd ? 1 : -1;
} else { /* source is local, check dest arg */
am_sender = 1;
if (argc > 1) {
p = argv[--argc];
if (!*p)
p = dot_dir_or_error();
remote_argv = argv + argc;
} else {
static char *dotarg[1] = { "." };
@@ -1418,7 +1476,7 @@ static int start_client(int argc, char *argv[])
} else { /* hostspec was found, so dest is remote */
argv[argc] = path;
if (rsync_port)
daemon_over_rsh = shell_cmd ? 1 : -1;
daemon_connection = shell_cmd ? 1 : -1;
}
}
} else { /* read_batch */
@@ -1432,6 +1490,12 @@ static int start_client(int argc, char *argv[])
rsync_port = 0;
}
/* A local transfer doesn't unbackslash anything, so leave the args alone. */
if (local_server) {
old_style_args = 2;
trust_sender_args = trust_sender_filter = 1;
}
if (!rsync_port && remote_argc && !**remote_argv) /* Turn an empty arg into a dot dir. */
*remote_argv = ".";
@@ -1439,8 +1503,15 @@ static int start_client(int argc, char *argv[])
char *dummy_host;
int dummy_port = rsync_port;
int i;
if (!argv[0][0])
goto invalid_empty;
/* For local source, extra source args must not have hostspec. */
for (i = 1; i < argc; i++) {
if (!argv[i][0]) {
invalid_empty:
rprintf(FERROR, "Empty source arg specified.\n");
exit_cleanup(RERR_SYNTAX);
}
if (check_for_hostspec(argv[i], &dummy_host, &dummy_port)) {
rprintf(FERROR, "Unexpected remote arg: %s\n", argv[i]);
exit_cleanup(RERR_SYNTAX);
@@ -1450,6 +1521,8 @@ static int start_client(int argc, char *argv[])
char *dummy_host;
int dummy_port = rsync_port;
int i;
if (filesfrom_fd < 0)
add_implied_include(remote_argv[0], daemon_connection);
/* For remote source, any extra source args must have either
* the same hostname or an empty hostname. */
for (i = 1; i < remote_argc; i++) {
@@ -1473,6 +1546,7 @@ static int start_client(int argc, char *argv[])
if (!rsync_port && !*arg) /* Turn an empty arg into a dot dir. */
arg = ".";
remote_argv[i] = arg;
add_implied_include(arg, daemon_connection);
}
}
@@ -1481,10 +1555,10 @@ static int start_client(int argc, char *argv[])
else
env_port = rsync_port;
if (daemon_over_rsh < 0)
if (daemon_connection < 0)
return start_socket_client(shell_machine, remote_argc, remote_argv, argc, argv);
if (password_file && !daemon_over_rsh) {
if (password_file && !daemon_connection) {
rprintf(FERROR, "The --password-file option may only be "
"used when accessing an rsync daemon.\n");
exit_cleanup(RERR_SYNTAX);
@@ -1503,6 +1577,10 @@ static int start_client(int argc, char *argv[])
shell_user = shell_machine;
shell_machine = p+1;
}
if (*shell_machine == '-') {
rprintf(FERROR, "Invalid remote host: hostnames may not start with '-'.\n");
exit_cleanup(RERR_SYNTAX);
}
}
if (DEBUG_GTE(CMD, 2)) {
@@ -1512,15 +1590,17 @@ static int start_client(int argc, char *argv[])
}
#ifdef HAVE_PUTENV
if (daemon_over_rsh)
if (daemon_connection)
set_env_num("RSYNC_PORT", env_port);
#else
(void)env_port;
#endif
pid = do_cmd(shell_cmd, shell_machine, shell_user, remote_argv, remote_argc, &f_in, &f_out);
/* if we're running an rsync server on the remote host over a
* remote shell command, we need to do the RSYNCD protocol first */
if (daemon_over_rsh) {
if (daemon_connection) {
int tmpret;
tmpret = start_inband_exchange(f_in, f_out, shell_user, remote_argc, remote_argv);
if (tmpret < 0)
@@ -1547,16 +1627,23 @@ static void sigusr2_handler(UNUSED(int val))
if (!am_server)
output_summary();
close_all();
#ifdef GCOV_COVERAGE
/* The receiver child is killed here via SIGUSR2 and exits with _exit(),
* bypassing the gcov atexit flush; without this it writes no .gcda. */
{ extern void __gcov_dump(void); __gcov_dump(); }
#endif
if (got_xfer_error)
_exit(RERR_PARTIAL);
_exit(0);
}
#if defined SIGINFO || defined SIGVTALRM
static void siginfo_handler(UNUSED(int val))
{
if (!am_server && !INFO_GTE(PROGRESS, 1))
want_progress_now = True;
}
#endif
void remember_children(UNUSED(int val))
{
@@ -1584,7 +1671,6 @@ void remember_children(UNUSED(int val))
#endif
}
/**
* This routine catches signals and tries to send them to gdb.
*
@@ -1608,7 +1694,6 @@ const char *get_panic_action(void)
return "xterm -display :0 -T Panic -n Panic -e gdb /proc/%d/exe %d";
}
/**
* Handle a fatal signal by launching a debugger, controlled by $RSYNC_PANIC_ACTION.
*
@@ -1632,6 +1717,22 @@ static void rsync_panic_handler(UNUSED(int whatsig))
}
#endif
static void unset_env_var(const char *var)
{
#ifdef HAVE_UNSETENV
unsetenv(var);
#else
#ifdef HAVE_PUTENV
char *mem;
if (asprintf(&mem, "%s=", var) < 0)
out_of_memory("unset_env_var");
putenv(mem);
#else
(void)var;
#endif
#endif
}
int main(int argc,char *argv[])
{
@@ -1667,7 +1768,22 @@ int main(int argc,char *argv[])
starttime = time(NULL);
our_uid = MY_UID();
our_gid = MY_GID();
am_root = our_uid == 0;
am_root = our_uid == ROOT_UID;
// DISPLAY should not be emptied unconditionally
if (!getenv("SSH_ASKPASS"))
unset_env_var("DISPLAY");
#if defined USE_OPENSSL && defined SET_OPENSSL_CONF
#define TO_STR2(x) #x
#define TO_STR(x) TO_STR2(x)
/* ./configure --with-openssl-conf=/etc/ssl/openssl-rsync.cnf
* defines SET_OPENSSL_CONF as that unquoted pathname. */
if (!getenv("OPENSSL_CONF")) /* Don't override it if it's already set. */
set_env_str("OPENSSL_CONF", TO_STR(SET_OPENSSL_CONF));
#undef TO_STR
#undef TO_STR2
#endif
memset(&stats, 0, sizeof(stats));
@@ -1687,6 +1803,7 @@ int main(int argc,char *argv[])
#if defined CONFIG_LOCALE && defined HAVE_SETLOCALE
setlocale(LC_CTYPE, "");
setlocale(LC_NUMERIC, "");
#endif
if (!parse_arguments(&argc, (const char ***) &argv)) {
@@ -1737,7 +1854,7 @@ int main(int argc,char *argv[])
if (am_server && protect_args) {
char buf[MAXPATHLEN];
protect_args = 2;
read_args(STDIN_FILENO, NULL, buf, sizeof buf, 1, &argv, &argc, NULL);
read_args(STDIN_FILENO, NULL, buf, sizeof buf, 1, 0, &argv, &argc, NULL);
if (!parse_arguments(&argc, (const char ***) &argv)) {
option_error();
exit_cleanup(RERR_SYNTAX);

33
match.c
View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2003-2020 Wayne Davison
* Copyright (C) 2003-2023 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,7 +24,9 @@
extern int checksum_seed;
extern int append_mode;
extern int xfersum_type;
extern struct name_num_item *xfer_sum_nni;
extern int xfer_sum_len;
int updating_basis_file;
char sender_file_sum[MAX_DIGEST_LEN];
@@ -140,11 +142,14 @@ static void hash_search(int f,struct sum_struct *s,
{
OFF_T offset, aligned_offset, end;
int32 k, want_i, aligned_i, backup;
char sum2[SUM_LENGTH];
char sum2[MAX_DIGEST_LEN];
uint32 s1, s2, sum;
int more;
schar *map;
// prevent possible memory leaks
memset(sum2, 0, sizeof sum2);
/* want_i is used to encourage adjacent matches, allowing the RLL
* coding of the output to work more efficiently. */
want_i = 0;
@@ -230,7 +235,7 @@ static void hash_search(int f,struct sum_struct *s,
done_csum2 = 1;
}
if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) {
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0) {
false_alarms++;
continue;
}
@@ -250,7 +255,7 @@ static void hash_search(int f,struct sum_struct *s,
if (i != aligned_i) {
if (sum != s->sums[aligned_i].sum1
|| l != s->sums[aligned_i].len
|| memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0)
|| memcmp(sum2, sum2_at(s, aligned_i), s->s2length) != 0)
goto check_want_i;
i = aligned_i;
}
@@ -269,7 +274,7 @@ static void hash_search(int f,struct sum_struct *s,
if (sum != s->sums[i].sum1)
goto check_want_i;
get_checksum2((char *)map, l, sum2);
if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0)
if (memcmp(sum2, sum2_at(s, i), s->s2length) != 0)
goto check_want_i;
/* OK, we have a re-alignment match. Bump the offset
* forward to the new match point. */
@@ -288,7 +293,7 @@ static void hash_search(int f,struct sum_struct *s,
&& (!updating_basis_file || s->sums[want_i].offset >= offset
|| s->sums[want_i].flags & SUMFLG_SAME_OFFSET)
&& sum == s->sums[want_i].sum1
&& memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) {
&& memcmp(sum2, sum2_at(s, want_i), s->s2length) == 0) {
/* we've found an adjacent match - the RLL coder
* will be happy */
i = want_i;
@@ -356,15 +361,13 @@ static void hash_search(int f,struct sum_struct *s,
**/
void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
{
int sum_len;
last_match = 0;
false_alarms = 0;
hash_hits = 0;
matches = 0;
data_transfer = 0;
sum_init(xfersum_type, checksum_seed);
sum_init(xfer_sum_nni, checksum_seed);
if (append_mode > 0) {
if (append_mode == 2) {
@@ -405,22 +408,22 @@ void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
matched(f, s, buf, len, -1);
}
sum_len = sum_end(sender_file_sum);
sum_end(sender_file_sum);
/* If we had a read error, send a bad checksum. We use all bits
* off as long as the checksum doesn't happen to be that, in
* which case we turn the last 0 bit into a 1. */
if (buf && buf->status != 0) {
int i;
for (i = 0; i < sum_len && sender_file_sum[i] == 0; i++) {}
memset(sender_file_sum, 0, sum_len);
if (i == sum_len)
for (i = 0; i < xfer_sum_len && sender_file_sum[i] == 0; i++) {}
memset(sender_file_sum, 0, xfer_sum_len);
if (i == xfer_sum_len)
sender_file_sum[i-1]++;
}
if (DEBUG_GTE(DELTASUM, 2))
rprintf(FINFO,"sending file_sum\n");
write_buf(f, sender_file_sum, sum_len);
write_buf(f, sender_file_sum, xfer_sum_len);
if (DEBUG_GTE(DELTASUM, 2)) {
rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n",

View File

@@ -1,25 +1,21 @@
#!/bin/sh
if [ x"$2" = x ]; then
echo "Usage: $0 SRC_DIR NAME.NUM.md" 1>&2
if [ $# != 1 ]; then
echo "Usage: $0 NAME.NUM.md" 1>&2
exit 1
fi
srcdir="$1"
inname="$2"
inname="$1"
srcdir=`dirname "$0"`
flagfile="$srcdir/.md2man-works"
if [ ! -d "$srcdir" ]; then
echo "The specified SRC_DIR is not a directory: $srcdir" 1>&2
exit 1
fi
force_flagfile="$srcdir/.md2man-force"
if [ ! -f "$flagfile" ]; then
# We test our smallest manpage just to see if the python setup works.
if "$srcdir/md2man" --test "$srcdir/rsync-ssl.1.md" >/dev/null 2>&1; then
if "$srcdir/md-convert" --test "$srcdir/rsync-ssl.1.md" >/dev/null 2>&1; then
touch $flagfile
else
outname=`echo "$inname" | sed 's/\.md$//'`
outname=`basename "$inname" .md`
if [ -f "$outname" ]; then
exit 0
elif [ -f "$srcdir/$outname" ]; then
@@ -37,4 +33,10 @@ if [ ! -f "$flagfile" ]; then
fi
fi
"$srcdir/md2man" "$srcdir/$inname"
if [ -f "$force_flagfile" ]; then
opt='--force-link-text'
else
opt=''
fi
"$srcdir/md-convert" $opt "$srcdir/$inname"

667
md-convert Executable file
View File

@@ -0,0 +1,667 @@
#!/usr/bin/env python3
# This script transforms markdown files into html and (optionally) nroff. The
# output files are written into the current directory named for the input file
# without the .md suffix and either the .html suffix or no suffix.
#
# If the input .md file has a section number at the end of the name (e.g.,
# rsync.1.md) a nroff file is also output (PROJ.NUM.md -> PROJ.NUM).
#
# The markdown input format has one extra extension: if a numbered list starts
# at 0, it is turned into a description list. The dl's dt tag is taken from the
# contents of the first tag inside the li, which is usually a p, code, or
# strong tag.
#
# The cmarkgfm or commonmark lib is used to transforms the input file into
# html. Then, the html.parser is used as a state machine that lets us tweak
# the html and (optionally) output nroff data based on the html tags.
#
# If the string @USE_GFM_PARSER@ exists in the file, the string is removed and
# a github-flavored-markup parser is used to parse the file.
#
# The man-page .md files also get the vars @VERSION@, @BINDIR@, and @LIBDIR@
# substituted. Some of these values depend on the Makefile $(prefix) (see the
# generated Makefile). If the maintainer wants to build files for /usr/local
# while creating release-ready man-page files for /usr, use the environment to
# set RSYNC_OVERRIDE_PREFIX=/usr.
# Copyright (C) 2020 - 2021 Wayne Davison
#
# This program is freely redistributable.
import os, sys, re, argparse, subprocess, time
from html.parser import HTMLParser
VALID_PAGES = 'README INSTALL COPYING rsync.1 rrsync.1 rsync-ssl.1 rsyncd.conf.5'.split()
CONSUMES_TXT = set('h1 h2 h3 p li pre'.split())
HTML_START = """\
<html><head>
<title>%TITLE%</title>
<meta charset="UTF-8"/>
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
<style>
body {
max-width: 50em;
margin: auto;
}
body, b, strong, u {
font-family: 'Roboto', sans-serif;
}
a.tgt { font-face: symbol; font-weight: 400; font-size: 70%; visibility: hidden; text-decoration: none; color: #ddd; padding: 0 4px; border: 0; }
a.tgt:after { content: '🔗'; }
a.tgt:hover { color: #444; background-color: #eaeaea; }
h1:hover > a.tgt, h2:hover > a.tgt, h3:hover > a.tgt, dt:hover > a.tgt { visibility: visible; }
code {
font-family: 'Roboto Mono', monospace;
font-weight: bold;
white-space: pre;
}
pre code {
display: block;
font-weight: normal;
}
blockquote pre code {
background: #f1f1f1;
}
dd p:first-of-type {
margin-block-start: 0em;
}
</style>
</head><body>
"""
TABLE_STYLE = """\
table {
border-color: grey;
border-spacing: 0;
}
tr {
border-top: 1px solid grey;
}
tr:nth-child(2n) {
background-color: #f6f8fa;
}
th, td {
border: 1px solid #dfe2e5;
text-align: center;
padding-left: 1em;
padding-right: 1em;
}
"""
MAN_HTML_END = """\
<div style="float: right"><p><i>%s</i></p></div>
"""
HTML_END = """\
</body></html>
"""
MAN_START = r"""
.TH "%s" "%s" "%s" "%s" "User Commands"
.\" prefix=%s
""".lstrip()
MAN_END = """\
"""
NORM_FONT = ('\1', r"\fP")
BOLD_FONT = ('\2', r"\fB")
UNDR_FONT = ('\3', r"\fI")
NBR_DASH = ('\4', r"\-")
NBR_SPACE = ('\xa0', r"\ ")
FILENAME_RE = re.compile(r'^(?P<fn>(?P<srcdir>.+/)?(?P<name>(?P<prog>[^/]+?)(\.(?P<sect>\d+))?)\.md)$')
ASSIGNMENT_RE = re.compile(r'^(\w+)=(.+)')
VER_RE = re.compile(r'^#define\s+RSYNC_VERSION\s+"(\d.+?)"', re.M)
TZ_RE = re.compile(r'^#define\s+MAINTAINER_TZ_OFFSET\s+(-?\d+(\.\d+)?)', re.M)
VAR_REF_RE = re.compile(r'\$\{(\w+)\}')
VERSION_RE = re.compile(r' (\d[.\d]+)[, ]')
BIN_CHARS_RE = re.compile(r'[\1-\7]+')
LONG_OPT_DASH_RE = re.compile(r'(--\w[-\w]+)')
SPACE_DOUBLE_DASH_RE = re.compile(r'\s--(\s)')
NON_SPACE_SINGLE_DASH_RE = re.compile(r'(^|\W)-')
WHITESPACE_RE = re.compile(r'\s')
CODE_BLOCK_RE = re.compile(r'[%s]([^=%s]+)[=%s]' % (BOLD_FONT[0], NORM_FONT[0], NORM_FONT[0]))
NBR_DASH_RE = re.compile(r'[%s]' % NBR_DASH[0])
INVALID_TARGET_CHARS_RE = re.compile(r'[^-A-Za-z0-9._]')
INVALID_START_CHAR_RE = re.compile(r'^([^A-Za-z0-9])')
MANIFY_LINESTART_RE = re.compile(r"^(['.])", flags=re.M)
md_parser = None
env_subs = { }
warning_count = 0
def main():
for mdfn in args.mdfiles:
parse_md_file(mdfn)
if args.test:
print("The test was successful.")
def parse_md_file(mdfn):
fi = FILENAME_RE.match(mdfn)
if not fi:
die('Failed to parse a md input file name:', mdfn)
fi = argparse.Namespace(**fi.groupdict())
fi.want_manpage = not not fi.sect
if fi.want_manpage:
fi.title = fi.prog + '(' + fi.sect + ') manpage'
else:
fi.title = fi.prog + ' for rsync'
if fi.want_manpage:
if not env_subs:
find_man_substitutions()
prog_ver = 'rsync ' + env_subs['VERSION']
if fi.prog != 'rsync':
prog_ver = fi.prog + ' from ' + prog_ver
fi.man_headings = (fi.prog, fi.sect, env_subs['date'], prog_ver, env_subs['prefix'])
with open(mdfn, 'r', encoding='utf-8') as fh:
txt = fh.read()
use_gfm_parser = '@USE_GFM_PARSER@' in txt
if use_gfm_parser:
txt = txt.replace('@USE_GFM_PARSER@', '')
if fi.want_manpage:
txt = (txt.replace('@VERSION@', env_subs['VERSION'])
.replace('@BINDIR@', env_subs['bindir'])
.replace('@LIBDIR@', env_subs['libdir']))
if use_gfm_parser:
if not gfm_parser:
die('Input file requires cmarkgfm parser:', mdfn)
fi.html_in = gfm_parser(txt)
else:
fi.html_in = md_parser(txt)
txt = None
TransformHtml(fi)
if args.test:
return
output_list = [ (fi.name + '.html', fi.html_out) ]
if fi.want_manpage:
output_list += [ (fi.name, fi.man_out) ]
for fn, txt in output_list:
if args.dest and args.dest != '.':
fn = os.path.join(args.dest, fn)
if os.path.lexists(fn):
os.unlink(fn)
print("Wrote:", fn)
with open(fn, 'w', encoding='utf-8') as fh:
fh.write(txt)
def find_man_substitutions():
srcdir = os.path.dirname(sys.argv[0]) + '/'
mtime = 0
git_dir = srcdir + '.git'
if os.path.lexists(git_dir):
mtime = int(subprocess.check_output(['git', '--git-dir', git_dir, 'log', '-1', '--format=%at']))
# Allow "prefix" to be overridden via the environment:
env_subs['prefix'] = os.environ.get('RSYNC_OVERRIDE_PREFIX', None)
if args.test:
env_subs['VERSION'] = '1.0.0'
env_subs['bindir'] = '/usr/bin'
env_subs['libdir'] = '/usr/lib/rsync'
tz_offset = 0
else:
for fn in (srcdir + 'version.h', 'Makefile'):
try:
st = os.lstat(fn)
except OSError:
die('Failed to find', srcdir + fn)
if not mtime:
mtime = st.st_mtime
with open(srcdir + 'version.h', 'r', encoding='utf-8') as fh:
txt = fh.read()
m = VER_RE.search(txt)
env_subs['VERSION'] = m.group(1)
m = TZ_RE.search(txt) # the tzdata lib may not be installed, so we use a simple hour offset
tz_offset = float(m.group(1)) * 60 * 60
with open('Makefile', 'r', encoding='utf-8') as fh:
for line in fh:
m = ASSIGNMENT_RE.match(line)
if not m:
continue
var, val = (m.group(1), m.group(2))
if var == 'prefix' and env_subs[var] is not None:
continue
while VAR_REF_RE.search(val):
val = VAR_REF_RE.sub(lambda m: env_subs[m.group(1)], val)
env_subs[var] = val
if var == 'srcdir':
break
env_subs['date'] = time.strftime('%d %b %Y', time.gmtime(mtime + tz_offset)).lstrip('0')
if 'SOURCE_DATE_EPOCH' in os.environ:
env_subs['date'] = time.strftime('%d %b %Y', time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
def html_via_commonmark(txt):
return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
class TransformHtml(HTMLParser):
def __init__(self, fi):
HTMLParser.__init__(self, convert_charrefs=True)
self.fn = fi.fn
st = self.state = argparse.Namespace(
list_state = [ ],
p_macro = ".P\n",
at_first_tag_in_li = False,
at_first_tag_in_dd = False,
dt_from = None,
in_pre = False,
in_code = False,
html_out = [ HTML_START.replace('%TITLE%', fi.title) ],
man_out = [ ],
txt = '',
want_manpage = fi.want_manpage,
created_hashtags = set(),
derived_hashtags = set(),
referenced_hashtags = set(),
bad_hashtags = set(),
latest_targets = [ ],
opt_prefix = 'opt',
a_href = None,
a_href_external = False,
a_txt_start = None,
after_a_tag = False,
target_suf = '',
)
if st.want_manpage:
st.man_out.append(MAN_START % fi.man_headings)
if '</table>' in fi.html_in:
st.html_out[0] = st.html_out[0].replace('</style>', TABLE_STYLE + '</style>')
self.feed(fi.html_in)
fi.html_in = None
if st.want_manpage:
st.html_out.append(MAN_HTML_END % env_subs['date'])
st.html_out.append(HTML_END)
st.man_out.append(MAN_END)
fi.html_out = ''.join(st.html_out)
st.html_out = None
fi.man_out = ''.join(st.man_out)
st.man_out = None
for tgt, txt in st.derived_hashtags:
derived = txt2target(txt, tgt)
if derived not in st.created_hashtags:
txt = BIN_CHARS_RE.sub('', txt.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' '))
warn('Unknown derived hashtag link in', self.fn, 'based on:', (tgt, txt))
for bad in st.bad_hashtags:
if bad in st.created_hashtags:
warn('Missing "#" in hashtag link in', self.fn + ':', bad)
else:
warn('Unknown non-hashtag link in', self.fn + ':', bad)
for bad in st.referenced_hashtags - st.created_hashtags:
warn('Unknown hashtag link in', self.fn + ':', '#' + bad)
def handle_UE(self):
st = self.state
if st.txt.startswith(('.', ',', '!', '?', ';', ':')):
st.man_out[-1] = ".UE " + st.txt[0] + "\n"
st.txt = st.txt[1:]
st.after_a_tag = False
def handle_starttag(self, tag, attrs_list):
st = self.state
if args.debug:
self.output_debug('START', (tag, attrs_list))
if st.at_first_tag_in_li:
if st.list_state[-1] == 'dl':
st.dt_from = tag
if tag == 'p':
tag = 'dt'
else:
st.html_out.append('<dt>')
elif tag == 'p':
st.at_first_tag_in_dd = True # Kluge to suppress a .P at the start of an li.
st.at_first_tag_in_li = False
if tag == 'p':
if not st.at_first_tag_in_dd:
st.man_out.append(st.p_macro)
elif tag == 'li':
st.at_first_tag_in_li = True
lstate = st.list_state[-1]
if lstate == 'dl':
return
if lstate == 'o':
st.man_out.append(".IP o\n")
else:
st.man_out.append(".IP " + str(lstate) + ".\n")
st.list_state[-1] += 1
elif tag == 'blockquote':
st.man_out.append(".RS 4\n")
elif tag == 'pre':
st.in_pre = True
st.man_out.append(st.p_macro + ".nf\n")
elif tag == 'code' and not st.in_pre:
st.in_code = True
st.txt += BOLD_FONT[0]
elif tag == 'strong' or tag == 'b':
st.txt += BOLD_FONT[0]
elif tag == 'em' or tag == 'i':
if st.want_manpage:
tag = 'u' # Change it into underline to be more like the manpage
st.txt += UNDR_FONT[0]
elif tag == 'ol':
start = 1
for var, val in attrs_list:
if var == 'start':
start = int(val) # We only support integers.
break
if st.list_state:
st.man_out.append(".RS\n")
if start == 0:
tag = 'dl'
attrs_list = [ ]
st.list_state.append('dl')
else:
st.list_state.append(start)
st.man_out.append(st.p_macro)
st.p_macro = ".IP\n"
elif tag == 'ul':
st.man_out.append(st.p_macro)
if st.list_state:
st.man_out.append(".RS\n")
st.p_macro = ".IP\n"
st.list_state.append('o')
elif tag == 'hr':
st.man_out.append(".l\n")
st.html_out.append("<hr />")
return
elif tag == 'a':
st.a_href = None
for var, val in attrs_list:
if var == 'href':
if val.startswith(('https://', 'http://', 'mailto:', 'ftp:')):
if st.after_a_tag:
self.handle_UE()
st.man_out.append(manify(st.txt.strip()) + "\n")
st.man_out.append(".UR " + val + "\n")
st.txt = ''
st.a_href = val
st.a_href_external = True
elif '#' in val:
pg, tgt = val.split('#', 1)
if pg and pg not in VALID_PAGES or '#' in tgt:
st.bad_hashtags.add(val)
elif tgt in ('', 'opt', 'dopt'):
st.a_href = val
st.a_href_external = False
elif pg == '':
st.referenced_hashtags.add(tgt)
if tgt in st.latest_targets:
warn('Found link to the current section in', self.fn + ':', val)
elif val not in VALID_PAGES:
st.bad_hashtags.add(val)
st.a_txt_start = len(st.txt)
st.html_out.append('<' + tag + ''.join(' ' + var + '="' + htmlify(val) + '"' for var, val in attrs_list) + '>')
st.at_first_tag_in_dd = False
def handle_endtag(self, tag):
st = self.state
if args.debug:
self.output_debug('END', (tag,))
if st.after_a_tag:
self.handle_UE()
if tag in CONSUMES_TXT or st.dt_from == tag:
txt = st.txt.strip()
st.txt = ''
else:
txt = None
add_to_txt = None
if tag == 'h1':
tgt = txt
target_suf = ''
if tgt.startswith('NEWS for '):
m = VERSION_RE.search(tgt)
if m:
tgt = m.group(1)
st.target_suf = '-' + tgt
self.add_targets(tag, tgt)
elif tag == 'h2':
st.man_out.append(st.p_macro + '.SH "' + manify(txt) + '"\n')
self.add_targets(tag, txt, st.target_suf)
st.opt_prefix = 'dopt' if txt == 'DAEMON OPTIONS' else 'opt'
elif tag == 'h3':
st.man_out.append(st.p_macro + '.SS "' + manify(txt) + '"\n')
self.add_targets(tag, txt, st.target_suf)
elif tag == 'p':
if st.dt_from == 'p':
tag = 'dt'
st.man_out.append('.IP "' + manify(txt) + '"\n')
if txt.startswith(BOLD_FONT[0]):
self.add_targets(tag, txt)
st.dt_from = None
elif txt != '':
st.man_out.append(manify(txt) + "\n")
elif tag == 'li':
if st.list_state[-1] == 'dl':
if st.at_first_tag_in_li:
die("Invalid 0. -> td translation")
tag = 'dd'
if txt != '':
st.man_out.append(manify(txt) + "\n")
st.at_first_tag_in_li = False
elif tag == 'blockquote':
st.man_out.append(".RE\n")
elif tag == 'pre':
st.in_pre = False
st.man_out.append(manify(txt) + "\n.fi\n")
elif (tag == 'code' and not st.in_pre):
st.in_code = False
add_to_txt = NORM_FONT[0]
elif tag == 'strong' or tag == 'b':
add_to_txt = NORM_FONT[0]
elif tag == 'em' or tag == 'i':
if st.want_manpage:
tag = 'u' # Change it into underline to be more like the manpage
add_to_txt = NORM_FONT[0]
elif tag == 'ol' or tag == 'ul':
if st.list_state.pop() == 'dl':
tag = 'dl'
if st.list_state:
st.man_out.append(".RE\n")
else:
st.p_macro = ".P\n"
st.at_first_tag_in_dd = False
elif tag == 'hr':
return
elif tag == 'a':
if st.a_href_external:
st.txt = st.txt.strip()
if args.force_link_text or st.a_href != st.txt:
st.man_out.append(manify(st.txt) + "\n")
st.man_out.append(".UE\n") # This might get replaced with a punctuation version in handle_UE()
st.after_a_tag = True
st.a_href_external = False
st.txt = ''
elif st.a_href:
atxt = st.txt[st.a_txt_start:]
find = 'href="' + st.a_href + '"'
for j in range(len(st.html_out)-1, 0, -1):
if find in st.html_out[j]:
pg, tgt = st.a_href.split('#', 1)
derived = txt2target(atxt, tgt)
if pg == '':
if derived in st.latest_targets:
warn('Found link to the current section in', self.fn + ':', st.a_href)
st.derived_hashtags.add((tgt, atxt))
st.html_out[j] = st.html_out[j].replace(find, 'href="' + pg + '#' + derived + '"')
break
else:
die('INTERNAL ERROR: failed to find href in html data:', find)
st.html_out.append('</' + tag + '>')
if add_to_txt:
if txt is None:
st.txt += add_to_txt
else:
txt += add_to_txt
if st.dt_from == tag:
st.man_out.append('.IP "' + manify(txt) + '"\n')
st.html_out.append('</dt><dd>')
st.at_first_tag_in_dd = True
st.dt_from = None
elif tag == 'dt':
st.html_out.append('<dd>')
st.at_first_tag_in_dd = True
def handle_data(self, txt):
st = self.state
if '](' in txt:
warn('Malformed link in', self.fn + ':', txt)
if args.debug:
self.output_debug('DATA', (txt,))
if st.in_pre:
html = htmlify(txt)
else:
txt = LONG_OPT_DASH_RE.sub(lambda x: x.group(1).replace('-', NBR_DASH[0]), txt)
txt = SPACE_DOUBLE_DASH_RE.sub(NBR_SPACE[0] + r'--\1', txt).replace('--', NBR_DASH[0]*2)
txt = NON_SPACE_SINGLE_DASH_RE.sub(r'\1' + NBR_DASH[0], txt)
html = htmlify(txt)
if st.in_code:
txt = WHITESPACE_RE.sub(NBR_SPACE[0], txt)
html = html.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' ') # <code> is non-breaking in CSS
st.html_out.append(html.replace(NBR_SPACE[0], '&nbsp;').replace(NBR_DASH[0], '-&#8288;'))
st.txt += txt
def add_targets(self, tag, txt, suf=None):
st = self.state
tag = '<' + tag + '>'
targets = CODE_BLOCK_RE.findall(txt)
if not targets:
targets = [ txt ]
tag_pos = 0
for txt in targets:
txt = txt2target(txt, st.opt_prefix)
if not txt:
continue
if suf:
txt += suf
if txt in st.created_hashtags:
for j in range(2, 1000):
chk = txt + '-' + str(j)
if chk not in st.created_hashtags:
print('Made link target unique:', chk)
txt = chk
break
if tag_pos == 0:
tag_pos -= 1
while st.html_out[tag_pos] != tag:
tag_pos -= 1
st.html_out[tag_pos] = tag[:-1] + ' id="' + txt + '">'
st.html_out.append('<a href="#' + txt + '" class="tgt"></a>')
tag_pos -= 1 # take into account the append
else:
st.html_out[tag_pos] = '<span id="' + txt + '"></span>' + st.html_out[tag_pos]
st.created_hashtags.add(txt)
st.latest_targets = targets
def output_debug(self, event, extra):
import pprint
st = self.state
if args.debug < 2:
st = argparse.Namespace(**vars(st))
if len(st.html_out) > 2:
st.html_out = ['...'] + st.html_out[-2:]
if len(st.man_out) > 2:
st.man_out = ['...'] + st.man_out[-2:]
print(event, extra)
pprint.PrettyPrinter(indent=2).pprint(vars(st))
def txt2target(txt, opt_prefix):
txt = txt.strip().rstrip(':')
m = CODE_BLOCK_RE.search(txt)
if m:
txt = m.group(1)
txt = NBR_DASH_RE.sub('-', txt)
txt = BIN_CHARS_RE.sub('', txt)
txt = INVALID_TARGET_CHARS_RE.sub('_', txt)
if opt_prefix and txt.startswith('-'):
txt = opt_prefix + txt
else:
txt = INVALID_START_CHAR_RE.sub(r't\1', txt)
return txt
def manify(txt):
return MANIFY_LINESTART_RE.sub(r'\&\1', txt.replace('\\', '\\\\')
.replace(NBR_SPACE[0], NBR_SPACE[1])
.replace(NBR_DASH[0], NBR_DASH[1])
.replace(NORM_FONT[0], NORM_FONT[1])
.replace(BOLD_FONT[0], BOLD_FONT[1])
.replace(UNDR_FONT[0], UNDR_FONT[1]))
def htmlify(txt):
return txt.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
def warn(*msg):
print(*msg, file=sys.stderr)
global warning_count
warning_count += 1
def die(*msg):
warn(*msg)
sys.exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Convert markdown into html and (optionally) nroff. Each input filename must have a .md suffix, which is changed to .html for the output filename. If the input filename ends with .num.md (e.g. foo.1.md) then a nroff file is also output with the input filename's .md suffix removed (e.g. foo.1).", add_help=False)
parser.add_argument('--test', action='store_true', help="Just test the parsing without outputting any files.")
parser.add_argument('--dest', metavar='DIR', help="Create files in DIR instead of the current directory.")
parser.add_argument('--force-link-text', action='store_true', help="Don't remove the link text if it matches the link href. Useful when nroff doesn't understand .UR and .UE.")
parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
parser.add_argument("mdfiles", metavar='FILE.md', nargs='+', help="One or more .md files to convert.")
args = parser.parse_args()
try:
import cmarkgfm
md_parser = cmarkgfm.markdown_to_html
gfm_parser = cmarkgfm.github_flavored_markdown_to_html
except:
try:
import commonmark
md_parser = html_via_commonmark
except:
die("Failed to find cmarkgfm or commonmark for python3.")
gfm_parser = None
main()
if warning_count:
sys.exit(1)

387
md2man
View File

@@ -1,387 +0,0 @@
#!/usr/bin/env python3
# This script takes a manpage written in markdown and turns it into an html web
# page and a nroff man page. The input file must have the name of the program
# and the section in this format: NAME.NUM.md. The output files are written
# into the current directory named NAME.NUM.html and NAME.NUM. The input
# format has one extra extension: if a numbered list starts at 0, it is turned
# into a description list. The dl's dt tag is taken from the contents of the
# first tag inside the li, which is usually a p, code, or strong tag. The
# cmarkgfm or commonmark lib is used to transforms the input file into html.
# The html.parser is used as a state machine that both tweaks the html and
# outputs the nroff data based on the html tags.
#
# Copyright (C) 2020 Wayne Davison
#
# This program is freely redistributable.
import sys, os, re, argparse, subprocess, time
from html.parser import HTMLParser
CONSUMES_TXT = set('h1 h2 p li pre'.split())
HTML_START = """\
<html><head>
<title>%s</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
<style>
body {
max-width: 50em;
margin: auto;
}
body, b, strong, u {
font-family: 'Roboto', sans-serif;
}
code {
font-family: 'Roboto Mono', monospace;
font-weight: bold;
white-space: pre;
}
pre code {
display: block;
font-weight: normal;
}
blockquote pre code {
background: #f1f1f1;
}
dd p:first-of-type {
margin-block-start: 0em;
}
</style>
</head><body>
"""
HTML_END = """\
<div style="float: right"><p><i>%s</i></p></div>
</body></html>
"""
MAN_START = r"""
.TH "%s" "%s" "%s" "%s" "User Commands"
""".lstrip()
MAN_END = """\
"""
NORM_FONT = ('\1', r"\fP")
BOLD_FONT = ('\2', r"\fB")
UNDR_FONT = ('\3', r"\fI")
NBR_DASH = ('\4', r"\-")
NBR_SPACE = ('\xa0', r"\ ")
md_parser = None
def main():
fi = re.match(r'^(?P<fn>(?P<srcdir>.+/)?(?P<name>(?P<prog>[^/]+)\.(?P<sect>\d+))\.md)$', args.mdfile)
if not fi:
die('Failed to parse NAME.NUM.md out of input file:', args.mdfile)
fi = argparse.Namespace(**fi.groupdict())
if not fi.srcdir:
fi.srcdir = './'
fi.title = fi.prog + '(' + fi.sect + ') man page'
fi.mtime = 0
git_dir = fi.srcdir + '.git'
if os.path.lexists(git_dir):
fi.mtime = int(subprocess.check_output(['git', '--git-dir', git_dir, 'log', '-1', '--format=%at']))
env_subs = { 'prefix': os.environ.get('RSYNC_OVERRIDE_PREFIX', None) }
if args.test:
env_subs['VERSION'] = '1.0.0'
env_subs['libdir'] = '/usr'
else:
for fn in (fi.srcdir + 'version.h', 'Makefile'):
try:
st = os.lstat(fn)
except:
die('Failed to find', fi.srcdir + fn)
if not fi.mtime:
fi.mtime = st.st_mtime
with open(fi.srcdir + 'version.h', 'r', encoding='utf-8') as fh:
txt = fh.read()
m = re.search(r'"(.+?)"', txt)
env_subs['VERSION'] = m.group(1)
with open('Makefile', 'r', encoding='utf-8') as fh:
for line in fh:
m = re.match(r'^(\w+)=(.+)', line)
if not m:
continue
var, val = (m.group(1), m.group(2))
if var == 'prefix' and env_subs[var] is not None:
continue
while re.search(r'\$\{', val):
val = re.sub(r'\$\{(\w+)\}', lambda m: env_subs[m.group(1)], val)
env_subs[var] = val
if var == 'srcdir':
break
with open(fi.fn, 'r', encoding='utf-8') as fh:
txt = fh.read()
txt = re.sub(r'@VERSION@', env_subs['VERSION'], txt)
txt = re.sub(r'@LIBDIR@', env_subs['libdir'], txt)
fi.html_in = md_parser(txt)
txt = None
fi.date = time.strftime('%d %b %Y', time.localtime(fi.mtime))
fi.man_headings = (fi.prog, fi.sect, fi.date, fi.prog + ' ' + env_subs['VERSION'])
HtmlToManPage(fi)
if args.test:
print("The test was successful.")
return
for fn, txt in ((fi.name + '.html', fi.html_out), (fi.name, fi.man_out)):
print("Wrote:", fn)
with open(fn, 'w', encoding='utf-8') as fh:
fh.write(txt)
def html_via_cmarkgfm(txt):
return cmarkgfm.markdown_to_html(txt)
def html_via_commonmark(txt):
return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
class HtmlToManPage(HTMLParser):
def __init__(self, fi):
HTMLParser.__init__(self, convert_charrefs=True)
st = self.state = argparse.Namespace(
list_state = [ ],
p_macro = ".P\n",
at_first_tag_in_li = False,
at_first_tag_in_dd = False,
dt_from = None,
in_pre = False,
in_code = False,
html_out = [ HTML_START % fi.title ],
man_out = [ MAN_START % fi.man_headings ],
txt = '',
)
self.feed(fi.html_in)
fi.html_in = None
st.html_out.append(HTML_END % fi.date)
st.man_out.append(MAN_END)
fi.html_out = ''.join(st.html_out)
st.html_out = None
fi.man_out = ''.join(st.man_out)
st.man_out = None
def handle_starttag(self, tag, attrs_list):
st = self.state
if args.debug:
self.output_debug('START', (tag, attrs_list))
if st.at_first_tag_in_li:
if st.list_state[-1] == 'dl':
st.dt_from = tag
if tag == 'p':
tag = 'dt'
else:
st.html_out.append('<dt>')
elif tag == 'p':
st.at_first_tag_in_dd = True # Kluge to suppress a .P at the start of an li.
st.at_first_tag_in_li = False
if tag == 'p':
if not st.at_first_tag_in_dd:
st.man_out.append(st.p_macro)
elif tag == 'li':
st.at_first_tag_in_li = True
lstate = st.list_state[-1]
if lstate == 'dl':
return
if lstate == 'o':
st.man_out.append(".IP o\n")
else:
st.man_out.append(".IP " + str(lstate) + ".\n")
st.list_state[-1] += 1
elif tag == 'blockquote':
st.man_out.append(".RS 4\n")
elif tag == 'pre':
st.in_pre = True
st.man_out.append(st.p_macro + ".nf\n")
elif tag == 'code' and not st.in_pre:
st.in_code = True
st.txt += BOLD_FONT[0]
elif tag == 'strong' or tag == 'b':
st.txt += BOLD_FONT[0]
elif tag == 'em' or tag == 'i':
tag = 'u' # Change it into underline to be more like the man page
st.txt += UNDR_FONT[0]
elif tag == 'ol':
start = 1
for var, val in attrs_list:
if var == 'start':
start = int(val) # We only support integers.
break
if st.list_state:
st.man_out.append(".RS\n")
if start == 0:
tag = 'dl'
attrs_list = [ ]
st.list_state.append('dl')
else:
st.list_state.append(start)
st.man_out.append(st.p_macro)
st.p_macro = ".IP\n"
elif tag == 'ul':
st.man_out.append(st.p_macro)
if st.list_state:
st.man_out.append(".RS\n")
st.p_macro = ".IP\n"
st.list_state.append('o')
st.html_out.append('<' + tag + ''.join(' ' + var + '="' + htmlify(val) + '"' for var, val in attrs_list) + '>')
st.at_first_tag_in_dd = False
def handle_endtag(self, tag):
st = self.state
if args.debug:
self.output_debug('END', (tag,))
if tag in CONSUMES_TXT or st.dt_from == tag:
txt = st.txt.strip()
st.txt = ''
else:
txt = None
add_to_txt = None
if tag == 'h1':
st.man_out.append(st.p_macro + '.SH "' + manify(txt) + '"\n')
elif tag == 'h2':
st.man_out.append(st.p_macro + '.SS "' + manify(txt) + '"\n')
elif tag == 'p':
if st.dt_from == 'p':
tag = 'dt'
st.man_out.append('.IP "' + manify(txt) + '"\n')
st.dt_from = None
elif txt != '':
st.man_out.append(manify(txt) + "\n")
elif tag == 'li':
if st.list_state[-1] == 'dl':
if st.at_first_tag_in_li:
die("Invalid 0. -> td translation")
tag = 'dd'
if txt != '':
st.man_out.append(manify(txt) + "\n")
st.at_first_tag_in_li = False
elif tag == 'blockquote':
st.man_out.append(".RE\n")
elif tag == 'pre':
st.in_pre = False
st.man_out.append(manify(txt) + "\n.fi\n")
elif (tag == 'code' and not st.in_pre):
st.in_code = False
add_to_txt = NORM_FONT[0]
elif tag == 'strong' or tag == 'b':
add_to_txt = NORM_FONT[0]
elif tag == 'em' or tag == 'i':
tag = 'u' # Change it into underline to be more like the man page
add_to_txt = NORM_FONT[0]
elif tag == 'ol' or tag == 'ul':
if st.list_state.pop() == 'dl':
tag = 'dl'
if st.list_state:
st.man_out.append(".RE\n")
else:
st.p_macro = ".P\n"
st.at_first_tag_in_dd = False
st.html_out.append('</' + tag + '>')
if add_to_txt:
if txt is None:
st.txt += add_to_txt
else:
txt += add_to_txt
if st.dt_from == tag:
st.man_out.append('.IP "' + manify(txt) + '"\n')
st.html_out.append('</dt><dd>')
st.at_first_tag_in_dd = True
st.dt_from = None
elif tag == 'dt':
st.html_out.append('<dd>')
st.at_first_tag_in_dd = True
def handle_data(self, txt):
st = self.state
if args.debug:
self.output_debug('DATA', (txt,))
if st.in_pre:
html = htmlify(txt)
else:
txt = re.sub(r'\s--(\s)', NBR_SPACE[0] + r'--\1', txt).replace('--', NBR_DASH[0]*2)
txt = re.sub(r'(^|\W)-', r'\1' + NBR_DASH[0], txt)
html = htmlify(txt)
if st.in_code:
txt = re.sub(r'\s', NBR_SPACE[0], txt)
html = html.replace(NBR_DASH[0], '-').replace(NBR_SPACE[0], ' ') # <code> is non-breaking in CSS
st.html_out.append(html.replace(NBR_SPACE[0], '&nbsp;').replace(NBR_DASH[0], '-&#8288;'))
st.txt += txt
def output_debug(self, event, extra):
import pprint
st = self.state
if args.debug < 2:
st = argparse.Namespace(**vars(st))
if len(st.html_out) > 2:
st.html_out = ['...'] + st.html_out[-2:]
if len(st.man_out) > 2:
st.man_out = ['...'] + st.man_out[-2:]
print(event, extra)
pprint.PrettyPrinter(indent=2).pprint(vars(st))
def manify(txt):
return re.sub(r"^(['.])", r'\&\1', txt.replace('\\', '\\\\')
.replace(NBR_SPACE[0], NBR_SPACE[1])
.replace(NBR_DASH[0], NBR_DASH[1])
.replace(NORM_FONT[0], NORM_FONT[1])
.replace(BOLD_FONT[0], BOLD_FONT[1])
.replace(UNDR_FONT[0], UNDR_FONT[1]), flags=re.M)
def htmlify(txt):
return txt.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
def warn(*msg):
print(*msg, file=sys.stderr)
def die(*msg):
warn(*msg)
sys.exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Transform a NAME.NUM.md markdown file into a NAME.NUM.html web page & a NAME.NUM man page.', add_help=False)
parser.add_argument('--test', action='store_true', help='Test if we can parse the input w/o updating any files.')
parser.add_argument('--debug', '-D', action='count', default=0, help='Output copious info on the html parsing. Repeat for even more.')
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
parser.add_argument('mdfile', help="The NAME.NUM.md file to parse.")
args = parser.parse_args()
try:
import cmarkgfm
md_parser = html_via_cmarkgfm
except:
try:
import commonmark
md_parser = html_via_commonmark
except:
die("Failed to find cmarkgfm or commonmark for python3.")
main()

1
md2man Symbolic link
View File

@@ -0,0 +1 @@
md-convert

22
mkgitver Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
srcdir=`dirname $0`
if [ ! -f git-version.h ]; then
touch git-version.h
fi
if test -d "$srcdir/.git" || test -f "$srcdir/.git"; then
gitver=`git describe --abbrev=8 2>/dev/null`
# NOTE: I'm avoiding "|" in sed since I'm not sure if sed -r is portable and "\|" fails on some OSes.
verchk=`echo "$gitver-" | sed -n '/^v3\.[0-9][0-9]*\.[0-9][0-9]*\(pre[0-9]*\)*-/p'`
if [ -n "$verchk" ]; then
echo "#define RSYNC_GITVER \"$gitver\"" >git-version.h.new
if ! diff git-version.h.new git-version.h >/dev/null; then
echo "Updating git-version.h"
mv git-version.h.new git-version.h
else
rm git-version.h.new
fi
fi
fi

View File

@@ -2,6 +2,7 @@
BEGIN {
while ((getline i < "proto.h") > 0) old_protos = old_protos ? old_protos "\n" i : i
close("proto.h")
protos = "/* This file is automatically generated with \"make proto\". DO NOT EDIT */\n"
}
@@ -35,5 +36,5 @@ inheader {
END {
if (old_protos != protos) print protos > "proto.h"
printf "" > "proto.h-tstamp"
system("touch proto.h-tstamp")
}

87
old_versions/README.md Normal file
View File

@@ -0,0 +1,87 @@
# Old rsync version archive
Static rsync binaries built from historical release tags. Two uses:
1. **Cross-version behaviour checks** — confirming whether a behaviour a user
reported on an old release is version-specific or option-driven.
2. **The version-mixing test suite**`runtests.py --rsync-bin2=...` runs the
current code against one of these as the daemon / remote-shell peer; CI
(`.github/workflows/ubuntu-version-mix.yml`) does this for every binary
here against the per-version manifests in `testsuite/expect/`.
Binaries are **statically linked** so they run regardless of the host's
shared libraries, and named `rsync_<version>`:
| Binary | Version | Protocol | Notes |
|----------------|---------|----------|-----------------------------------------|
| `rsync_2.6.0` | 2.6.0 | 27 | 2004; needs autoconf regen (see below) |
| `rsync_3.0.0` | 3.0.0 | 30 | 2008 |
| `rsync_3.1.0` | 3.1.0 | 31 | 2013 |
| `rsync_3.1.3` | 3.1.3 | 31 | Ubuntu 18.04 / Debian buster era (2018) |
| `rsync_3.2.0` | 3.2.0 | 31 | 2020 (zstd/lz4/xxhash negotiation added)|
| `rsync_3.2.7` | 3.2.7 | 31 | 2022 |
| `rsync_3.3.0` | 3.3.0 | 31 | 2024 |
| `rsync_3.4.0` | 3.4.0 | 32 | 2025 |
| `rsync_3.4.1` | 3.4.1 | 32 | 2025 |
These are every `x.y.0` release from 2.6.0 (2004) onward plus a few point
releases. 2.6.0 is the practical floor: older tags need progressively more
porting to build on a current toolchain.
All built `--disable-openssl` and with `_FORTIFY_SOURCE` disabled (see below);
xxhash/zstd/lz4 are compiled in where the version supports them.
## Adding a version
```bash
./build_static.sh 3.2.7 # uses git tag v3.2.7
./build_static.sh 3.0.9 v3.0.9 # explicit tag if naming differs
```
The script checks out the tag into a throwaway `git worktree`, applies the
minimal patches needed to compile old sources on a modern toolchain, links
statically, verifies the result is static and reports the requested version,
then installs `rsync_<version>` here and removes the worktree.
Override the source repo with `RSYNC_REPO=/path/to/rsync ./build_static.sh ...`
(defaults to `../rsync.4`).
## Why the patches?
Modern GCC (>= 14, C23 default) and glibc reject things old rsync relied on.
`build_static.sh` handles these, each guarded so it's a no-op when not needed:
1. **K&R `lseek64()` redeclaration** in `syscall.c` clashes with glibc's real
prototype — removed.
2. **`gettimeofday()`** — glibc only has the 2-arg form; configure misdetects
the 1-arg form, so `HAVE_GETTIMEOFDAY_TZ` is forced on in `config.h`.
3. **C23 `()` == `(void)`** breaks K&R prototypes called with arguments
(`qsort` comparator, `pool->bomb`, etc.) — built with `-std=gnu11`.
4. Assorted modern `-Werror` promotions (incompatible pointer types, implicit
declarations) downgraded to warnings; bundled zlib/popt used to keep the
static link self-contained.
5. **OpenSSL (3.2+)** is disabled with `--disable-openssl`: linking
`libcrypto.a` statically drags in jitterentropy (`jent_*`) and zlib's
`uncompress` (OpenSSL's COMP module), which don't resolve here. OpenSSL only
provided optional MD4/MD5, which rsync implements natively, so checksum
behaviour is unaffected.
6. **`_FORTIFY_SOURCE` disabled** (`-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0`):
modern Ubuntu defaults it to `=3`, whose stricter object-size checks turn
latent (historically benign) over-reads in OLD rsync into hard
`*** buffer overflow detected ***` aborts when the binary runs as a
server/daemon — which made e.g. 3.1.3 and 3.2.7 unusable as peers. Disabling
it makes the archival binaries behave as the released versions did.
7. **Pre-3.0 tags (e.g. 2.6.0)** ship `configure.in`, not a generated
`configure`. The script runs `autoheader`/`autoconf` to generate it, after
neutralizing the `AC_CHECK_FUNCS(fn,,AC_LIBOBJ(lib/...))` fallbacks for
`inet_ntop`/`inet_pton`/`getaddrinfo`/`getnameinfo` — modern autoconf emits
broken shell for those never-taken branches (the funcs exist in glibc). It
also generates `proto.h` (no make rule in that era) and stubs the vendored
`lib/addrinfo.h` the tag dropped (modern glibc supplies `struct addrinfo`).
All guarded so they no-op on 3.x.
Newer versions may need fewer or different tweaks; if a build fails, the
script prints the first compiler errors from its log.

128
old_versions/build_static.sh Executable file
View File

@@ -0,0 +1,128 @@
#!/bin/bash
# Build a static rsync binary from a historical git tag, for cross-version
# behaviour testing. Produces ./rsync_<version> in this directory.
#
# Usage: ./build_static.sh <version> [git-tag]
# Example: ./build_static.sh 3.1.3 # uses tag v3.1.3
# ./build_static.sh 3.2.7 v3.2.7
#
# Old rsync releases don't compile cleanly on a modern toolchain (GCC >= 14
# defaults to C23, where an empty () prototype means (void); glibc dropped the
# 1-arg gettimeofday; lseek64 K&R redeclarations clash). This script applies
# the minimal, best-effort workarounds and links statically so the result is
# self-contained and reproducible regardless of the host's shared libraries.
#
# Each workaround is guarded so it's a no-op on versions that don't need it.
set -euo pipefail
VERSION="${1:?usage: build_static.sh <version> [git-tag]}"
TAG="${2:-v$VERSION}"
ARCHIVE_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO="${RSYNC_REPO:-/home/tridge/project/rsync/rsync.4}" # any rsync worktree
WORKTREE="$(mktemp -d /tmp/rsync-build-XXXXXX)"
OUT="$ARCHIVE_DIR/rsync_$VERSION"
# C standard restores K&R () semantics; permissive flags downgrade the pile of
# modern -Werror promotions (incompatible pointers, implicit decls) to warnings.
# _FORTIFY_SOURCE is forced OFF: modern Ubuntu defaults it to =3, whose stricter
# object-size checks turn latent (historically benign) over-reads in OLD rsync
# into hard "*** buffer overflow detected ***" aborts when the binary acts as a
# server/daemon. Disabling it makes these archival binaries behave the way the
# released versions did, which is the whole point of the archive.
CFLAGS_OLD="-I. -I./zlib -O2 -g -std=gnu11 -fcommon -DHAVE_CONFIG_H -Wno-error \
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
-Wno-incompatible-pointer-types -Wno-implicit-function-declaration -Wno-int-conversion"
cleanup() {
cd "$REPO"
git worktree remove --force "$WORKTREE" 2>/dev/null || true
git worktree prune 2>/dev/null || true
}
trap cleanup EXIT
echo ">>> checking out $TAG into $WORKTREE"
# prefer an exact tag to avoid ambiguity with similarly-named branches
REF="$TAG"
if git -C "$REPO" rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
REF="refs/tags/$TAG"
fi
git -C "$REPO" worktree add --detach "$WORKTREE" "$REF"
cd "$WORKTREE"
# --- workaround 1: K&R lseek64 redeclaration clashes with glibc's prototype ---
if grep -q 'off64_t lseek64();' syscall.c 2>/dev/null; then
echo ">>> patching syscall.c lseek64 redeclaration"
perl -0pi -e 's/#ifdef HAVE_LSEEK64\n#if !SIZEOF_OFF64_T\n\tOFF_T lseek64\(\);\n#else\n\toff64_t lseek64\(\);\n#endif\n\treturn lseek64/#ifdef HAVE_LSEEK64\n\treturn lseek64/' syscall.c
fi
# --- workaround 0: pre-3.0 tags ship configure.in, not a generated configure.
# Generate it. Modern autoconf emits broken shell for their
# AC_CHECK_FUNCS(fn,,AC_LIBOBJ(lib/...)) fallbacks -- but those branches are
# dead on a modern host (glibc has inet_ntop/inet_pton/getaddrinfo/getnameinfo),
# so neutralize the AC_LIBOBJ replacements before regenerating.
OLD_TREE=0
if [ ! -f ./configure ] && { [ -f configure.in ] || [ -f configure.ac ]; }; then
OLD_TREE=1
acsrc=configure.ac; [ -f configure.in ] && acsrc=configure.in
echo ">>> generating configure for an old tag (autoheader/autoconf)"
sed -i 's#AC_LIBOBJ(lib/[a-zA-Z_]*)#:#g' "$acsrc"
autoheader 2>/dev/null || true
autoconf 2>/dev/null || { echo "autoconf failed"; exit 1; }
fi
CONF_ARGS=(--disable-md2man --with-included-zlib=yes --with-included-popt=yes)
# OpenSSL (3.2+) only adds optional MD4/MD5 that rsync already implements, but
# linking libcrypto.a statically drags in jitterentropy + zlib's uncompress,
# which aren't resolvable here. Drop it when the flag exists.
if ./configure --help 2>/dev/null | grep -q -- '--disable-openssl'; then
echo ">>> disabling openssl for self-contained static link"
CONF_ARGS+=(--disable-openssl)
fi
echo ">>> configure (bundled zlib + popt, static-friendly)"
./configure "${CONF_ARGS[@]}" \
>"$WORKTREE/conf.log" 2>&1 || { tail -20 "$WORKTREE/conf.log"; exit 1; }
# --- workaround 2: modern glibc only has the 2-arg gettimeofday ---------------
if grep -q '/\* #undef HAVE_GETTIMEOFDAY_TZ \*/' config.h; then
echo ">>> forcing HAVE_GETTIMEOFDAY_TZ (configure misdetects it)"
sed -i 's|/\* #undef HAVE_GETTIMEOFDAY_TZ \*/|#define HAVE_GETTIMEOFDAY_TZ 1|' config.h
fi
# --- workaround 4 (old trees only): generate proto.h if the tree has no make
# rule for it, and stub a vendored lib/addrinfo.h that the git tag dropped
# (modern glibc supplies struct addrinfo / sockaddr_storage, so empty is right).
if [ "$OLD_TREE" = 1 ]; then
if [ ! -f proto.h ] && [ -f mkproto.awk ]; then
echo ">>> generating proto.h"
cat ./*.c ./lib/compat.c 2>/dev/null | awk -f ./mkproto.awk > proto.h
fi
if grep -q 'include "lib/addrinfo.h"' rsync.h 2>/dev/null && [ ! -f lib/addrinfo.h ]; then
echo ">>> stubbing lib/addrinfo.h"
echo '/* emptied: modern glibc provides struct addrinfo */' > lib/addrinfo.h
fi
fi
echo ">>> building (static)"
make -j"$(nproc)" CFLAGS="$CFLAGS_OLD" LDFLAGS="-static" \
>"$WORKTREE/make.log" 2>&1 || { grep -E 'error:|\*\*\*' "$WORKTREE/make.log" | head; exit 1; }
# verify it's actually static before we keep it
if ldd ./rsync 2>&1 | grep -qv 'not a dynamic executable'; then
echo "ERROR: binary is not statically linked:" >&2
ldd ./rsync >&2
exit 1
fi
GOT="$(./rsync --version | head -1 | awk '{print $3}')"
if [ "$GOT" != "$VERSION" ]; then
echo "ERROR: built version '$GOT' != requested '$VERSION'" >&2
exit 1
fi
cp ./rsync "$OUT"
strip "$OUT"
echo ">>> installed $OUT"
"$OUT" --version | head -1
file "$OUT"

BIN
old_versions/rsync_2.6.0 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.0.0 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.1.0 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.1.3 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.2.0 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.2.7 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.3.0 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.4.0 Executable file
View File

Binary file not shown.

BIN
old_versions/rsync_3.4.1 Executable file
View File

Binary file not shown.

1074
options.c
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,12 @@
TARGETS := all install install-ssl-daemon install-all install-strip conf gen gensend reconfigure restatus \
proto man clean cleantests distclean test check check29 check30 installcheck splint doxygen doxygen-upload
TARGETS := all install install-ssl-daemon install-all install-strip uninstall uninstall-ssl-daemon uninstall-all conf gen reconfigure restatus \
proto man clean cleantests distclean test check check29 check30 installcheck splint \
doxygen doxygen-upload finddead rrsync
.PHONY: $(TARGETS)
.PHONY: $(TARGETS) auto-prep
$(TARGETS):
@if test x`packaging/prep-auto-dir` = x; then echo "auto-build-save is not setup"; exit 1; fi
$(TARGETS): auto-prep
make -C build $@
auto-prep:
@if test x`packaging/prep-auto-dir` = x; then echo "auto-build-save is not setup"; exit 1; fi
@echo 'Build branch: '`readlink build/.branch | tr % /`

View File

@@ -1,174 +0,0 @@
#!/usr/bin/env -S python3 -B
# This script turns one or more diff files in the patches dir (which is
# expected to be a checkout of the rsync-patches git repo) into a branch
# in the main rsync git checkout. This allows the applied patch to be
# merged with the latest rsync changes and tested. To update the diff
# with the resulting changes, see the patch-update script.
import os, sys, re, argparse, glob
sys.path = ['packaging'] + sys.path
from pkglib import *
def main():
global created, info, local_branch
cur_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
local_branch = get_patch_branches(args.base_branch)
if args.delete_local_branches:
for name in sorted(local_branch):
branch = f"patch/{args.base_branch}/{name}"
cmd_chk(['git', 'branch', '-D', branch])
local_branch = set()
if args.add_missing:
for fn in sorted(glob.glob(f"{args.patches_dir}/*.diff")):
name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
if name not in local_branch and fn not in args.patch_files:
args.patch_files.append(fn)
if not args.patch_files:
return
for fn in args.patch_files:
if not fn.endswith('.diff'):
die(f"Filename is not a .diff file: {fn}")
if not os.path.isfile(fn):
die(f"File not found: {fn}")
scanned = set()
info = { }
patch_list = [ ]
for fn in args.patch_files:
m = re.match(r'^(?P<dir>.*?)(?P<name>[^/]+)\.diff$', fn)
patch = argparse.Namespace(**m.groupdict())
if patch.name in scanned:
continue
patch.fn = fn
lines = [ ]
commit_hash = None
with open(patch.fn, 'r', encoding='utf-8') as fh:
for line in fh:
m = re.match(r'^based-on: (\S+)', line)
if m:
commit_hash = m[1]
break
if (re.match(r'^index .*\.\..* \d', line)
or re.match(r'^diff --git ', line)
or re.match(r'^--- (old|a)/', line)):
break
lines.append(re.sub(r'\s*\Z', "\n", line, 1))
info_txt = ''.join(lines).strip() + "\n"
lines = None
parent = args.base_branch
patches = re.findall(r'patch -p1 <%s/(\S+)\.diff' % args.patches_dir, info_txt)
if patches:
last = patches.pop()
if last != patch.name:
warn(f"No identity patch line in {patch.fn}")
patches.append(last)
if patches:
parent = patches.pop()
if parent not in scanned:
diff_fn = patch.dir + parent + '.diff'
if not os.path.isfile(diff_fn):
die(f"Failed to find parent of {patch.fn}: {parent}")
# Add parent to args.patch_files so that we will look for the
# parent's parent. Any duplicates will be ignored.
args.patch_files.append(diff_fn)
else:
warn(f"No patch lines found in {patch.fn}")
info[patch.name] = [ parent, info_txt, commit_hash ]
patch_list.append(patch)
created = set()
for patch in patch_list:
create_branch(patch)
cmd_chk(['git', 'checkout', args.base_branch])
def create_branch(patch):
if patch.name in created:
return
created.add(patch.name)
parent, info_txt, commit_hash = info[patch.name]
parent = argparse.Namespace(dir=patch.dir, name=parent, fn=patch.dir + parent + '.diff')
if parent.name == args.base_branch:
parent_branch = commit_hash if commit_hash else args.base_branch
else:
create_branch(parent)
parent_branch = '/'.join(['patch', args.base_branch, parent.name])
branch = '/'.join(['patch', args.base_branch, patch.name])
print("\n" + '=' * 64)
print(f"Processing {branch} ({parent_branch})")
if patch.name in local_branch:
cmd_chk(['git', 'branch', '-D', branch])
cmd_chk(['git', 'checkout', '-b', branch, parent_branch])
info_fn = 'PATCH.' + patch.name
with open(info_fn, 'w', encoding='utf-8') as fh:
fh.write(info_txt)
cmd_chk(['git', 'add', info_fn])
with open(patch.fn, 'r', encoding='utf-8') as fh:
patch_txt = fh.read()
cmd_run('patch -p1'.split(), input=patch_txt)
for fn in glob.glob('*.orig') + glob.glob('*/*.orig'):
os.unlink(fn)
pos = 0
new_file_re = re.compile(r'\nnew file mode (?P<mode>\d+)\s+--- /dev/null\s+\+\+\+ b/(?P<fn>.+)')
while True:
m = new_file_re.search(patch_txt, pos)
if not m:
break
os.chmod(m['fn'], int(m['mode'], 8))
cmd_chk(['git', 'add', m['fn']])
pos = m.end()
while True:
cmd_chk('git status'.split())
ans = input('Press Enter to commit, Ctrl-C to abort, or type a wild-name to add a new file: ')
if ans == '':
break
cmd_chk("git add " + ans, shell=True)
while True:
s = cmd_run(['git', 'commit', '-a', '-m', f"Creating branch from {patch.name}.diff."])
if not s.returncode:
break
s = cmd_run(['/bin/zsh'])
if s.returncode:
die('Aborting due to shell error code')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Create a git patch branch from an rsync patch file.", add_help=False)
parser.add_argument('--branch', '-b', dest='base_branch', metavar='BASE_BRANCH', default='master', help="The branch the patch is based on. Default: master.")
parser.add_argument('--add-missing', '-a', action='store_true', help="Add a branch for every patches/*.diff that doesn't have a branch.")
parser.add_argument('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
parser.add_argument('--delete', dest='delete_local_branches', action='store_true', help="Delete all the local patch/BASE/* branches, not just the ones that are being recreated.")
parser.add_argument('--patches-dir', '-p', metavar='DIR', default='patches', help="Override the location of the rsync-patches dir. Default: patches.")
parser.add_argument('patch_files', metavar='patches/DIFF_FILE', nargs='*', help="Specify what patch diff files to process. Default: all of them.")
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
args = parser.parse_args()
main()
# vim: sw=4 et ft=python

148
packaging/cull-options Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
# This script outputs either perl or python code that parses all possible options
# that the code in options.c might send to the server. The resulting code is then
# included in the rrsync script.
import re, argparse
short_no_arg = { }
short_with_num = { '@': 1 }
long_opts = { # These include some extra long-args that BackupPC uses:
'block-size': 1,
'daemon': -1,
'debug': 1,
'fake-super': 0,
'fuzzy': 0,
'group': 0,
'hard-links': 0,
'ignore-times': 0,
'info': 1,
'links': 0,
'log-file': 3,
'munge-links': 0,
'no-munge-links': -1,
'one-file-system': 0,
'owner': 0,
'perms': 0,
'recursive': 0,
'stderr': 1,
'times': 0,
'copy-devices': -1,
'write-devices': -1,
}
def main():
last_long_opt = None
with open('../options.c') as fh:
for line in fh:
m = re.search(r"argstr\[x\+\+\] = '([^.ie])'", line)
if m:
short_no_arg[m.group(1)] = 1
last_long_opt = None
continue
m = re.search(r'asprintf\([^,]+, "-([a-zA-Z0-9])\%l?[ud]"', line)
if m:
short_with_num[m.group(1)] = 1
last_long_opt = None
continue
m = re.search(r'args\[ac\+\+\] = "--([^"=]+)"', line)
if m:
last_long_opt = m.group(1)
if last_long_opt not in long_opts:
long_opts[last_long_opt] = 0
else:
last_long_opt = None
continue
if last_long_opt:
m = re.search(r'args\[ac\+\+\] = safe_arg\("", ([^[("\s]+)\);', line)
if m:
long_opts[last_long_opt] = 2
last_long_opt = None
continue
if 'args[ac++] = ' in line:
last_long_opt = None
m = re.search(r'return "--([^"]+-dest)";', line)
if m:
long_opts[m.group(1)] = 2
last_long_opt = None
continue
m = re.search(r'asprintf\([^,]+, "--([^"=]+)=', line)
if not m:
m = re.search(r'args\[ac\+\+\] = "--([^"=]+)=', line)
if not m:
m = re.search(r'args\[ac\+\+\] = safe_arg\("--([^"=]+)"', line)
if not m:
m = re.search(r'fmt = .*: "--([^"=]+)=', line)
if m:
long_opts[m.group(1)] = 1
last_long_opt = None
long_opts['files-from'] = 3
txt = """\
### START of options data produced by the cull-options script. ###
# To disable a short-named option, add its letter to this string:
"""
txt += str_assign('short_disabled', 's') + "\n"
txt += '# These are also disabled when the restricted dir is not "/":\n'
txt += str_assign('short_disabled_subdir', 'KLk') + "\n"
txt += '# These are all possible short options that we will accept (when not disabled above):\n'
txt += str_assign('short_no_arg', ''.join(sorted(short_no_arg)), 'DO NOT REMOVE ANY')
txt += str_assign('short_with_num', ''.join(sorted(short_with_num)), 'DO NOT REMOVE ANY')
txt += """
# To disable a long-named option, change its value to a -1. The values mean:
# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
# check the arg when receiving; and 3 = always check the arg.
"""
print(txt, end='')
if args.python:
print("long_opts = {")
sep = ':'
else:
print("our %long_opt = (")
sep = ' =>'
for opt in sorted(long_opts):
if opt.startswith(('min-', 'max-')):
val = 1
else:
val = long_opts[opt]
print(' ', repr(opt) + sep, str(val) + ',')
if args.python:
print("}")
else:
print(");")
print("\n### END of options data produced by the cull-options script. ###")
def str_assign(name, val, comment=None):
comment = ' # ' + comment if comment else ''
if args.python:
return name + ' = ' + repr(val) + comment + "\n"
return 'our $' + name + ' = ' + repr(val) + ';' + comment + "\n"
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Output culled rsync options for rrsync.", add_help=False)
out_group = parser.add_mutually_exclusive_group()
out_group.add_argument('--perl', action='store_true', help="Output perl code.")
out_group.add_argument('--python', action='store_true', help="Output python code (the default).")
parser.add_argument('--help', '-h', action='help', help="Output this help message and exit.")
args = parser.parse_args()
if not args.perl:
args.python = True
main()
# vim: sw=4 et

View File

@@ -1,85 +0,0 @@
#!/usr/bin/env perl
# This script outputs some perl code that parses all possible options
# that the code in options.c might send to the server. This perl code
# is included in the rrsync script.
use strict;
our %short_no_arg;
our %short_with_num = ( '@' => 1 );
our %long_opt = ( # These include some extra long-args that BackupPC uses:
'block-size' => 1,
'daemon' => -1,
'debug' => 1,
'fake-super' => 0,
'fuzzy' => 0,
'group' => 0,
'hard-links' => 0,
'ignore-times' => 0,
'info' => 1,
'links' => 0,
'log-file' => 3,
'one-file-system' => 0,
'owner' => 0,
'perms' => 0,
'recursive' => 0,
'times' => 0,
'write-devices' => -1,
);
our $last_long_opt;
open(IN, '../options.c') or die "Unable to open ../options.c: $!\n";
while (<IN>) {
if (/\Qargstr[x++]\E = '([^.ie])'/) {
$short_no_arg{$1} = 1;
undef $last_long_opt;
} elsif (/\Qasprintf(\E[^,]+, "-([a-zA-Z0-9])\%l?[ud]"/) {
$short_with_num{$1} = 1;
undef $last_long_opt;
} elsif (/\Qargs[ac++]\E = "--([^"=]+)"/) {
$last_long_opt = $1;
$long_opt{$1} = 0 unless exists $long_opt{$1};
} elsif (defined($last_long_opt)
&& /\Qargs[ac++]\E = ([^["\s]+);/) {
$long_opt{$last_long_opt} = 2;
undef $last_long_opt;
} elsif (/return "--([^"]+-dest)";/) {
$long_opt{$1} = 2;
undef $last_long_opt;
} elsif (/\Qasprintf(\E[^,]+, "--([^"=]+)=/ || /\Qargs[ac++]\E = "--([^"=]+)=/ || /fmt = .*: "--([^"=]+)=/) {
$long_opt{$1} = 1;
undef $last_long_opt;
}
}
close IN;
my $short_no_arg = join('', sort keys %short_no_arg);
my $short_with_num = join('', sort keys %short_with_num);
print <<EOT;
# These options are the only options that rsync might send to the server,
# and only in the option format that the stock rsync produces.
# To disable a short-named option, add its letter to this string:
our \$short_disabled = 's';
our \$short_no_arg = '$short_no_arg'; # DO NOT REMOVE ANY
our \$short_with_num = '$short_with_num'; # DO NOT REMOVE ANY
# To disable a long-named option, change its value to a -1. The values mean:
# 0 = the option has no arg; 1 = the arg doesn't need any checking; 2 = only
# check the arg when receiving; and 3 = always check the arg.
our \%long_opt = (
EOT
foreach my $opt (sort keys %long_opt) {
my $val = $long_opt{$opt};
$val = 1 if $opt =~ /^(max-|min-)/;
$val = 3 if $opt eq 'files-from';
$val = q"$only eq 'r' ? -1 : " . $val if $opt =~ /^(remove-|log-file)/;
$val = q"$only eq 'w' ? -1 : " . $val if $opt eq 'sender';
print " '$opt' => $val,\n";
}
print ");\n\n";

2
packaging/ftp.filt Normal file
View File

@@ -0,0 +1,2 @@
- /generated-files/
- /binaries/

View File

@@ -1,9 +1,9 @@
Summary: A fast, versatile, remote (and local) file-copying tool
Name: rsync
Version: 3.2.2
%define fullversion %{version}pre2
Release: 0.1.pre2
%define srcdir src-previews
Version: 3.4.3
%define fullversion %{version}
Release: 1
%define srcdir src
Group: Applications/Internet
License: GPL
Source0: https://rsync.samba.org/ftp/rsync/%{srcdir}/rsync-%{fullversion}.tar.gz
@@ -79,9 +79,5 @@ rm -rf $RPM_BUILD_ROOT
%dir /etc/rsync-ssl/certs
%changelog
* Sun Jun 28 2020 Wayne Davison <wayne@opencoder.net>
Released 3.2.2pre2.
* Fri Mar 21 2008 Wayne Davison <wayne@opencoder.net>
Added installation of /etc/xinetd.d/rsync file and some commented-out
lines that demonstrate how to use the rsync-patches tar file.
* Wed May 20 2026 Rsync Project <rsync.project@gmail.com>
Released 3.4.3.

View File

@@ -1,99 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2020 Wayne Davison
#
# This program is freely redistributable.
import os, re, argparse
HTML_START = """\
<html><head>
<title>%s</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&display=swap" rel="stylesheet">
<style>
body {
max-width: 50em;
margin: auto;
}
body, b, strong, u {
font-family: 'Roboto', sans-serif;
}
code {
font-family: 'Roboto Mono', monospace;
font-weight: bold;
}
pre code {
display: block;
font-weight: normal;
}
blockquote pre code {
background: #f1f1f1;
}
dd p:first-of-type {
margin-block-start: 0em;
}
</style>
</head><body>
"""
HTML_END = """\
</body></html>
"""
md_parser = None
def main():
for mdfn in args.mdfiles:
if not mdfn.endswith('.md'):
print('Ignoring non-md input file:', mdfn)
continue
title = re.sub(r'.*/', '', mdfn).replace('.md', '')
htfn = mdfn.replace('.md', '.html')
print("Parsing", mdfn, '->', htfn)
with open(mdfn, 'r', encoding='utf-8') as fh:
txt = fh.read()
txt = re.sub(r'\s--\s', '\xa0-- ', txt)
html = md_parser(txt)
html = re.sub(r'(<code>)([\s\S]*?)(</code>)', lambda m: m[1] + re.sub(r'\s', '\xa0', m[2]) + m[3], html)
html = html.replace('--', '&#8209;&#8209;').replace("\xa0-", '&nbsp;&#8209;').replace("\xa0", '&nbsp;')
html = re.sub(r'(\W)-', r'\1&#8209;', html)
if os.path.lexists(htfn):
os.unlink(htfn)
with open(htfn, 'w', encoding='utf-8') as fh:
fh.write(HTML_START % title)
fh.write(html)
fh.write(HTML_END)
def html_via_cmarkgfm(txt):
return cmarkgfm.markdown_to_html(txt)
def html_via_commonmark(txt):
return commonmark.HtmlRenderer().render(commonmark.Parser().parse(txt))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Output html for md pages.', add_help=False)
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
parser.add_argument("mdfiles", nargs='+', help="The .md files to turn into .html files.")
args = parser.parse_args()
try:
import cmarkgfm
md_parser = html_via_cmarkgfm
except:
try:
import commonmark
md_parser = html_via_commonmark
except:
die("Failed to find cmarkgfm or commonmark for python3.")
main()

Some files were not shown because too many files have changed in this diff Show More