Compare commits

...

332 Commits

Author SHA1 Message Date
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
163 changed files with 13927 additions and 5603 deletions

View File

@@ -1,23 +0,0 @@
freebsd_task:
name: FreeBSD
freebsd_instance:
image_family: freebsd-13-0
env:
PATH: /usr/local/bin:$PATH
prep_script:
- dd if=/dev/zero of=/tmp/zpool bs=1M count=1024
- zpool create -m `pwd`/testtmp zpool /tmp/zpool
- pkg install -y bash autotools m4 xxhash zstd liblz4 wget
- wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
configure_script:
- CPPFLAGS=-I/usr/local/include/ LDFLAGS=-L/usr/local/lib/ ./configure --disable-md2man
make_script:
- make
install_script:
- make install
info_script:
- rsync --version
test_script:
- RSYNC_EXPECT_SKIPPED=acls-default,acls,crtimes,protected-regular make check
ssl_file_list_script:
- rsync-ssl --no-motd download.samba.org::rsyncftp/ || true

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

@@ -0,0 +1,77 @@
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.
run: RSYNC_EXPECT_SKIPPED=crtimes make check
- name: ssl file list
run: ./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: almalinux-8-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

View File

@@ -1,125 +0,0 @@
name: build
on:
push:
branches: [ master ]
paths-ignore: [ .cirrus.yml ]
pull_request:
branches: [ master ]
paths-ignore: [ .cirrus.yml ]
schedule:
- cron: '42 8 * * *'
jobs:
ubuntu-build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: prep
run: |
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl wget
wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
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 make check
- name: check30
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check30
- name: check29
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check29
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v2
with:
name: ubuntu-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
macos-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: prep
run: |
brew install automake openssl xxhash zstd lz4 wget
sudo pip3 install commonmark
wget -O git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
echo "/usr/local/bin" >>$GITHUB_PATH
- name: configure
run: CPPFLAGS=-I/usr/local/opt/openssl/include/ LDFLAGS=-L/usr/local/opt/openssl/lib/ ./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=acls-default,chmod-temp-dir,chown-fake,devices-fake,dir-sgid,protected-regular,xattrs-hlink,xattrs make check
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v2
with:
name: macos-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
cygwin-build:
runs-on: windows-2022
if: (github.event_name == 'schedule' || contains(github.event.head_commit.message, '[buildall]'))
steps:
- uses: actions/checkout@v2
- 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
curl.exe -o git-version.h https://gist.githubusercontent.com/WayneD/c11243fa374fc64d4e42f2855c8e3827/raw/rsync-git-version.h
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
run: bash -c 'RSYNC_EXPECT_SKIPPED=acls-default,acls,chown,devices,dir-sgid,protected-regular make check'
- 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@v2
with:
name: cygwin-bin
path: |
rsync.exe
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

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

@@ -0,0 +1,56 @@
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
run: bash -c 'RSYNC_EXPECT_SKIPPED=acls-default,acls,bare-do-open-symlink-race,chdir-symlink-race,chmod-symlink-race,chown,daemon-chroot-acl,devices,dir-sgid,open-noatime,protected-regular,sender-flist-symlink-leak,simd-checksum,symlink-dirlink-basis make check'
- 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:
name: cygwin-bin
path: |
rsync.exe
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

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

@@ -0,0 +1,50 @@
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
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: freebsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

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

@@ -0,0 +1,58 @@
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
run: sudo RSYNC_EXPECT_SKIPPED=acls-default,chmod-temp-dir,chown-fake,daemon-chroot-acl,devices-fake,dir-sgid,open-noatime,protected-regular,simd-checksum,xattrs-hlink,xattrs make check
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: macos-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

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

@@ -0,0 +1,51 @@
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
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: netbsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

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

@@ -0,0 +1,52 @@
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
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: openbsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

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

@@ -0,0 +1,50 @@
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
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: solaris-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

View File

@@ -0,0 +1,60 @@
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 make check
- name: check30
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check30
- name: check29
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check29
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: ubuntu-22.04-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

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

@@ -0,0 +1,56 @@
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
run: sudo make install
- name: info
run: rsync --version
- name: check
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check
- name: check30
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check30
- name: check29
run: sudo RSYNC_EXPECT_SKIPPED=crtimes make check29
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
name: ubuntu-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync

1
.gitignore vendored
View File

@@ -58,3 +58,4 @@ aclocal.m4
/auto-build-save
.deps
/*.exe
*.dSYM/

View File

@@ -26,7 +26,7 @@ build user (after installing python3's pip package):
You can test if you've got it fixed by running (from the rsync checkout):
> ./md2man --test rsync-ssl.1.md
> ./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.
@@ -104,6 +104,8 @@ like.
> 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
@@ -230,6 +232,9 @@ not completely implement the "New Sockets" API.
[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`.
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

View File

@@ -49,20 +49,21 @@ OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
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
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 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
# 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:
@@ -70,6 +71,8 @@ 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
@@ -176,20 +179,20 @@ T_UNSAFE_OBJ = t_unsafe.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/sn
t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
T_CHMOD_SECURE_OBJ = t_chmod_secure.o syscall.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 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 git-version.h
.PHONY: gensend
gensend: gen
if ! diff git-version.h $(srcdir)/gists/rsync-git-version.h >/dev/null; then \
./rsync -ai git-version.h $(srcdir)/gists/rsync-git-version.h && \
(cd $(srcdir)/gists && git commit --allow-empty-message -m '' rsync-git-version.h && git push) ; \
fi
rsync -aic $(GENFILES) git-version.h $${SAMBA_HOST-samba.org}:/home/ftp/pub/rsync/generated-files/ || true
aclocal.m4: $(srcdir)/m4/*.m4
aclocal -I $(srcdir)/m4
@@ -318,20 +321,28 @@ test: check
.PHONY: check
check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
$(srcdir)/runtests.py --rsync-bin=`pwd`/rsync$(EXEEXT)
.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) --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) --protocol=30
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)
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/chown-fake.test:
ln -s chown.test $(srcdir)/testsuite/chown-fake.test
@@ -347,7 +358,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`
# TODO: Add 'dist' target; need to know which files will be included
@@ -364,4 +375,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/

713
NEWS.md
View File

@@ -1,3 +1,685 @@
# NEWS for rsync 3.4.3 (20 May 2026)
## Changes in this version:
### SECURITY FIXES:
Six CVEs are fixed in this release. All six are assigned by
VulnCheck as CNA. Affected versions are 3.4.2 and earlier in every
case. Three of the six (CVE-2026-29518, CVE-2026-43617,
CVE-2026-43619) require non-default daemon configuration to reach:
the first and third need `use chroot = no` for a module, the second
needs `daemon chroot = ...` set in rsyncd.conf. Two (CVE-2026-43618,
CVE-2026-43620) are reachable from a normal pull or a normal
authenticated daemon connection. The sixth (CVE-2026-45232) is
reachable only when `RSYNC_PROXY` is set and the proxy (or a MITM)
returns a pathological response. Many thanks to the external
researchers who reported these issues.
- CVE-2026-29518 (CVSS v4.0 7.3, HIGH): TOCTOU symlink race condition
allowing local privilege escalation in daemon mode without chroot.
An rsync daemon configured with "use chroot = no" was exposed to a
time-of-check / time-of-use race on parent path components: a local
attacker with write access to a module could 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. Default "use chroot = yes" is
not exposed. `secure_relative_open()` (added in 3.4.0 for
CVE-2024-12086) was previously unused in the daemon-no-chroot
case; the fix enables it there and reroutes the sender's
read-path opens through it. Reported by Nullx3D (Batuhan Sancak),
Damien Neil and Michael Stapelberg.
- CVE-2026-43617 (CVSS v3.1 4.8, MEDIUM): Hostname/ACL bypass on an
rsync daemon configured with `daemon chroot = /X` in rsyncd.conf
when the chroot tree lacks DNS resolution support. The
reverse-DNS lookup of the connecting client was performed *after*
the daemon chroot had been entered; if /X did not contain the
libc resolver fixtures (`/etc/resolv.conf`, `/etc/nsswitch.conf`,
`/etc/hosts`, NSS service modules) the lookup failed and the
connecting hostname was set to "UNKNOWN", causing hostname-based
deny rules to silently fail open. IP-based ACLs are unaffected.
The per-module `use chroot` setting is unrelated to this issue.
The fix performs the lookup before entering the daemon chroot.
Reported by MegaManSec.
- CVE-2026-43618 (CVSS v3.1 8.1, HIGH): Integer overflow in the
compressed-token decoder enabling remote memory disclosure to an
authenticated daemon peer. The receiver accumulated a 32-bit
signed counter without overflow checking; a malicious sender could
trigger an overflow that, with careful manipulation, leaked process
memory contents to the attacker -- environment variables,
passwords, heap and library pointers -- significantly weakening
ASLR. The fix bounds the counter and adds wire-input validation in
several adjacent places (defence-in-depth). Workaround for older
releases: `refuse options = compress` in rsyncd.conf. Reported by
Omar Elsayed.
- CVE-2026-43619 (CVSS v3.1 6.3, MEDIUM): Symlink races on path-based
system calls in "use chroot = no" daemon mode (generalisation of
CVE-2026-29518). Earlier fixes for symlink races on the receiver's
open() call missed the same race class on every other path-based
system call: chmod, lchown, utimes, rename, unlink, mkdir, symlink,
mknod, link, rmdir and lstat. The fix routes each affected
path-based syscall through a parent dirfd opened under
RESOLVE_BENEATH-equivalent kernel-enforced confinement (openat2 on
Linux 5.6+, O_RESOLVE_BENEATH on FreeBSD 13+ and macOS 15+,
per-component O_NOFOLLOW walk elsewhere). Default "use chroot =
yes" is not exposed. Reported by Andrew Tridgell as a follow-on
audit of CVE-2026-29518.
- CVE-2026-43620 (CVSS v3.1 6.5, MEDIUM): Out-of-bounds read in the
receiver's recv_files() enabling remote denial-of-service of any
client pulling from a malicious server (incomplete fix of commit
797e17f). The earlier parent_ndx<0 guard added to send_files() was
not applied to the visually-identical block in recv_files(). A
malicious rsync server can drive any connecting client into a
deterministic SIGSEGV by setting CF_INC_RECURSE in the
compatibility flags and sending a crafted file list and transfer
record. inc_recurse is the protocol-30+ default, so no special
options are required on the victim. Workaround for older
releases: `--no-inc-recursive` on the client. Reported by Pratham
Gupta.
- CVE-2026-45232 (CVSS v3.1 3.1, LOW): Off-by-one out-of-bounds stack
write in the rsync client's HTTP CONNECT proxy handler
(`establish_proxy_connection()` in `socket.c`). After issuing the
CONNECT request, rsync read the proxy's first response line one
byte at a time into a 1024-byte stack buffer with the bound
`cp < &buffer[sizeof buffer - 1]`. If the proxy (or a MITM in
front of it) returned 1023+ bytes on that first line without a
newline terminator, `cp` exited the loop pointing at a buffer slot
the loop never wrote, leaving `*cp` holding stale stack data from
the earlier `snprintf()` of the outgoing CONNECT request. The
post-loop logic then wrote a single `\0` one byte past the end of
the buffer on the stack. Reach is client-side only, and only when
`RSYNC_PROXY` is set so rsync tunnels an `rsync://` connection
through an HTTP CONNECT proxy. The written byte is always `\0`
and the offset is fixed by the buffer size, not attacker-chosen,
so this is not an arbitrary-write primitive: practical impact is
corruption of one adjacent stack byte and possible later
misbehaviour or crash. The fix detects the "buffer filled without
finding `\n`" case explicitly by position and refuses the response
with "proxy response line too long". Reported by Aisle Research
via Michal Ruprich (rsync-3.4.1-2.el10 QE).
In addition to the six CVE fixes, this release adds defence-in-depth
hardening on several adjacent paths: bounded wire-supplied counts and
lengths in flist/io/acls/xattrs, a guard against length underflow in
cumulative `snprintf()` callers, a parent block-index bounds check on
the receiver, a NULL check in `read_delay_line()`, a lower ceiling on
`MAX_WIRE_DEL_STAT` to avoid signed-int overflow in the
`read_del_stats()` accumulator, rejection of hyphen-prefixed
remote-shell hostnames (defence-in-depth against argv-injection in
tooling that forwards untrusted input into the hostspec position;
reported by Aisle Research via Michal Ruprich), and a NULL-check on
`localtime_r()` in `timestring()` to keep a malicious server from
crashing the client by advertising a file with an out-of-range
modtime.
### BUG FIXES:
- Fixed a regression introduced by the 3.4.0 secure_relative_open()
CVE fix where legitimate directory symlinks on the receiver side
(e.g. when using `-K` / `--copy-dirlinks`) caused "failed
verification -- update discarded" errors on delta transfers. The
old code rejected every symlink in the path with a per-component
`O_NOFOLLOW` walk; the receiver now uses kernel-enforced "stay
below dirfd" path resolution where available. Fixes #715.
### PORTABILITY / BUILD:
- secure_relative_open() now uses `openat2(RESOLVE_BENEATH |
RESOLVE_NO_MAGICLINKS)` on Linux 5.6+, and `openat()` with
`O_RESOLVE_BENEATH` on FreeBSD 13+ and macOS 15+ (Sequoia) /
iOS 18+. The kernel rejects ".." escapes, absolute symlinks, and
symlinks whose target lies outside the starting directory, while
still following symlinks that resolve within it -- the same
trade-off that fixes the issue #715 regression without weakening
the original CVE protection. Other platforms (Solaris, OpenBSD,
NetBSD, Cygwin) retain the previous per-component `O_NOFOLLOW`
walk; on those platforms the issue #715 regression remains
visible.
- testsuite/xattrs: ignore `SUNWattr_*` in the Solaris `xls`
helper.
### DEVELOPER RELATED:
- Added testsuite/symlink-dirlink-basis.test (taken from PR #864
by Samuel Henrique) covering the issue #715 regression and
several edge cases (`--backup`, `--inplace`, `--partial-dir`
with protocol < 29, top-level files). The test skips on
platforms without a RESOLVE_BENEATH equivalent.
- Added regression tests for the new security fixes:
`chmod-symlink-race.test`, `chdir-symlink-race.test`,
`bare-do-open-symlink-race.test`, `alt-dest-symlink-race.test`,
`copy-dest-source-symlink.test`, `sender-flist-symlink-leak.test`,
`secure-relpath-validation.test`, `daemon-chroot-acl.test` and
`daemon-refuse-compress.test`. The symlink-race tests skip on
Cygwin, Solaris, OpenBSD and NetBSD (no RESOLVE_BENEATH
equivalent on those platforms).
- runtests.py now errors early with a clear message when any of
the test helper programs (`tls`, `trimslash`, `t_unsafe`,
`t_chmod_secure`, `t_secure_relpath`, `wildtest`, `getgroups`,
`getfsdev`) are missing, instead of letting many tests fail with
confusing "not found" errors.
- Added OpenBSD and NetBSD CI jobs that run `make check` on those
platforms.
- Added Ubuntu 22.04 and AlmaLinux 8 CI workflows so future
backports to the two mainstream LTS families build and test on
the same CI surface as trunk.
- testsuite/protected-regular.test now runs unprivileged via
`unshare` with user-namespace UID mapping, falling back to skip
if `unshare`/`uidmap` is not available; previously it required
real root.
- Added `symlink-dirlink-basis` to the Cygwin CI's expected-skipped
list.
- Removed the old release system (replaced by the new release
script in 3.4.2).
------------------------------------------------------------------------------
# NEWS for rsync 3.4.2 (28 Apr 2026)
## Changes in this version:
### SECURITY RELATED:
Several security-relevant defects were reported and fixed since 3.4.1.
None were assigned a CVE — rsync's fork-per-connection design scopes
the impact of each of these to the attacker's own connection, which is
equivalent to the client closing the socket itself — but they are
fixed here as a matter of hygiene and to reduce the chances of a
future exploitable combination. Many thanks to the external
researchers who reported these issues.
- Fixed a signed integer overflow in the PROXY protocol v2 header
parser: a negative `len` field could bypass the size check and cause
a stack buffer overflow in `read_buf()`. Reported by John Walker of
ZeroPath.
- Fixed an invalid access to the files array. Reported by Calum
Hutton of Rapid7.
- Reject negative token values in the compressed-stream token
decoder; a negative value could cause callers to misinterpret a
missing data pointer as literal data. Reported by Will Sergeant.
- Fixed the element count passed to the xattr `qsort()` (see
https://www.openwall.com/lists/oss-security/2026/04/16/2).
- Fixed a buffer underflow in `clean_fname()`, and added a regression
test.
- Fixed an uninitialized `mul_one` in the AVX2 get_checksum1 path
(undefined behaviour), and added a SIMD-checksum self-test that
cross-checks SSE2, SSSE3 and AVX2 against the C reference on both
aligned and unaligned buffers.
- Fixed an uninitialized `buf1` on the first call to
`get_checksum2()` in the MD4 path (fixes #673).
- Zero all new memory from internal allocations: `my_alloc()` now uses
`calloc`, and `expand_item_list()` zeros the expanded portion after
`realloc`. This gives more predictable behaviour if stale or
uninitialised memory is ever accidentally read.
### BUG FIXES:
- Call `tzset()` before chroot so that log timestamps continue to
reflect the configured local timezone after the daemon chroots
(glibc needs `/etc/localtime`, which is unreachable post-chroot).
- Use the correct time when writing to the log file.
- Do not clear `DISPLAY` unconditionally.
- Fixed a Y2038 bug in `syscall.c` by replacing the `Int32x32To64`
macro (which truncates its arguments to 32 bits) with a plain
64-bit multiplication.
- Fixed ACL ID mapping for non-root users (closes #618).
- Fixed handling of objects with many xattrs on FreeBSD.
- Fixed `--open-noatime` not taking effect when opening regular
files: `O_NOATIME` is now also passed to `do_open_nofollow()`, which
has been used for regular files since the CVE fix "fixed symlink
race condition in sender".
- Ignore "directory has vanished" errors.
- Fixed the removal of multiple leading slashes.
- Added the missing `--dirs` long option.
- Fixed a segfault if `poptGetContext()` returns NULL (e.g. under
OOM) by not passing NULL to `poptReadDefaultConfig()`. Reported by
Ronnie Sahlberg; found with `malloc-fail-tester`.
- Fixed a build error on ia64 NonStop (which treats missing
prototypes as an error, not a warning).
- Fixed a flaky hardlinks test (fixes #735).
### ENHANCEMENTS:
- Added multi-threaded `zstd` compression, gated by a new
`--compress-threads=N` option, with validation and man-page
coverage.
- Documented the `temp dir` parameter in the rsyncd.conf man page
(fixes #820).
- Improved rendering of interior dashes in long-option names in
`md-convert` (perhaps fixes #686).
### PORTABILITY / BUILD:
- Fixed glibc 2.43 const-preserving overloads of `strtok()`,
`strchr()` etc. by declaring the affected locals with the right
constness. Contributed by Holger Hoffstätte.
- Converted the bundled zlib 1.2.8 from K&R-style function
definitions to ANSI prototypes, so it builds with clang 16+.
- Avoid using `bool` as an identifier; it is a keyword in C23.
- `configure.ac`: check for xattr functions in libc first and only
fall back to `-lattr`, avoiding spurious overlinking when `-lattr`
happens to be installed. Contributed by Eli Schwartz.
- Made the build reproducible by honouring `SOURCE_DATE_EPOCH` for
the manpage date.
- Removed obsolete `popt/findme.c` and `popt/findme.h` that upstream
popt 1.14 folded into `popt.c` (fixes #710). Contributed by Alan
Coopersmith.
### INTERNAL:
- Made many module-global variables `const` so they can live in
`.rodata` and enable additional compiler optimization.
### DEVELOPER RELATED:
- Replaced `runtests.sh` with `runtests.py`, a Python test runner
that supports `--valgrind` (with per-process log files so valgrind
output no longer interferes with output comparisons) and
`-j/--parallel` execution for roughly a 7× speed-up on typical
hardware.
- Added a SIMD checksum self-test and a `clean-fname-underflow`
regression test.
- Various CI fixes for macOS and Cygwin (including adding
`simd-checksum` to the expected-skipped lists on platforms without
SIMD), and tests now run on `ubuntu-latest`.
- removed support for the unmaintained rsync-patches archive
------------------------------------------------------------------------------
# NEWS for rsync 3.4.1 (16 Jan 2025)
Release 3.4.1 is a fix for regressions introduced in 3.4.0
## Changes in this version:
### BUG FIXES:
- fixed handling of -H flag with conflict in internal flag values
- fixed a user after free in logging of failed rename
- fixed build on systems without openat()
- removed dependency on alloca() in bundled popt
### DEVELOPER RELATED:
- fix to permissions handling in the developer release script
------------------------------------------------------------------------------
# NEWS for rsync 3.4.0 (15 Jan 2025)
Release 3.4.0 is a security release that fixes a number of important vulnerabilities.
For more details on the vulnerabilities please see the CERT report
https://kb.cert.org/vuls/id/952657
## Changes in this version:
### PROTOCOL NUMBER:
- The protocol number was changed to 32 to make it easier for
administrators to check their servers have been updated
### SECURITY FIXES:
Many thanks to Simon Scannell, Pedro Gallegos, and Jasiel Spelman at
Google Cloud Vulnerability Research and Aleksei Gorban (Loqpa) for
discovering these vulnerabilities and working with the rsync project
to develop and test fixes.
- CVE-2024-12084 - Heap Buffer Overflow in Checksum Parsing.
- CVE-2024-12085 - Info Leak via uninitialized Stack contents defeats ASLR.
- CVE-2024-12086 - Server leaks arbitrary client files.
- CVE-2024-12087 - Server can make client write files outside of destination directory using symbolic links.
- CVE-2024-12088 - --safe-links Bypass.
- CVE-2024-12747 - symlink race condition.
### BUG FIXES:
- Fixed the included popt to avoid a memory error on modern gcc versions.
- Fixed an incorrect extern variable's type that caused an ACL issue on macOS.
- Fixed IPv6 configure check
### INTERNAL:
- Updated included popt to version 1.19.
### DEVELOPER RELATED:
- Various improvements to the release scripts and git setup.
- Improved packaging/var-checker to identify variable type issues.
- added FreeBSD and Solaris CI builds
------------------------------------------------------------------------------
# NEWS for rsync 3.3.0 (6 Apr 2024)
## Changes in this version:
### BUG FIXES:
- Fixed a bug with `--sparse --inplace` where a trailing gap in the source
file would not clear out the trailing data in the destination file.
- Fixed an buffer overflow in the checksum2 code if SHA1 is being used for
the checksum2 algorithm.
- Fixed an issue when rsync is compiled using `_FORTIFY_SOURCE` so that the
extra tests don't complain about a strlcpy() limit value (which was too
large, even though it wasn't possible for the larger value to cause an
overflow).
- Add a backtick to the list of characters that the filename quoting needs to
escape using backslashes.
- Fixed a string-comparison issue in the internal handling of `--progress` (a
locale such as tr_TR.utf-8 needed the internal triggering of `--info` options
to use upper-case flag names to ensure that they match).
- Make sure that a local transfer marks the sender side as trusted.
- Change the argv handling to work with a newer popt library -- one that likes
to free more data than it used to.
- Rsync now calls `OpenSSL_add_all_algorithms()` when compiled against an older
openssl library.
- Fixed a problem in the daemon auth for older protocols (29 and before) if the
openssl library is being used to compute MD4 checksums.
- Fixed `rsync -VV` on Cygwin -- it needed a flush of stdout.
- Fixed an old stats bug that counted devices as symlinks.
### ENHANCEMENTS:
- Enhanced rrsync with the `-no-overwrite` option that allows you to ensure
that existing files on your restricted but writable directory can't be
modified.
- Enhanced the manpages to mark links with .UR & .UE. If your nroff doesn't
support these idioms, touch the file `.md2man-force` in the source directory
so that `md-convert` gets called with the `--force-link-text` option, and
that should ensure that your manpages are still readable even with the
ignored markup.
- Some manpage improvements on the handling of [global] modules.
- Changed the mapfrom & mapto perl scripts (in the support dir) into a single
python script named idmap. Converted a couple more perl scripts into python.
- Changed the mnt-excl perl script (in the support dir) into a python script.
### DEVELOPER RELATED:
- Updated config.guess (timestamp 2023-01-01) and config.sub (timestamp
2023-01-21).
------------------------------------------------------------------------------
# NEWS for rsync 3.2.7 (20 Oct 2022)
## Changes in this version:
### BUG FIXES:
- Fixed the client-side validating of the remote sender's filtering behavior.
- More fixes for the "unrequested file-list name" name, including a copy of
"/" with `--relative` enabled and a copy with a lot of related paths with
`--relative` enabled (often derived from a `--files-from` list).
- When rsync gets an unpack error on an ACL, mention the filename.
- Avoid over-setting sanitize_paths when a daemon is serving "/" (even if
"use chroot" is false).
### ENHANCEMENTS:
- Added negotiated daemon-auth support that allows a stronger checksum digest
to be used to validate a user's login to the daemon. Added SHA512, SHA256,
and SHA1 digests to MD5 & MD4. These new digests are at the highest priority
in the new daemon-auth negotiation list.
- Added support for the SHA1 digest in file checksums. While this tends to be
overkill, it is available if someone really needs it. This overly-long
checksum is at the lowest priority in the normal checksum negotiation list.
See [`--checksum-choice`](rsync.1#opt) (`--cc`) and the `RSYNC_CHECKSUM_LIST`
environment var for how to customize this.
- Improved the xattr hash table to use a 64-bit key without slowing down the
key's computation. This should make extra sure that a hash collision doesn't
happen.
- If the `--version` option is repeated (e.g. `-VV`) then the information is
output in a (still readable) JSON format. Client side only.
- The script `support/json-rsync-version` is available to get the JSON style
version output from any rsync. The script accepts either text on stdin
**or** an arg that specifies an rsync executable to run with a doubled
`--version` option. If the text we get isn't already in JSON format, it is
converted. Newer rsync versions will provide more complete json info than
older rsync versions. Various tweaks are made to keep the flag names
consistent across versions.
- The [`use chroot`](rsyncd.conf.5#) daemon parameter now defaults to "unset"
so that rsync can use chroot when it works and a sanitized copy when chroot
is not supported (e.g., for a non-root daemon). Explicitly setting the
parameter to true or false (on or off) behaves the same way as before.
- The `--fuzzy` option was optimized a bit to try to cut down on the amount of
computations when considering a big pool of files. The simple heuristic from
Kenneth Finnegan resulted in about a 2x speedup.
- If rsync is forced to use protocol 29 or before (perhaps due to talking to an
rsync before 3.0.0), the modify time of a file is limited to 4-bytes. Rsync
now interprets this value as an unsigned integer so that a current year past
2038 can continue to be represented. This does mean that years prior to 1970
cannot be represented in an older protocol, but this trade-off seems like the
right choice given that (1) 2038 is very rapidly approaching, and (2) newer
protocols support a much wider range of old and new dates.
- The rsync client now treats an empty destination arg as an error, just like
it does for an empty source arg. This doesn't affect a `host:` arg (which is
treated the same as `host:.`) since the arg is not completely empty. The use
of [`--old-args`](rsync.1#opt) (including via `RSYNC_OLD_ARGS`) allows the
prior behavior of treating an empty destination arg as a ".".
### PACKAGING RELATED:
- The checksum code now uses openssl's EVP methods, which gets rid of various
deprecation warnings and makes it easy to support more digest methods. On
newer systems, the MD4 digest is marked as legacy in the openssl code, which
makes openssl refuse to support it via EVP. You can choose to ignore this
and allow rsync's MD4 code to be used for older rsync connections (when
talking to an rsync prior to 3.0.0) or you can choose to configure rsync to
tell openssl to enable legacy algorithms (see below).
- A simple openssl config file is supplied that can be installed for rsync to
use. If you install packaging/openssl-rsync.cnf to a public spot (such as
`/etc/ssl/openssl-rsync.cnf`) and then run configure with the option
`--with-openssl-conf=/path/name.cnf`, this will cause rsync to export the
configured path in the OPENSSL_CONF environment variable (when the variable
is not already set). This will enable openssl's MD4 code for rsync to use.
- The packager may wish to include an explicit "use chroot = true" in the top
section of their supplied /etc/rsyncd.conf file if the daemon is being
installed to run as the root user (though rsync should behave the same even
with the value unset, a little extra paranoia doesn't hurt).
- I've noticed that some packagers haven't installed support/nameconvert for
users to use in their chrooted rsync configs. Even if it is not installed
as an executable script (to avoid a python3 dependency) it would be good to
install it with the other rsync-related support scripts.
- It would be good to add support/json-rsync-version to the list of installed
support scripts.
------------------------------------------------------------------------------
# NEWS for rsync 3.2.6 (9 Sep 2022)
## Changes in this version:
### BUG FIXES:
- More path-cleaning improvements in the file-list validation code to avoid
rejecting of valid args.
- A file-list validation fix for a [`--files-from`](rsync.1#opt) file that ends
without a line-terminating character.
- Added a safety check that prevents the sender from removing destination files
when a local copy using [`--remove-source-files`](rsync.1#opt) has some files
that are shared between the sending & receiving hierarchies, including the
case where the source dir & destination dir are identical.
- Fixed a bug in the internal MD4 checksum code that could cause the digest
to be sporadically incorrect (the openssl version was/is fine).
- A minor tweak to rrsync added "copy-devices" to the list of known args, but
left it disabled by default.
### ENHANCEMENTS:
- Rename `--protect-args` to [`--secluded-args`](rsync.1#opt) to make it
clearer how it differs from the default backslash-escaped arg-protecting
behavior of rsync. The old option names are still accepted. The
environment-variable override did not change its name.
### PACKAGING RELATED:
- The configure option `--with-protected-args` was renamed to
`--with-secluded-args`. This option makes `--secluded-args` the default
rsync behavior instead of using backslash escaping for protecting args.
- The mkgitver script now makes sure that a `.git` dir/file is in the top-level
source dir before calling `git describe`. It also runs a basic check on the
version value. This should avoid using an unrelated git description for
rsync's version.
### DEVELOPER RELATED:
- The configure script no longer sets the -pedantic-errors CFLAG (which it
used to try to do only for gcc).
- The name_num_obj struct was modified to allow its dynamic name_num_item list
to be initialized in a better way.
------------------------------------------------------------------------------
# NEWS for rsync 3.2.5 (14 Aug 2022)
## Changes in this version:
### SECURITY FIXES:
- Added some file-list safety checking that helps to ensure that a rogue
sending rsync can't add unrequested top-level names and/or include recursive
names that should have been excluded by the sender. These extra safety
checks only require the receiver rsync to be updated. When dealing with an
untrusted sending host, it is safest to copy into a dedicated destination
directory for the remote content (i.e. don't copy into a destination
directory that contains files that aren't from the remote host unless you
trust the remote host). Fixes CVE-2022-29154.
- A fix for CVE-2022-37434 in the bundled zlib (buffer overflow issue).
### BUG FIXES:
- Fixed the handling of filenames specified with backslash-quoted wildcards
when the default remote-arg-escaping is enabled.
- Fixed the configure check for signed char that was causing a host that
defaults to unsigned characters to generate bogus rolling checksums. This
made rsync send mostly literal data for a copy instead of finding matching
data in the receiver's basis file (for a file that contains high-bit
characters).
- Lots of manpage improvements, including an attempt to better describe how
include/exclude filters work.
- If rsync is compiled with an xxhash 0.8 library and then moved to a system
with a dynamically linked xxhash 0.7 library, we now detect this and disable
the XX3 hashes (since these routines didn't stabilize until 0.8).
### ENHANCEMENTS:
- The [`--trust-sender`](rsync.1#opt) option was added as a way to bypass the
extra file-list safety checking (should that be required).
### PACKAGING RELATED:
- A note to those wanting to patch older rsync versions: the changes in this
release requires the quoted argument change from 3.2.4. Then, you'll want
every single code change from 3.2.5 since there is no fluff in this release.
- The build date that goes into the manpages is now based on the developer's
release date, not on the build's local-timezone interpretation of the date.
### DEVELOPER RELATED:
- Configure now defaults GETGROUPS_T to gid_t when cross compiling.
- Configure now looks for the bsd/string.h include file in order to fix the
build on a host that has strlcpy() in the main libc but not defined in the
main string.h file.
------------------------------------------------------------------------------
# NEWS for rsync 3.2.4 (15 Apr 2022)
## Changes in this version:
@@ -5,11 +687,12 @@
### BEHAVIOR CHANGES:
- A new form of arg protection was added that works similarly to the older
[`--protect-args`](rsync.1#opt) (`-s`) option but in a way that avoids
`--protect-args` ([`-s`](rsync.1#opt)) option but in a way that avoids
breaking things like rrsync (the restricted rsync script): rsync now uses
backslash escaping for sending "shell-active" characters to the remote
shell. This includes spaces, so fetching a remote file via a simple quoted
filename value now works by default without any extra quoting:
backslash escaping for sending "shell-active" characters to the remote shell
(such as `$(){}<>#&` and others). This includes spaces, so fetching a remote
file via a quoted filename value now works by default without any extra
quoting:
```shell
rsync -aiv host:'a simple file.pdf' .
@@ -17,10 +700,14 @@
Wildcards are not escaped in filename args, but they are escaped in options
like the [`--suffix`](rsync.1#opt) and [`--usermap`](rsync.1#opt) values.
If your rsync script depends on the old arg-splitting behavior, either run
it with the [`--old-args`](rsync.1#opt) option or `export RSYNC_OLD_ARGS=1`
in the script's environment. See also the [ADVANCED USAGE](rsync.1#)
section of rsync's manpage for how to use a more modern arg style.
If a script depends on the old arg behavior (perhaps because it quotes or
protects the args already, or perhaps because it expects arg splitting),
there are two easy ways to get things going with a modern rsync: either
`export RSYNC_OLD_ARGS=1` in the script's environment (perhaps in the script
itself) or add the option [`--old-args`](rsync.1#opt) to the rsync commands
that are run. See also the [ADVANCED USAGE](rsync.1#) section of rsync's
manpage for how to use a more modern arg style.
- A long-standing bug was preventing rsync from figuring out the current
locale's decimal point character, which made rsync always output numbers
@@ -109,7 +796,7 @@
- Fixed a potential issue in git-set-file-times when handling commits with
high-bit characters in the description & when handling a description that
might mimick the git raw-commit deliniators. (See the support dir.)
might mimic the git raw-commit deliniators. (See the support dir.)
- The bundled systemd/rsync.service file now includes `Restart=on-failure`.
@@ -4482,6 +5169,14 @@
| RELEASE DATE | VER. | DATE OF COMMIT\* | PROTOCOL |
|--------------|--------|------------------|-------------|
| 20 May 2026 | 3.4.3 | | 32 |
| 28 Apr 2026 | 3.4.2 | | 32 |
| 16 Jan 2025 | 3.4.1 | | 32 |
| 15 Jan 2025 | 3.4.0 | 15 Jan 2025 | 32 |
| 06 Apr 2024 | 3.3.0 | | 31 |
| 20 Oct 2022 | 3.2.7 | | 31 |
| 09 Sep 2022 | 3.2.6 | | 31 |
| 14 Aug 2022 | 3.2.5 | | 31 |
| 15 Apr 2022 | 3.2.4 | | 31 |
| 06 Aug 2020 | 3.2.3 | | 31 |
| 04 Jul 2020 | 3.2.2 | | 31 |

View File

@@ -34,7 +34,7 @@ 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/WayneD/rsync/blob/master/INSTALL.md
[1]: https://github.com/RsyncProject/rsync/blob/master/INSTALL.md
SETUP
-----
@@ -112,6 +112,7 @@ 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
--------------
@@ -120,7 +121,7 @@ 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][6] and [on Samba's site][7].
[6]: https://github.com/WayneD/rsync
[6]: https://github.com/RsyncProject/rsync
[7]: https://git.samba.org/?p=rsync.git;a=summary
See [the download page][8] for full details on all the ways to grab the
@@ -132,13 +133,12 @@ source.
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][9] in this
distribution, or at [the Free Software Foundation][10].
[9]: https://github.com/WayneD/rsync/blob/master/COPYING
[9]: https://github.com/RsyncProject/rsync/blob/master/COPYING
[10]: https://www.fsf.org/licenses/gpl.html

View File

@@ -9,4 +9,5 @@ help backporting fixes into an older release, feel free to ask.
Email your vulnerability information to rsync's maintainer:
Wayne Davison <wayne@opencoder.net>
Rsync Project <rsync.project@gmail.com>

View File

@@ -99,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;

10
acls.c
View File

@@ -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))
@@ -764,6 +765,7 @@ static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode
/* 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. */
@@ -980,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);

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);
}
@@ -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);
@@ -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

@@ -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);

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-2022 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
@@ -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:
@@ -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,7 +336,7 @@ void get_checksum2(char *buf, int32 len, char *sum)
}
#endif
case CSUM_MD5: {
md5_context m5;
md_context m5;
uchar seedbuf[4];
md5_begin(&m5);
if (proper_seed_order) {
@@ -242,20 +356,6 @@ void get_checksum2(char *buf, int32 len, char *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,7 +491,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
}
#endif
case CSUM_MD5: {
md5_context m5;
md_context m5;
md5_begin(&m5);
@@ -389,23 +506,6 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *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_context 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_begin(&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_result(&ctx.m5, (uchar *)sum);
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

@@ -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()) {

View File

@@ -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];

View File

@@ -30,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;
@@ -38,6 +39,7 @@ extern int ignore_errors;
extern int preserve_xattrs;
extern int kluge_around_eof;
extern int munge_symlinks;
extern int use_secure_symlinks;
extern int open_noatime;
extern int sanitize_paths;
extern int numeric_ids;
@@ -67,6 +69,7 @@ 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;
@@ -149,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) {
@@ -187,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)
@@ -381,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);
@@ -429,7 +442,7 @@ 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)
@@ -690,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;
@@ -815,6 +828,20 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
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... */
@@ -951,29 +978,20 @@ 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(module_id)) < 0)
@@ -990,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])) {
@@ -1285,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;
}
}

211
compat.c
View File

@@ -52,6 +52,7 @@ 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;
@@ -60,13 +61,16 @@ 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 */
@@ -79,6 +83,9 @@ 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, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
@@ -91,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)
@@ -123,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))
@@ -176,8 +181,8 @@ void set_allow_inc_recurse(void)
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) {
@@ -199,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";
@@ -210,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);
}
}
@@ -223,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;
}
@@ -257,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;
}
@@ -286,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;
@@ -321,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);
@@ -338,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;
@@ -400,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;
}
@@ -433,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);
@@ -464,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;
}
@@ -523,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);
@@ -552,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)
@@ -801,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

@@ -4,7 +4,6 @@ AC_INIT([rsync],[ ],[https://rsync.samba.org/bug-tracking.html])
AC_C_BIGENDIAN
AC_HEADER_DIRENT
AC_HEADER_TIME
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \
unistd.h utime.h compat.h sys/param.h ctype.h sys/wait.h sys/stat.h \
@@ -13,16 +12,17 @@ AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \
netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h mcheck.h \
sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h dl.h \
popt.h popt/popt.h linux/falloc.h netinet/in_systm.h netgroup.h \
zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h)
zlib.h xxhash.h openssl/md4.h openssl/md5.h zstd.h lz4.h sys/file.h \
bsd/string.h)
AC_CHECK_HEADERS([netinet/ip.h], [], [], [[#include <netinet/in.h>]])
AC_HEADER_MAJOR_FIXED
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([byteorder.h])
AC_CONFIG_HEADER(config.h)
AC_CONFIG_HEADERS([config.h])
AC_PREREQ([2.69])
PACKAGE_VERSION=`sed 's/.*"\(.*\)".*/\1/' <$srcdir/version.h`
PACKAGE_VERSION=`sed -n 's/.*RSYNC_VERSION.*"\(.*\)".*/\1/p' <$srcdir/version.h`
AC_MSG_NOTICE([Configuring rsync $PACKAGE_VERSION])
@@ -60,7 +60,6 @@ AC_PROG_AWK
AC_PROG_EGREP
AC_PROG_INSTALL
AC_PROG_MKDIR_P
AC_PROG_CC_STDC
AC_SUBST(SHELL)
AC_PATH_PROG([PERL], [perl])
AC_PATH_PROG([PYTHON3], [python3])
@@ -135,6 +134,16 @@ if test x"$GCC" = x"yes"; then
CFLAGS="$CFLAGS -Wall -W"
fi
AC_ARG_WITH(openssl-conf,
AS_HELP_STRING([--with-openssl-conf=PATH],[set default OPENSSL_CONF path for rsync]))
case "$with_openssl_conf" in
*[^-/a-zA-Z0-9.,=@+_]*) AC_MSG_ERROR([Invalid path given to --with-openssl-conf]) ;;
/*) CFLAGS="$CFLAGS -DSET_OPENSSL_CONF=$with_openssl_conf" ;;
no|'') ;;
yes) AC_MSG_ERROR([No path given to --with-openssl-conf]) ;;
*) AC_MSG_ERROR([Non absolute path given to --with-openssl-conf]) ;;
esac
AC_ARG_WITH(rrsync,
AS_HELP_STRING([--with-rrsync],[also install the rrsync script and its manpage]))
if test x"$with_rrsync" != x"yes"; then
@@ -152,10 +161,10 @@ AC_ARG_WITH(included-popt,
AC_ARG_WITH(included-zlib,
AS_HELP_STRING([--with-included-zlib],[use bundled zlib library, not from system]))
AC_ARG_WITH(protected-args,
AS_HELP_STRING([--with-protected-args],[make --protected-args option the default]))
if test x"$with_protected_args" = x"yes"; then
AC_DEFINE_UNQUOTED(RSYNC_USE_PROTECTED_ARGS, 1, [Define to 1 if --protected-args should be the default])
AC_ARG_WITH(secluded-args,
AS_HELP_STRING([--with-secluded-args],[make --secluded-args option the default]))
if test x"$with_secluded_args" = x"yes"; then
AC_DEFINE_UNQUOTED(RSYNC_USE_SECLUDED_ARGS, 1, [Define to 1 if --secluded-args should be the default])
fi
AC_ARG_WITH(rsync-path,
@@ -379,22 +388,22 @@ AS_HELP_STRING([--disable-ipv6],[disable to omit ipv6 support]),
;;
esac ],
AC_TRY_RUN([ /* AF_INET6 availability check */
AC_RUN_IFELSE([AC_LANG_SOURCE([[ /* AF_INET6 availability check */
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
main()
int main()
{
if (socket(AF_INET6, SOCK_STREAM, 0) < 0)
exit(1);
else
exit(0);
}
],
AC_MSG_RESULT(yes)
AC_DEFINE(INET6, 1, [true if you have IPv6]),
AC_MSG_RESULT(no),
AC_MSG_RESULT(no)
]])],
[AC_MSG_RESULT(yes)
AC_DEFINE(INET6, 1, true if you have IPv6)],
[AC_MSG_RESULT(no)],
[AC_MSG_RESULT(no)]
))
dnl Do you want to disable use of locale functions
@@ -415,6 +424,26 @@ case $host_os in
* ) AC_MSG_RESULT(no);;
esac
# We default to using our zlib unless --with-included-zlib=no is given.
if test x"$with_included_zlib" != x"no"; then
with_included_zlib=yes
elif test x"$ac_cv_header_zlib_h" != x"yes"; then
with_included_zlib=yes
fi
if test x"$with_included_zlib" != x"yes"; then
AC_CHECK_LIB(z, deflateParams, , [with_included_zlib=yes])
fi
AC_MSG_CHECKING([whether to use included zlib])
if test x"$with_included_zlib" = x"yes"; then
AC_MSG_RESULT($srcdir/zlib)
BUILD_ZLIB='$(zlib_OBJS)'
CFLAGS="-I$srcdir/zlib $CFLAGS"
else
AC_DEFINE(EXTERNAL_ZLIB, 1, [Define to 1 if using external zlib])
AC_MSG_RESULT(no)
fi
AC_MSG_CHECKING([whether to enable use of openssl crypto library])
AC_ARG_ENABLE([openssl],
AS_HELP_STRING([--disable-openssl],[disable to omit openssl crypto library]))
@@ -423,10 +452,10 @@ AH_TEMPLATE([USE_OPENSSL],
if test x"$enable_openssl" != x"no"; then
if test x"$ac_cv_header_openssl_md4_h" = x"yes" && test x"$ac_cv_header_openssl_md5_h" = x"yes"; then
AC_MSG_RESULT(yes)
AC_SEARCH_LIBS(MD5_Init, crypto,
AC_SEARCH_LIBS(EVP_MD_CTX_copy, crypto,
[AC_DEFINE(USE_OPENSSL)
enable_openssl=yes],
[err_msg="$err_msg$nl- Failed to find MD5_Init function in openssl crypto lib.";
[err_msg="$err_msg$nl- Failed to find EVP_MD_CTX_copy function in openssl crypto lib.";
no_lib="$no_lib openssl"])
else
AC_MSG_RESULT(no)
@@ -517,7 +546,7 @@ fi
AC_MSG_CHECKING([whether to enable zstd compression])
AC_ARG_ENABLE([zstd],
AC_HELP_STRING([--disable-zstd], [disable to omit zstd compression]))
AS_HELP_STRING([--disable-zstd], [disable to omit zstd compression]))
AH_TEMPLATE([SUPPORT_ZSTD],
[Undefine if you do not want zstd compression. By default this is defined.])
if test x"$enable_zstd" != x"no"; then
@@ -538,7 +567,7 @@ fi
AC_MSG_CHECKING([whether to enable LZ4 compression])
AC_ARG_ENABLE([lz4],
AC_HELP_STRING([--disable-lz4], [disable to omit LZ4 compression]))
AS_HELP_STRING([--disable-lz4], [disable to omit LZ4 compression]))
AH_TEMPLATE([SUPPORT_LZ4],
[Undefine if you do not want LZ4 compression. By default this is defined.])
if test x"$enable_lz4" != x"no"; then
@@ -564,7 +593,7 @@ if test x"$no_lib" != x; then
echo ""
echo "See the INSTALL file for hints on how to install the missing libraries and/or"
echo "how to generate (or fetch) manpages:"
echo " https://github.com/WayneD/rsync/blob/master/INSTALL.md"
echo " https://github.com/RsyncProject/rsync/blob/master/INSTALL.md"
echo ""
echo "To disable one or more features, the relevant configure options are:"
for lib in $no_lib; do
@@ -624,7 +653,11 @@ fi
AC_TYPE_UID_T
AC_CHECK_TYPES([mode_t,off_t,size_t,pid_t,id_t])
AC_TYPE_GETGROUPS
if test "$cross_compiling" = no; then
AC_TYPE_GETGROUPS
else
AC_DEFINE([GETGROUPS_T],[gid_t],[Define to the type of elements in the array set by `getgroups'. Usually this is either `int' or `gid_t'.])
fi
AC_CHECK_MEMBERS([struct stat.st_rdev,
struct stat.st_mtimensec,
struct stat.st_mtimespec.tv_nsec,
@@ -857,7 +890,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd chown chmod lchmod mknod mkfifo \
fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
chflags getattrlist mktime innetgr linkat \
memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
strlcat strlcpy strtol mallinfo mallinfo2 getgroups setgroups geteuid getegid \
strlcat strlcpy stpcpy strtol mallinfo mallinfo2 getgroups setgroups geteuid getegid \
setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
seteuid strerror putenv iconv_open locale_charset nl_langinfo getxattr \
extattr_get_link sigaction sigprocmask setattrlist getgrouplist \
@@ -1066,26 +1099,13 @@ elif test x"$ac_cv_header_popt_h" != x"yes"; then
with_included_popt=yes
fi
if test x"$GCC" = x"yes"; then
if test x"$with_included_popt" != x"yes"; then
# Turn pedantic warnings into errors to ensure an array-init overflow is an error.
CFLAGS="$CFLAGS -pedantic-errors"
else
# Our internal popt code cannot be compiled with pedantic warnings as errors, so try to
# turn off pedantic warnings (which will not lose the error for array-init overflow).
# Older gcc versions don't understand -Wno-pedantic, so check if --help=warnings lists
# -Wpedantic and use that as a flag.
case `$CC --help=warnings 2>/dev/null | grep Wpedantic` in
*-Wpedantic*) CFLAGS="$CFLAGS -pedantic-errors -Wno-pedantic" ;;
esac
fi
fi
AC_MSG_CHECKING([whether to use included libpopt])
if test x"$with_included_popt" = x"yes"; then
AC_MSG_RESULT($srcdir/popt)
BUILD_POPT='$(popt_OBJS)'
CFLAGS="-I$srcdir/popt $CFLAGS"
AC_DEFINE(POPT_SYSCONFDIR, "/etc", [sysconfig dir for popt])
AC_DEFINE(PACKAGE, "rsync", [package name for rsync])
if test x"$ALLOCA" != x
then
# this can be removed when/if we add an included alloca.c;
@@ -1096,28 +1116,8 @@ else
AC_MSG_RESULT(no)
fi
# We default to using our zlib unless --with-included-zlib=no is given.
if test x"$with_included_zlib" != x"no"; then
with_included_zlib=yes
elif test x"$ac_cv_header_zlib_h" != x"yes"; then
with_included_zlib=yes
fi
if test x"$with_included_zlib" != x"yes"; then
AC_CHECK_LIB(z, deflateParams, , [with_included_zlib=yes])
fi
AC_MSG_CHECKING([whether to use included zlib])
if test x"$with_included_zlib" = x"yes"; then
AC_MSG_RESULT($srcdir/zlib)
BUILD_ZLIB='$(zlib_OBJS)'
CFLAGS="-I$srcdir/zlib $CFLAGS"
else
AC_DEFINE(EXTERNAL_ZLIB, 1, [Define to 1 if using external zlib])
AC_MSG_RESULT(no)
fi
AC_CACHE_CHECK([for unsigned char],rsync_cv_SIGNED_CHAR_OK,[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[signed char *s = ""]])],[rsync_cv_SIGNED_CHAR_OK=yes],[rsync_cv_SIGNED_CHAR_OK=no])])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[signed char *s = (signed char *)""]])],[rsync_cv_SIGNED_CHAR_OK=yes],[rsync_cv_SIGNED_CHAR_OK=no])])
if test x"$rsync_cv_SIGNED_CHAR_OK" = x"yes"; then
AC_DEFINE(SIGNED_CHAR_OK, 1, [Define to 1 if "signed char" is a valid type])
fi
@@ -1392,7 +1392,7 @@ else
AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs (or equivalent)])
AC_DEFINE(SUPPORT_XATTRS, 1)
AC_DEFINE(NO_SYMLINK_USER_XATTRS, 1, [True if symlinks do not support user xattrs])
AC_CHECK_LIB(attr,getxattr)
AC_SEARCH_LIBS(getxattr,attr)
;;
darwin*)
AC_MSG_RESULT(Using OS X xattrs)

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

View File

@@ -6,7 +6,7 @@
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 struct parm_struct parm_table[] = {"
parms = "\nstatic const struct parm_struct parm_table[] = {"
comment_fmt = "\n/********** %s **********/\n"
tdstruct = "typedef struct {"
}

View File

@@ -60,9 +60,9 @@ BOOL read_only True
BOOL reverse_lookup True
BOOL strict_modes True
BOOL transfer_logging False
BOOL use_chroot True
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++;
}

327
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-2022 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
@@ -25,16 +25,21 @@
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];
@@ -44,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)
@@ -70,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
@@ -212,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) {
@@ -292,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)
{
@@ -442,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));
@@ -456,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;
@@ -625,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))
@@ -706,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();
@@ -718,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);
}
@@ -744,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,
@@ -890,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 */
@@ -1298,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;
@@ -1313,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)) {
@@ -1355,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;
}

71
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-2022 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;
@@ -73,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;
@@ -145,7 +146,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;
}
@@ -754,7 +756,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)
@@ -834,13 +836,13 @@ 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)
#ifndef CAN_SET_NSEC
(void)read_varint(f);
(void)read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
#else
modtime_nsec = read_varint(f);
modtime_nsec = read_varint_bounded(f, 0, MAX_WIRE_NSEC, "modtime_nsec");
else
modtime_nsec = 0;
#endif
@@ -859,8 +861,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
#endif
}
#endif
if (!(xflags & XMIT_SAME_MODE))
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. */
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
@@ -986,6 +999,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 */
@@ -1375,7 +1401,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
if (st.st_size == 0) {
int fd = do_open(fname, O_RDONLY, 0);
int fd = do_open_checklinks(fname);
if (fd >= 0) {
st.st_size = get_device_size(fd, fname);
close(fd);
@@ -2352,7 +2378,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;
@@ -2569,6 +2595,19 @@ 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);
@@ -2627,7 +2666,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;
}
@@ -2644,7 +2683,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++;
@@ -3139,8 +3178,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-2022 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
@@ -229,11 +229,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;
@@ -247,6 +249,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)
@@ -783,7 +789,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;
@@ -875,9 +881,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));
@@ -981,7 +990,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
@@ -1109,7 +1118,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);
@@ -1312,7 +1321,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
}
if (relative_paths && !implied_dirs && file->mode != 0
&& do_stat(dn, &sx.st) < 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) {
@@ -1424,7 +1433,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
&& (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;
}
@@ -1466,10 +1475,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));
@@ -1496,7 +1505,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));
@@ -1795,7 +1804,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
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(fnamecmp, O_RDONLY, 0)) >= 0)
if ((fd = do_open_nofollow(fnamecmp, O_RDONLY)) >= 0)
real_sx.st.st_size = sx.st.st_size = get_device_size(fd, fnamecmp);
}
@@ -1805,7 +1814,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
;
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);
@@ -1819,7 +1828,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;
}
@@ -1864,7 +1873,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
/* open the file */
if (fd < 0 && (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:
@@ -1893,7 +1902,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
back_file = NULL;
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;
@@ -2013,7 +2022,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;
@@ -2029,7 +2038,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;
@@ -2037,10 +2046,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;
}
}
@@ -2104,7 +2117,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)) {
@@ -2139,6 +2152,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)
@@ -2167,6 +2182,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)

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

10
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]];
@@ -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) {

114
io.c
View File

@@ -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,6 +55,7 @@ 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;
@@ -84,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;
@@ -113,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 */
@@ -376,6 +380,7 @@ 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;
}
@@ -419,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'. */
@@ -434,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)
@@ -450,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
@@ -1057,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]);
@@ -1071,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
@@ -1125,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'
@@ -1567,14 +1603,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);
@@ -1751,6 +1788,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 {
@@ -1824,6 +1868,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
@@ -1930,6 +2013,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",
@@ -1937,7 +2035,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);

View File

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

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

@@ -20,7 +20,6 @@
#include "rsync.h"
#if !defined USE_OPENSSL || USE_MD5_ASM /* { */
void md5_begin(md_context *ctx)
{
ctx->A = 0x67452301;
@@ -198,7 +197,7 @@ void md5_update(md_context *ctx, const uchar *input, uint32 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])
{
@@ -224,7 +223,6 @@ 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 /* { */

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,14 +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]);
#if defined USE_OPENSSL && !defined USE_MD5_ASM
#define md5_context MD5_CTX
#define md5_begin MD5_Init
#define md5_update MD5_Update
#define md5_result(cptr, digest) MD5_Final(digest, cptr)
#else
#define md5_context md_context
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

@@ -9,7 +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)(); /* called if malloc fails */
void (*bomb)(const char*, const char*, int); /* called if malloc fails */
int flags;
/* statistical data */
@@ -42,6 +42,7 @@ 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*, const char*, int), int flags)
@@ -100,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);
@@ -235,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);
@@ -292,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

@@ -2,7 +2,7 @@
* Unix SMB/CIFS implementation.
* Based on the Samba ACL support code.
* Copyright (C) Jeremy Allison 2000.
* Copyright (C) 2007-2020 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* The permission functions have been changed to get/set all bits via
* one call. Some functions that rsync doesn't need were also removed.
@@ -175,7 +175,7 @@ int sys_acl_delete_def_file(const char *name)
return acl_delete_def_file(name);
}
int sys_acl_free_acl(SMB_ACL_T the_acl)
int sys_acl_free_acl(SMB_ACL_T the_acl)
{
return acl_free(the_acl);
}
@@ -185,7 +185,7 @@ int sys_acl_free_acl(SMB_ACL_T the_acl)
* The interface to DEC/Compaq Tru64 UNIX ACLs
* is based on Draft 13 of the POSIX spec which is
* slightly different from the Draft 16 interface.
*
*
* Also, some of the permset manipulation functions
* such as acl_clear_perm() and acl_add_perm() appear
* to be broken on Tru64 so we have to manipulate
@@ -310,7 +310,7 @@ int sys_acl_delete_def_file(const char *name)
return acl_delete_def_file((char *)name);
}
int sys_acl_free_acl(SMB_ACL_T the_acl)
int sys_acl_free_acl(SMB_ACL_T the_acl)
{
return acl_free(the_acl);
}
@@ -457,7 +457,7 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
break;
}
ndefault = count - naccess;
/*
* if the caller wants the default ACL we have to copy
* the entries down to the start of the acl[] buffer
@@ -517,7 +517,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
if (acl_d->acl[naccess].a_type & ACL_DEFAULT)
break;
}
acl_d->count = naccess;
return acl_d;
@@ -532,7 +532,7 @@ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *b
if (*tag_type_p == SMB_ACL_USER || *tag_type_p == SMB_ACL_GROUP)
*u_g_id_p = entry->a_id;
return 0;
}
@@ -633,7 +633,7 @@ static int acl_sort(SMB_ACL_T acl_d)
}
return 0;
}
int sys_acl_valid(SMB_ACL_T acl_d)
{
return acl_sort(acl_d);
@@ -755,11 +755,11 @@ int sys_acl_delete_def_file(const char *path)
ret = acl(path, SETACL, acl_d->count, acl_d->acl);
sys_acl_free_acl(acl_d);
return ret;
}
int sys_acl_free_acl(SMB_ACL_T acl_d)
int sys_acl_free_acl(SMB_ACL_T acl_d)
{
SAFE_FREE(acl_d);
return 0;
@@ -895,10 +895,10 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
int ndefault; /* # of default ACL entries */
if (hpux_acl_call_presence() == False) {
/* Looks like we don't have the acl() system call on HPUX.
/* Looks like we don't have the acl() system call on HPUX.
* May be the system doesn't have the latest version of JFS.
*/
return NULL;
return NULL;
}
if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
@@ -949,7 +949,7 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
break;
}
ndefault = count - naccess;
/*
* if the caller wants the default ACL we have to copy
* the entries down to the start of the acl[] buffer
@@ -1109,9 +1109,9 @@ struct hpux_acl_types {
* aclp - Array of ACL structures.
* acl_type_count - Pointer to acl_types structure. Should already be
* allocated.
* Output:
* Output:
*
* acl_type_count - This structure is filled up with counts of various
* acl_type_count - This structure is filled up with counts of various
* acl types.
*/
@@ -1123,28 +1123,28 @@ static void hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_type
for (i = 0; i < acl_count; i++) {
switch (aclp[i].a_type) {
case USER:
case USER:
acl_type_count->n_user++;
break;
case USER_OBJ:
case USER_OBJ:
acl_type_count->n_user_obj++;
break;
case DEF_USER_OBJ:
case DEF_USER_OBJ:
acl_type_count->n_def_user_obj++;
break;
case GROUP:
case GROUP:
acl_type_count->n_group++;
break;
case GROUP_OBJ:
case GROUP_OBJ:
acl_type_count->n_group_obj++;
break;
case DEF_GROUP_OBJ:
case DEF_GROUP_OBJ:
acl_type_count->n_def_group_obj++;
break;
case OTHER_OBJ:
case OTHER_OBJ:
acl_type_count->n_other_obj++;
break;
case DEF_OTHER_OBJ:
case DEF_OTHER_OBJ:
acl_type_count->n_def_other_obj++;
break;
case CLASS_OBJ:
@@ -1159,14 +1159,14 @@ static void hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_type
case DEF_GROUP:
acl_type_count->n_def_group++;
break;
default:
default:
acl_type_count->n_illegal_obj++;
break;
}
}
}
/* swap_acl_entries: Swaps two ACL entries.
/* swap_acl_entries: Swaps two ACL entries.
*
* Inputs: aclp0, aclp1 - ACL entries to be swapped.
*/
@@ -1189,25 +1189,25 @@ static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1)
}
/* prohibited_duplicate_type
* Identifies if given ACL type can have duplicate entries or
* Identifies if given ACL type can have duplicate entries or
* not.
*
* Inputs: acl_type - ACL Type.
*
* Outputs:
* Outputs:
*
* Return..
* Return..
*
* True - If the ACL type matches any of the prohibited types.
* False - If the ACL type doesn't match any of the prohibited types.
*/
*/
static BOOL hpux_prohibited_duplicate_type(int acl_type)
{
switch (acl_type) {
case USER:
case GROUP:
case DEF_USER:
case DEF_USER:
case DEF_GROUP:
return True;
default:
@@ -1217,7 +1217,7 @@ static BOOL hpux_prohibited_duplicate_type(int acl_type)
/* get_needed_class_perm
* Returns the permissions of a ACL structure only if the ACL
* type matches one of the pre-determined types for computing
* type matches one of the pre-determined types for computing
* CLASS_OBJ permissions.
*
* Inputs: aclp - Pointer to ACL structure.
@@ -1226,17 +1226,17 @@ static BOOL hpux_prohibited_duplicate_type(int acl_type)
static int hpux_get_needed_class_perm(struct acl *aclp)
{
switch (aclp->a_type) {
case USER:
case GROUP_OBJ:
case GROUP:
case DEF_USER_OBJ:
case USER:
case GROUP_OBJ:
case GROUP:
case DEF_USER_OBJ:
case DEF_USER:
case DEF_GROUP_OBJ:
case DEF_GROUP_OBJ:
case DEF_GROUP:
case DEF_CLASS_OBJ:
case DEF_OTHER_OBJ:
case DEF_OTHER_OBJ:
return aclp->a_perm;
default:
default:
return 0;
}
}
@@ -1267,15 +1267,15 @@ static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
#if !defined(HAVE_HPUX_ACLSORT)
/*
* The aclsort() system call is available on the latest HPUX General
* Patch Bundles. So for HPUX, we developed our version of acl_sort
* function. Because, we don't want to update to a new
* Patch Bundles. So for HPUX, we developed our version of acl_sort
* function. Because, we don't want to update to a new
* HPUX GR bundle just for aclsort() call.
*/
struct hpux_acl_types acl_obj_count;
int n_class_obj_perm = 0;
int i, j;
if (!acl_count) {
DEBUG(10, ("Zero acl count passed. Returning Success\n"));
return 0;
@@ -1290,8 +1290,8 @@ static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp)
hpux_count_obj(acl_count, aclp, &acl_obj_count);
/* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
* CLASS_OBJ and OTHER_OBJ
/* There should be only one entry each of type USER_OBJ, GROUP_OBJ,
* CLASS_OBJ and OTHER_OBJ
*/
if (acl_obj_count.n_user_obj != 1
@@ -1313,15 +1313,15 @@ or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n"));
return -1;
}
/* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
* structures.
/* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl
* structures.
*
* Sorting crieteria - First sort by ACL type. If there are multiple entries of
* same ACL type, sort by ACL id.
*
* I am using the trivial kind of sorting method here because, performance isn't
* I am using the trivial kind of sorting method here because, performance isn't
* really effected by the ACLs feature. More over there aren't going to be more
* than 17 entries on HPUX.
* than 17 entries on HPUX.
*/
for (i = 0; i < acl_count; i++) {
@@ -1390,7 +1390,7 @@ static int acl_sort(SMB_ACL_T acl_d)
}
return 0;
}
int sys_acl_valid(SMB_ACL_T acl_d)
{
return acl_sort(acl_d);
@@ -1405,11 +1405,11 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d)
int ret;
if (hpux_acl_call_presence() == False) {
/* Looks like we don't have the acl() system call on HPUX.
/* Looks like we don't have the acl() system call on HPUX.
* May be the system doesn't have the latest version of JFS.
*/
errno=ENOSYS;
return -1;
return -1;
}
if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) {
@@ -1538,11 +1538,11 @@ int sys_acl_delete_def_file(const char *path)
ret = acl(path, ACL_SET, acl_d->count, acl_d->acl);
sys_acl_free_acl(acl_d);
return ret;
}
int sys_acl_free_acl(SMB_ACL_T acl_d)
int sys_acl_free_acl(SMB_ACL_T acl_d)
{
free(acl_d);
return 0;
@@ -1723,7 +1723,7 @@ int sys_acl_delete_def_file(const char *name)
return acl_delete_def_file(name);
}
int sys_acl_free_acl(SMB_ACL_T acl_d)
int sys_acl_free_acl(SMB_ACL_T acl_d)
{
if (acl_d->freeaclp) {
acl_free(acl_d->aclp);
@@ -1834,12 +1834,12 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
}
/* Get the acl using statacl */
DEBUG(10, ("Entering sys_acl_get_file\n"));
DEBUG(10, ("path_p is %s\n", path_p));
file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
if (file_acl == NULL) {
errno=ENOMEM;
DEBUG(0, ("Error in AIX sys_acl_get_file: %d\n", errno));
@@ -1931,9 +1931,9 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
* to be specified but, it's better than leaving it 0 */
acl_entry_link->entryp->ace_type = acl_entry->ace_type;
acl_entry_link->entryp->ace_access = acl_entry->ace_access;
memcpy(acl_entry_link->entryp->ace_id, idp, sizeof (struct ace_id));
/* The access in the acl entries must be left shifted by *
@@ -1962,7 +1962,7 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
DEBUG(10, ("acl_entry = %d\n", acl_entry));
DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
acl_entry = acl_nxt(acl_entry);
}
} /* end of if enabled */
@@ -2014,12 +2014,12 @@ SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type)
new_acl_entry->ace_access = file_acl->o_access << 6;
idp->id_type = SMB_ACL_OTHER;
break;
case 1:
new_acl_entry->ace_access = file_acl->u_access << 6;
idp->id_type = SMB_ACL_USER_OBJ;
break;
default:
return NULL;
@@ -2048,7 +2048,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
int rc = 0;
/* Get the acl using fstatacl */
DEBUG(10, ("Entering sys_acl_get_fd\n"));
DEBUG(10, ("fd is %d\n", fd));
file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
@@ -2095,12 +2095,12 @@ SMB_ACL_T sys_acl_get_fd(int fd)
DEBUG(10, ("acl_entry is %d\n", acl_entry));
DEBUG(10, ("acl_last(file_acl) id %d\n", acl_last(file_acl)));
/* Check if the extended acl bit is on. *
* If it isn't, do not show the *
* contents of the acl since AIX intends *
* the extended info to remain unused */
if (file_acl->acl_mode & S_IXACL){
/* while we are not pointing to the very end */
while (acl_entry < acl_last(file_acl)) {
@@ -2115,7 +2115,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
}
idp = acl_entry->ace_id;
/* Check if this is the first entry in the linked list. *
* The first entry needs to keep prevp pointing to NULL *
* and already has entryp allocated. */
@@ -2177,7 +2177,7 @@ SMB_ACL_T sys_acl_get_fd(int fd)
DEBUG(10, ("acl_entry = %d\n", acl_entry));
DEBUG(10, ("The ace_type is %d\n", acl_entry->ace_type));
acl_entry = acl_nxt(acl_entry);
}
} /* end of if enabled */
@@ -2210,43 +2210,43 @@ SMB_ACL_T sys_acl_get_fd(int fd)
}
acl_entry_link->nextp = NULL;
new_acl_entry = acl_entry_link->entryp;
idp = new_acl_entry->ace_id;
new_acl_entry->ace_len = sizeof (struct acl_entry);
new_acl_entry->ace_type = ACC_PERMIT;
idp->id_len = sizeof (struct ace_id);
DEBUG(10, ("idp->id_len = %d\n", idp->id_len));
memset(idp->id_data, 0, sizeof (uid_t));
switch (i) {
case 2:
new_acl_entry->ace_access = file_acl->g_access << 6;
idp->id_type = SMB_ACL_GROUP_OBJ;
break;
case 3:
new_acl_entry->ace_access = file_acl->o_access << 6;
idp->id_type = SMB_ACL_OTHER;
break;
case 1:
new_acl_entry->ace_access = file_acl->u_access << 6;
idp->id_type = SMB_ACL_USER_OBJ;
break;
default:
return NULL;
}
acl_entry_link_head->count++;
DEBUG(10, ("new_acl_entry->ace_access = %d\n", new_acl_entry->ace_access));
}
acl_entry_link_head->count = 0;
SAFE_FREE(file_acl);
return acl_entry_link_head;
}
#endif
@@ -2274,7 +2274,7 @@ int sys_acl_get_info(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T *tag_type_p, uint32 *b
SMB_ACL_T sys_acl_init(int count)
{
struct acl_entry_link *theacl = NULL;
if (count < 0) {
errno = EINVAL;
return NULL;
@@ -2383,9 +2383,9 @@ int sys_acl_valid(SMB_ACL_T theacl)
}
DEBUG(10, ("user_obj=%d, group_obj=%d, other_obj=%d\n", user_obj, group_obj, other_obj));
if (user_obj != 1 || group_obj != 1 || other_obj != 1)
return -1;
return -1;
return 0;
}
@@ -2404,7 +2404,7 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
DEBUG(10, ("Entering sys_acl_set_file\n"));
DEBUG(10, ("File name is %s\n", name));
/* AIX has no default ACL */
if (acltype == SMB_ACL_TYPE_DEFAULT)
return 0;
@@ -2449,7 +2449,7 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
errno = ENOMEM;
DEBUG(0, ("Error in sys_acl_set_file is %d\n", errno));
return -1;
}
}
memcpy(file_acl_temp, file_acl, file_acl->acl_len);
SAFE_FREE(file_acl);
@@ -2460,15 +2460,15 @@ int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
file_acl->acl_len += sizeof (struct acl_entry);
acl_entry->ace_len = acl_entry_link->entryp->ace_len;
acl_entry->ace_access = acl_entry_link->entryp->ace_access;
/* In order to use this, we'll need to wait until we can get denies */
/* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
acl_entry->ace_type = ACC_SPECIFY; */
acl_entry->ace_type = ACC_SPECIFY;
ace_id = acl_entry->ace_id;
ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
DEBUG(10, ("The id type is %d\n", ace_id->id_type));
ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
@@ -2496,7 +2496,7 @@ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
uint user_id;
uint acl_length;
uint rc;
DEBUG(10, ("Entering sys_acl_set_fd\n"));
acl_length = BUFSIZ;
file_acl = (struct acl *)SMB_MALLOC(BUFSIZ);
@@ -2508,7 +2508,7 @@ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
}
memset(file_acl, 0, BUFSIZ);
file_acl->acl_len = ACL_SIZ;
file_acl->acl_mode = S_IXACL;
@@ -2550,22 +2550,22 @@ int sys_acl_set_fd(int fd, SMB_ACL_T theacl)
file_acl->acl_len += sizeof (struct acl_entry);
acl_entry->ace_len = acl_entry_link->entryp->ace_len;
acl_entry->ace_access = acl_entry_link->entryp->ace_access;
/* In order to use this, we'll need to wait until we can get denies */
/* if (!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT)
acl_entry->ace_type = ACC_SPECIFY; */
acl_entry->ace_type = ACC_SPECIFY;
ace_id = acl_entry->ace_id;
ace_id->id_type = acl_entry_link->entryp->ace_id->id_type;
DEBUG(10, ("The id type is %d\n", ace_id->id_type));
ace_id->id_len = acl_entry_link->entryp->ace_id->id_len;
memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof (uid_t));
memcpy(ace_id->id_data, &user_id, sizeof (uid_t));
}
rc = fchacl(fd, file_acl, file_acl->acl_len);
DEBUG(10, ("errno is %d\n", errno));
DEBUG(10, ("return code is %d\n", rc));
@@ -2594,7 +2594,7 @@ int sys_acl_free_acl(SMB_ACL_T posix_acl)
SAFE_FREE(acl_entry_link->prevp);
SAFE_FREE(acl_entry_link->entryp);
SAFE_FREE(acl_entry_link);
return 0;
}

View File

@@ -3,7 +3,7 @@
* Version 2.2.x
* Portable SMB ACL interface
* Copyright (C) Jeremy Allison 2000
* 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
@@ -232,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

@@ -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;
}

View File

@@ -65,7 +65,7 @@ typedef enum {
struct enum_list {
int value;
char *name;
const char *name;
};
struct parm_struct {
@@ -73,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;
};
@@ -95,7 +95,7 @@ static item_list section_list = EMPTY_ITEM_LIST;
static int iSectionIndex = -1;
static BOOL bInGlobalSection = True;
static struct enum_list enum_syslog_facility[] = {
static const struct enum_list enum_syslog_facility[] = {
#ifdef LOG_AUTH
{ LOG_AUTH, "auth" },
#endif
@@ -178,7 +178,7 @@ static char *expand_vars(const char *str)
for (t = buf, f = str; bufsize && *f; ) {
if (*f == '%' && isUpper(f+1)) {
char *percent = strchr(f+1, '%');
const char *percent = strchr(f+1, '%');
if (percent && percent - f < bufsize) {
char *val;
strlcpy(t, f+1, percent - f);

22
log.c
View File

@@ -36,8 +36,6 @@ extern int protocol_version;
extern int always_checksum;
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;
@@ -456,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,
@@ -680,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';

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],

82
main.c
View File

@@ -66,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;
@@ -89,6 +89,8 @@ 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;
@@ -104,7 +106,7 @@ 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;
@@ -237,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()
@@ -384,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];
@@ -392,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 = ", ";
}
}
@@ -660,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,
@@ -687,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, '/');
@@ -1076,6 +1096,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);
@@ -1371,15 +1392,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.
@@ -1395,10 +1407,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 */
@@ -1431,6 +1439,8 @@ static int start_client(int argc, char *argv[])
if (argc > 1) {
p = argv[--argc];
if (!*p)
p = dot_dir_or_error();
remote_argv = argv + argc;
} else {
static char *dotarg[1] = { "." };
@@ -1472,8 +1482,10 @@ static int start_client(int argc, char *argv[])
}
/* A local transfer doesn't unbackslash anything, so leave the args alone. */
if (local_server)
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 = ".";
@@ -1500,6 +1512,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++) {
@@ -1523,6 +1537,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);
}
}
@@ -1553,6 +1568,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)) {
@@ -1737,7 +1756,20 @@ int main(int argc,char *argv[])
our_gid = MY_GID();
am_root = our_uid == ROOT_UID;
unset_env_var("DISPLAY");
// 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));

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

@@ -8,6 +8,7 @@ fi
inname="$1"
srcdir=`dirname "$0"`
flagfile="$srcdir/.md2man-works"
force_flagfile="$srcdir/.md2man-force"
if [ ! -f "$flagfile" ]; then
# We test our smallest manpage just to see if the python setup works.
@@ -32,4 +33,10 @@ if [ ! -f "$flagfile" ]; then
fi
fi
"$srcdir/md-convert" "$srcdir/$inname"
if [ -f "$force_flagfile" ]; then
opt='--force-link-text'
else
opt=''
fi
"$srcdir/md-convert" $opt "$srcdir/$inname"

View File

@@ -115,10 +115,12 @@ 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+)=(.+)')
QUOTED_RE = re.compile(r'"(.+?)"')
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')
@@ -213,6 +215,7 @@ def find_man_substitutions():
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:
@@ -224,8 +227,10 @@ def find_man_substitutions():
with open(srcdir + 'version.h', 'r', encoding='utf-8') as fh:
txt = fh.read()
m = QUOTED_RE.search(txt)
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:
@@ -241,7 +246,10 @@ def find_man_substitutions():
if var == 'srcdir':
break
env_subs['date'] = time.strftime('%d %b %Y', time.localtime(mtime))
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):
@@ -272,7 +280,10 @@ class TransformHtml(HTMLParser):
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 = '',
)
@@ -311,6 +322,13 @@ class TransformHtml(HTMLParser):
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:
@@ -383,13 +401,20 @@ class TransformHtml(HTMLParser):
for var, val in attrs_list:
if var == 'href':
if val.startswith(('https://', 'http://', 'mailto:', 'ftp:')):
pass # nothing to check
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('#', 2)
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:
@@ -405,6 +430,8 @@ class TransformHtml(HTMLParser):
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 = ''
@@ -469,12 +496,20 @@ class TransformHtml(HTMLParser):
elif tag == 'hr':
return
elif tag == 'a':
if st.a_href:
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('#', 2)
pg, tgt = st.a_href.split('#', 1)
derived = txt2target(atxt, tgt)
if pg == '':
if derived in st.latest_targets:
@@ -509,6 +544,7 @@ class TransformHtml(HTMLParser):
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)
@@ -605,12 +641,13 @@ def die(*msg):
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Output html and (optionally) nroff for markdown pages.", add_help=False)
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="Put files into DIR instead of the current directory.")
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", nargs='+', help="The source .md files to convert.")
parser.add_argument("mdfiles", metavar='FILE.md', nargs='+', help="One or more .md files to convert.")
args = parser.parse_args()
try:

View File

@@ -1,14 +1,16 @@
#!/bin/sh
srcdir=`dirname $0`
gitver=`git describe --abbrev=8 2>/dev/null`
if [ ! -f git-version.h ]; then
touch git-version.h
fi
case "$gitver" in
*.*)
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"
@@ -16,5 +18,5 @@ case "$gitver" in
else
rm git-version.h.new
fi
;;
esac
fi
fi

233
options.c
View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2000, 2001, 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2002-2022 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
@@ -27,6 +27,8 @@
extern int module_id;
extern int local_server;
extern int sanitize_paths;
extern int trust_sender_args;
extern int trust_sender_filter;
extern unsigned int module_dirlen;
extern filter_rule_list filter_list;
extern filter_rule_list daemon_filter_list;
@@ -64,6 +66,7 @@ int preserve_atimes = 0;
int preserve_crtimes = 0;
int omit_dir_times = 0;
int omit_link_times = 0;
int trust_sender = 0;
int update_only = 0;
int open_noatime = 0;
int cvs_exclude = 0;
@@ -83,6 +86,7 @@ int sparse_files = 0;
int preallocate_files = 0;
int do_compression = 0;
int do_compression_level = CLVL_NOT_SPECIFIED;
int do_compression_threads = 0; /*n = 0 use rsync thread, n >= 1 spawn n threads for compression */
int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
int am_server = 0;
int am_sender = 0;
@@ -110,11 +114,20 @@ int mkpath_dest_arg = 0;
int allow_inc_recurse = 1;
int xfer_dirs = -1;
int am_daemon = 0;
/* Set after a successful per-module chroot ("use chroot = yes") in
* clientserver.c. NOT set for the daemon-level "daemon chroot = /X"
* chroot: that confines path resolution to /X, but module paths
* /X/modA, /X/modB, etc. are not chroot boundaries, so the per-module
* symlink-race defenses (secure_relative_open() / do_*_at() in
* syscall.c, gated by `am_daemon && !am_chrooted`) must still fire
* even when the daemon is inside a daemon chroot. */
int am_chrooted = 0;
int connect_timeout = 0;
int keep_partial = 0;
int safe_symlinks = 0;
int copy_unsafe_links = 0;
int munge_symlinks = 0;
int use_secure_symlinks = 0;
int size_only = 0;
int daemon_bwlimit = 0;
int bwlimit = 0;
@@ -197,6 +210,7 @@ int remote_option_cnt = 0;
const char **remote_options = NULL;
const char *checksum_choice = NULL;
const char *compress_choice = NULL;
static const char *empty_argv[1];
int quiet = 0;
int output_motd = 1;
@@ -221,7 +235,7 @@ char *iconv_opt =
struct chmod_mode_struct *chmod_modes = NULL;
static const char *debug_verbosity[] = {
static const char *const debug_verbosity[] = {
/*0*/ NULL,
/*1*/ NULL,
/*2*/ "BIND,CMD,CONNECT,DEL,DELTASUM,DUP,FILTER,FLIST,ICONV",
@@ -232,7 +246,7 @@ static const char *debug_verbosity[] = {
#define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1)
static const char *info_verbosity[1+MAX_VERBOSITY] = {
static const char *const info_verbosity[1+MAX_VERBOSITY] = {
/*0*/ "NONREG",
/*1*/ "COPY,DEL,FLIST,MISC,NAME,STATS,SYMSAFE",
/*2*/ "BACKUP,MISC2,MOUNT,NAME2,REMOVE,SKIP",
@@ -293,7 +307,7 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = {
DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"),
DEBUG_WORD(EXIT, W_CLI|W_SRV, "Debug exit events (levels 1-3)"),
DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-2)"),
DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-3)"),
DEBUG_WORD(FLIST, W_SND|W_REC, "Debug file-list operations (levels 1-4)"),
DEBUG_WORD(FUZZY, W_REC, "Debug fuzzy scoring (levels 1-2)"),
DEBUG_WORD(GENR, W_REC, "Debug generator functions"),
@@ -470,7 +484,7 @@ static void parse_output_words(struct output_struct *words, short *levels, const
static void output_item_help(struct output_struct *words)
{
short *levels = words == info_words ? info_levels : debug_levels;
const char **verbosity = words == info_words ? info_verbosity : debug_verbosity;
const char *const*verbosity = words == info_words ? info_verbosity : debug_verbosity;
char buf[128], *opt, *fmt = "%-10s %s\n";
int j;
@@ -752,6 +766,8 @@ static struct poptOption long_options[] = {
{"skip-compress", 0, POPT_ARG_STRING, &skip_compress, 0, 0, 0 },
{"compress-level", 0, POPT_ARG_INT, &do_compression_level, 0, 0, 0 },
{"zl", 0, POPT_ARG_INT, &do_compression_level, 0, 0, 0 },
{"compress-threads", 0, POPT_ARG_INT, &do_compression_threads, 0, 0, 0 },
{"zt", 0, POPT_ARG_INT, &do_compression_threads, 0, 0, 0 },
{0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
{"progress", 0, POPT_ARG_VAL, &do_progress, 1, 0, 0 },
{"no-progress", 0, POPT_ARG_VAL, &do_progress, 0, 0, 0 },
@@ -785,9 +801,12 @@ static struct poptOption long_options[] = {
{"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 0, 0, 0},
{"old-args", 0, POPT_ARG_NONE, 0, OPT_OLD_ARGS, 0, 0},
{"no-old-args", 0, POPT_ARG_VAL, &old_style_args, 0, 0, 0},
{"protect-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
{"secluded-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0},
{"no-secluded-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
{"protect-args", 0, POPT_ARG_VAL, &protect_args, 1, 0, 0},
{"no-protect-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
{"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
{"trust-sender", 0, POPT_ARG_VAL, &trust_sender, 1, 0, 0},
{"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 },
{"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 },
{"usermap", 0, POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 },
@@ -837,7 +856,7 @@ static struct poptOption long_options[] = {
{0,0,0,0, 0, 0, 0}
};
static struct poptOption long_daemon_options[] = {
static const struct poptOption long_daemon_options[] = {
/* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
{"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
{"bwlimit", 0, POPT_ARG_INT, &daemon_bwlimit, 0, 0, 0 },
@@ -946,7 +965,7 @@ static void set_refuse_options(void)
if (!am_daemon
|| op->shortName == 'e' /* Required for compatibility flags */
|| op->shortName == '0' /* --from0 just modifies --files-from, so refuse that instead (or not) */
|| op->shortName == 's' /* --protect-args is always OK */
|| op->shortName == 's' /* --secluded-args is always OK */
|| op->shortName == 'n' /* --dry-run is always OK */
|| strcmp("iconv", longName) == 0
|| strcmp("no-iconv", longName) == 0
@@ -1149,7 +1168,7 @@ static time_t parse_time(const char *arg)
{
const char *cp;
time_t val, now = time(NULL);
struct tm t, *today = localtime(&now);
struct tm t, tmp, *today = localtime_r(&now, &tmp);
int in_date, old_mday, n;
memset(&t, 0, sizeof t);
@@ -1341,7 +1360,7 @@ char *alt_dest_opt(int type)
**/
int parse_arguments(int *argc_p, const char ***argv_p)
{
static poptContext pc;
poptContext pc;
const char *arg, **argv = *argv_p;
int argc = *argc_p;
int opt, want_dest_type;
@@ -1361,11 +1380,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
/* TODO: Call poptReadDefaultConfig; handle errors. */
/* The context leaks in case of an error, but if there's a
* problem we always exit anyhow. */
if (pc)
poptFreeContext(pc);
pc = poptGetContext(RSYNC_NAME, argc, argv, long_options, 0);
if (pc == NULL) {
strlcpy(err_buf, "poptGetContext returned NULL\n", sizeof err_buf);
return 0;
}
if (!am_server) {
poptReadDefaultConfig(pc, 0);
popt_unalias(pc, "--daemon");
@@ -1407,7 +1426,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
strlcpy(err_buf,
"Attempt to hack rsync thwarted!\n",
sizeof err_buf);
return 0;
goto cleanup;
}
#ifdef ICONV_OPTION
iconv_opt = NULL;
@@ -1453,7 +1472,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
snprintf(err_buf, sizeof err_buf,
"the --temp-dir path is WAY too long.\n");
return 0;
goto cleanup;
}
if (!daemon_opt) {
@@ -1463,8 +1482,16 @@ int parse_arguments(int *argc_p, const char ***argv_p)
exit_cleanup(RERR_SYNTAX);
}
*argv_p = argv = poptGetArgs(pc);
*argc_p = argc = count_args(argv);
argv = poptGetArgs(pc);
argc = count_args(argv);
if (!argc) {
*argv_p = empty_argv;
*argc_p = 0;
} else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
out_of_memory("parse_arguments");
argv = *argv_p;
poptFreeContext(pc);
am_starting_up = 0;
daemon_opt = 0;
am_daemon = 1;
@@ -1519,7 +1546,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case 'a':
if (refused_archive_part) {
create_refuse_error(refused_archive_part);
return 0;
goto cleanup;
}
if (!recurse) /* preserve recurse == 2 */
recurse = 1;
@@ -1589,7 +1616,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case 'P':
if (refused_partial || refused_progress) {
create_refuse_error(refused_partial ? refused_partial : refused_progress);
return 0;
goto cleanup;
}
do_progress = 1;
keep_partial = 1;
@@ -1624,7 +1651,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (*arg != '-') {
snprintf(err_buf, sizeof err_buf,
"Remote option must start with a dash: %s\n", arg);
return 0;
goto cleanup;
}
if (remote_option_cnt+2 >= remote_option_alloc) {
remote_option_alloc += 16;
@@ -1666,27 +1693,27 @@ int parse_arguments(int *argc_p, const char ***argv_p)
ssize_t size;
arg = poptGetOptArg(pc);
if ((size = parse_size_arg(arg, 'b', "block-size", 0, max_blength, False)) < 0)
return 0;
goto cleanup;
block_size = (int32)size;
break;
}
case OPT_MAX_SIZE:
if ((max_size = parse_size_arg(max_size_arg, 'b', "max-size", 0, -1, False)) < 0)
return 0;
goto cleanup;
max_size_arg = strdup(do_big_num(max_size, 0, NULL));
break;
case OPT_MIN_SIZE:
if ((min_size = parse_size_arg(min_size_arg, 'b', "min-size", 0, -1, False)) < 0)
return 0;
goto cleanup;
min_size_arg = strdup(do_big_num(min_size, 0, NULL));
break;
case OPT_BWLIMIT: {
ssize_t size = parse_size_arg(bwlimit_arg, 'K', "bwlimit", 512, -1, True);
if (size < 0)
return 0;
goto cleanup;
bwlimit_arg = strdup(do_big_num(size, 0, NULL));
bwlimit = (size + 512) / 1024;
break;
@@ -1715,7 +1742,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"ERROR: the %s option conflicts with the %s option\n",
alt_dest_opt(want_dest_type), alt_dest_opt(0));
return 0;
goto cleanup;
}
alt_dest_type = want_dest_type;
@@ -1723,7 +1750,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"ERROR: at most %d %s args may be specified\n",
MAX_BASIS_DIRS, alt_dest_opt(0));
return 0;
goto cleanup;
}
/* We defer sanitizing this arg until we know what
* our destination directory is going to be. */
@@ -1736,7 +1763,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"Invalid argument passed to --chmod (%s)\n",
arg);
return 0;
goto cleanup;
}
break;
@@ -1755,11 +1782,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (usermap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--usermap conflicts with prior --chown.\n");
return 0;
goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify --usermap once.\n");
return 0;
goto cleanup;
}
usermap = (char *)poptGetOptArg(pc);
usermap_via_chown = False;
@@ -1771,11 +1798,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (groupmap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--groupmap conflicts with prior --chown.\n");
return 0;
goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify --groupmap once.\n");
return 0;
goto cleanup;
}
groupmap = (char *)poptGetOptArg(pc);
groupmap_via_chown = False;
@@ -1794,11 +1821,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!usermap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--chown conflicts with prior --usermap.\n");
return 0;
goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify a user-affecting --chown once.\n");
return 0;
goto cleanup;
}
if (asprintf(&usermap, "*:%.*s", len, chown) < 0)
out_of_memory("parse_arguments");
@@ -1810,11 +1837,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!groupmap_via_chown) {
snprintf(err_buf, sizeof err_buf,
"--chown conflicts with prior --groupmap.\n");
return 0;
goto cleanup;
}
snprintf(err_buf, sizeof err_buf,
"You can only specify a group-affecting --chown once.\n");
return 0;
goto cleanup;
}
if (asprintf(&groupmap, "*:%s", arg) < 0)
out_of_memory("parse_arguments");
@@ -1842,7 +1869,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf,sizeof(err_buf),
"ACLs are not supported on this %s\n",
am_server ? "server" : "client");
return 0;
goto cleanup;
#endif
case 'X':
@@ -1853,7 +1880,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf,sizeof(err_buf),
"extended attributes are not supported on this %s\n",
am_server ? "server" : "client");
return 0;
goto cleanup;
#endif
case OPT_STOP_AFTER: {
@@ -1862,7 +1889,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
stop_at_utime = time(NULL);
if ((val = atol(arg) * 60) <= 0 || LONG_MAX - val < stop_at_utime || (long)(time_t)val != val) {
snprintf(err_buf, sizeof err_buf, "invalid --stop-after value: %s\n", arg);
return 0;
goto cleanup;
}
stop_at_utime += val;
break;
@@ -1873,11 +1900,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
arg = poptGetOptArg(pc);
if ((stop_at_utime = parse_time(arg)) == (time_t)-1) {
snprintf(err_buf, sizeof err_buf, "invalid --stop-at format: %s\n", arg);
return 0;
goto cleanup;
}
if (stop_at_utime <= time(NULL)) {
snprintf(err_buf, sizeof err_buf, "--stop-at time is not in the future: %s\n", arg);
return 0;
goto cleanup;
}
break;
#endif
@@ -1895,7 +1922,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
else {
snprintf(err_buf, sizeof err_buf,
"--stderr mode \"%s\" is not one of errors, all, or client\n", arg);
return 0;
goto cleanup;
}
saw_stderr_opt = 1;
break;
@@ -1906,13 +1933,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
* turned this option off. */
if (opt >= OPT_REFUSED_BASE) {
create_refuse_error(opt);
return 0;
goto cleanup;
}
snprintf(err_buf, sizeof err_buf, "%s%s: %s\n",
am_server ? "on remote machine: " : "",
poptBadOption(pc, POPT_BADOPTION_NOALIAS),
poptStrerror(opt));
return 0;
goto cleanup;
}
}
@@ -1920,7 +1947,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
saw_stderr_opt = 1;
if (version_opt_cnt) {
print_rsync_version(FINFO);
print_rsync_version(version_opt_cnt > 1 && !am_server ? FNONE : FINFO);
exit_cleanup(0);
}
@@ -1932,9 +1959,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (max_alloc_arg) {
ssize_t size = parse_size_arg(max_alloc_arg, 'B', "max-alloc", 1024*1024, -1, True);
if (size < 0)
return 0;
goto cleanup;
max_alloc = size;
}
if (!max_alloc)
max_alloc = SIZE_MAX;
if (old_style_args < 0) {
if (!am_server && protect_args <= 0 && (arg = getenv("RSYNC_OLD_ARGS")) != NULL && *arg) {
@@ -1945,8 +1974,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
} else if (old_style_args) {
if (protect_args > 0) {
snprintf(err_buf, sizeof err_buf,
"--protect-args conflicts with --old-args.\n");
return 0;
"--secluded-args conflicts with --old-args.\n");
goto cleanup;
}
protect_args = 0;
}
@@ -1957,7 +1986,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
else if ((arg = getenv("RSYNC_PROTECT_ARGS")) != NULL && *arg)
protect_args = atoi(arg) ? 1 : 0;
else {
#ifdef RSYNC_USE_PROTECTED_ARGS
#ifdef RSYNC_USE_SECLUDED_ARGS
protect_args = 1;
#else
protect_args = 0;
@@ -1991,8 +2020,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
do_compression = CPRES_AUTO;
if (do_compression && refused_compress) {
create_refuse_error(refused_compress);
return 0;
goto cleanup;
}
if (do_compression_threads < 0)
do_compression_threads = 0;
}
#ifdef HAVE_SETVBUF
@@ -2016,7 +2047,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
default:
snprintf(err_buf, sizeof err_buf,
"Invalid --outbuf setting -- specify N, L, or B.\n");
return 0;
goto cleanup;
}
setvbuf(stdout, (char *)NULL, mode, 0);
}
@@ -2044,7 +2075,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
}
if (refused_no_iconv && !iconv_opt) {
create_refuse_error(refused_no_iconv);
return 0;
goto cleanup;
}
#endif
@@ -2055,18 +2086,30 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (orig_protect_args == 2 && am_server)
protect_args = orig_protect_args;
if (protect_args == 1 && am_server)
if (protect_args == 1 && am_server) {
poptFreeContext(pc);
return 1;
}
*argv_p = argv = poptGetArgs(pc);
*argc_p = argc = count_args(argv);
/* Because popt 1.19 has started to free the returned args data, we now
* make a copy of the array and then do an immediate cleanup. */
argv = poptGetArgs(pc);
argc = count_args(argv);
if (!argc) {
*argv_p = empty_argv;
*argc_p = 0;
} else if (poptDupArgv(argc, argv, argc_p, argv_p) != 0)
out_of_memory("parse_arguments");
argv = *argv_p;
poptFreeContext(pc);
pc = NULL;
#ifndef SUPPORT_LINKS
if (preserve_links && !am_sender) {
snprintf(err_buf, sizeof err_buf,
"symlinks are not supported on this %s\n",
am_server ? "server" : "client");
return 0;
goto cleanup;
}
#endif
@@ -2075,7 +2118,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"hard links are not supported on this %s\n",
am_server ? "server" : "client");
return 0;
goto cleanup;
}
#endif
@@ -2083,20 +2126,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (am_root < 0 && preserve_xattrs > 1) {
snprintf(err_buf, sizeof err_buf,
"--fake-super conflicts with -XX\n");
return 0;
goto cleanup;
}
#else
if (am_root < 0) {
snprintf(err_buf, sizeof err_buf,
"--fake-super requires an rsync with extended attributes enabled\n");
return 0;
goto cleanup;
}
#endif
if (write_batch && read_batch) {
snprintf(err_buf, sizeof err_buf,
"--write-batch and --read-batch can not be used together\n");
return 0;
goto cleanup;
}
if (write_batch > 0 || read_batch) {
if (am_server) {
@@ -2115,25 +2158,25 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (read_batch && files_from) {
snprintf(err_buf, sizeof err_buf,
"--read-batch cannot be used with --files-from\n");
return 0;
goto cleanup;
}
if (read_batch && remove_source_files) {
snprintf(err_buf, sizeof err_buf,
"--read-batch cannot be used with --remove-%s-files\n",
remove_source_files == 1 ? "source" : "sent");
return 0;
goto cleanup;
}
if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) {
snprintf(err_buf, sizeof err_buf,
"the batch-file name must be %d characters or less.\n",
MAX_BATCH_NAME_LEN);
return 0;
goto cleanup;
}
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
snprintf(err_buf, sizeof err_buf,
"the --temp-dir path is WAY too long.\n");
return 0;
goto cleanup;
}
if (max_delete < 0 && max_delete != INT_MIN) {
@@ -2167,7 +2210,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (delete_before + !!delete_during + delete_after > 1) {
snprintf(err_buf, sizeof err_buf,
"You may not combine multiple --delete-WHEN options.\n");
return 0;
goto cleanup;
}
if (delete_before || delete_during || delete_after)
delete_mode = 1;
@@ -2178,7 +2221,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
delete_during = 1;
else {
create_refuse_error(refused_delete_before);
return 0;
goto cleanup;
}
} else if (refused_delete_during)
delete_before = 1;
@@ -2187,14 +2230,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (!xfer_dirs && delete_mode) {
snprintf(err_buf, sizeof err_buf,
"--delete does not work without --recursive (-r) or --dirs (-d).\n");
return 0;
goto cleanup;
}
if (missing_args == 3) /* simplify if both options were specified */
missing_args = 2;
if (refused_delete && (delete_mode || missing_args == 2)) {
create_refuse_error(refused_delete);
return 0;
goto cleanup;
}
if (remove_source_files) {
@@ -2203,7 +2246,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
* options. */
if (refused_delete && am_sender) {
create_refuse_error(refused_delete);
return 0;
goto cleanup;
}
need_messages_from_generator = 1;
}
@@ -2257,7 +2300,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"--suffix cannot contain slashes: %s\n",
backup_suffix);
return 0;
goto cleanup;
}
if (backup_dir) {
size_t len;
@@ -2270,7 +2313,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (len > sizeof backup_dir_buf - 128) {
snprintf(err_buf, sizeof err_buf,
"the --backup-dir path is WAY too long.\n");
return 0;
goto cleanup;
}
backup_dir_len = (int)len;
if (!backup_dir_len) {
@@ -2289,7 +2332,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--suffix cannot be empty %s\n", backup_dir_len < 0
? "when --backup-dir is the same as the dest dir"
: "without a --backup-dir");
return 0;
goto cleanup;
} else if (make_backups && delete_mode && !delete_excluded && !am_server) {
snprintf(backup_dir_buf, sizeof backup_dir_buf,
"P *%s", backup_suffix);
@@ -2317,7 +2360,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (do_progress && !am_server) {
if (!log_before_transfer && INFO_EQ(NAME, 0))
parse_output_words(info_words, info_levels, "name", DEFAULT_PRIORITY);
parse_output_words(info_words, info_levels, "flist2,progress", DEFAULT_PRIORITY);
parse_output_words(info_words, info_levels, "FLIST2,PROGRESS", DEFAULT_PRIORITY);
}
if (dry_run)
@@ -2358,11 +2401,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (whole_file > 0) {
snprintf(err_buf, sizeof err_buf,
"--append cannot be used with --whole-file\n");
return 0;
goto cleanup;
}
if (refused_inplace) {
create_refuse_error(refused_inplace);
return 0;
goto cleanup;
}
inplace = 1;
}
@@ -2370,7 +2413,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (write_devices) {
if (refused_inplace) {
create_refuse_error(refused_inplace);
return 0;
goto cleanup;
}
inplace = 1;
}
@@ -2385,13 +2428,13 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--%s cannot be used with --%s\n",
append_mode ? "append" : "inplace",
delay_updates ? "delay-updates" : "partial-dir");
return 0;
goto cleanup;
}
/* --inplace implies --partial for refusal purposes, but we
* clear the keep_partial flag for internal logic purposes. */
if (refused_partial) {
create_refuse_error(refused_partial);
return 0;
goto cleanup;
}
keep_partial = 0;
#else
@@ -2399,7 +2442,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"--%s is not supported on this %s\n",
append_mode ? "append" : "inplace",
am_server ? "server" : "client");
return 0;
goto cleanup;
#endif
} else {
if (keep_partial && !partial_dir && !am_server) {
@@ -2413,7 +2456,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
partial_dir = NULL;
if (!partial_dir && refused_partial) {
create_refuse_error(refused_partial);
return 0;
goto cleanup;
}
keep_partial = 1;
}
@@ -2434,14 +2477,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (am_server) {
snprintf(err_buf, sizeof err_buf,
"The --files-from sent to the server cannot specify a host.\n");
return 0;
goto cleanup;
}
files_from = p;
filesfrom_host = h;
if (strcmp(files_from, "-") == 0) {
snprintf(err_buf, sizeof err_buf,
"Invalid --files-from remote filename\n");
return 0;
goto cleanup;
}
} else {
if (sanitize_paths)
@@ -2460,11 +2503,16 @@ int parse_arguments(int *argc_p, const char ***argv_p)
snprintf(err_buf, sizeof err_buf,
"failed to open files-from file %s: %s\n",
files_from, strerror(errno));
return 0;
goto cleanup;
}
}
}
if (trust_sender || am_server || read_batch)
trust_sender_args = trust_sender_filter = 1;
else if (old_style_args || filesfrom_host != NULL)
trust_sender_args = 1;
am_starting_up = 0;
return 1;
@@ -2472,6 +2520,9 @@ int parse_arguments(int *argc_p, const char ***argv_p)
options_rejected:
snprintf(err_buf, sizeof err_buf,
"Your options have been rejected by the server.\n");
cleanup:
if (pc)
poptFreeContext(pc);
return 0;
}
@@ -2487,17 +2538,24 @@ static char SPLIT_ARG_WHEN_OLD[1];
**/
char *safe_arg(const char *opt, const char *arg)
{
#define SHELL_CHARS "!#$&;|<>(){}\"' \t\\"
#define SHELL_CHARS "!#$&;|<>(){}\"'` \t\\"
#define WILD_CHARS "*?[]" /* We don't allow remote brace expansion */
BOOL is_filename_arg = !opt;
char *escapes = is_filename_arg ? SHELL_CHARS : WILD_CHARS SHELL_CHARS;
BOOL escape_leading_dash = is_filename_arg && *arg == '-';
BOOL escape_leading_tilde = 0;
int len1 = opt && *opt ? strlen(opt) + 1 : 0;
int len2 = strlen(arg);
int extras = escape_leading_dash ? 2 : 0;
char *ret;
if (!protect_args && old_style_args < 2 && (!old_style_args || (!is_filename_arg && opt != SPLIT_ARG_WHEN_OLD))) {
const char *f;
if (*arg == '~' && is_filename_arg && !am_sender && !trust_sender_args
&& ((relative_paths && !strstr(arg, "/./"))
|| !strchr(arg, '/'))) {
extras++;
escape_leading_tilde = 1;
}
for (f = arg; *f; f++) {
if (strchr(escapes, *f))
extras++;
@@ -2520,8 +2578,13 @@ char *safe_arg(const char *opt, const char *arg)
else {
const char *f = arg;
char *t = ret + len1;
if (escape_leading_tilde)
*t++ = '\\';
while (*f) {
if (strchr(escapes, *f))
if (*f == '\\') {
if (!is_filename_arg || !strchr(WILD_CHARS, f[1]))
*t++ = '\\';
} else if (strchr(escapes, *f))
*t++ = '\\';
*t++ = *f++;
}

View File

@@ -1,4 +1,4 @@
TARGETS := all install install-ssl-daemon install-all install-strip conf gen gensend reconfigure restatus \
TARGETS := all install install-ssl-daemon install-all install-strip conf gen reconfigure restatus \
proto man clean cleantests distclean test check check29 check30 installcheck splint \
doxygen doxygen-upload finddead rrsync

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

View File

@@ -27,6 +27,7 @@ long_opts = { # These include some extra long-args that BackupPC uses:
'recursive': 0,
'stderr': 1,
'times': 0,
'copy-devices': -1,
'write-devices': -1,
}

View File

@@ -1,6 +1,6 @@
Summary: A fast, versatile, remote (and local) file-copying tool
Name: rsync
Version: 3.2.4
Version: 3.4.3
%define fullversion %{version}
Release: 1
%define srcdir src
@@ -79,9 +79,5 @@ rm -rf $RPM_BUILD_ROOT
%dir /etc/rsync-ssl/certs
%changelog
* Fri Apr 15 2022 Wayne Davison <wayne@opencoder.net>
Released 3.2.4.
* 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

@@ -0,0 +1,18 @@
# This config file can be used with rsync to enable legacy digests
# (such as MD4) by using the OPENSSL_CONF environment variable.
# See rsync's configure --with-openssl-conf=/path/name option.
openssl_conf = openssl_init
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1

View File

@@ -1,244 +0,0 @@
#!/usr/bin/env -S python3 -B
# This script is used to turn one or more of the "patch/BASE/*" branches
# into one or more diffs in the "patches" directory. Pass the option
# --gen if you want generated files in the diffs. Pass the name of
# one or more diffs if you want to just update a subset of all the
# diffs.
import os, sys, re, argparse, time, shutil
sys.path = ['packaging'] + sys.path
from pkglib import *
MAKE_GEN_CMDS = [
'./prepare-source'.split(),
'cd build && if test -f config.status ; then ./config.status ; else ../configure ; fi',
'make -C build gen'.split(),
]
TMP_DIR = "patches.gen"
os.environ['GIT_MERGE_AUTOEDIT'] = 'no'
def main():
global master_commit, parent_patch, description, completed, last_touch
if not os.path.isdir(args.patches_dir):
die(f'No "{args.patches_dir}" directory was found.')
if not os.path.isdir('.git'):
die('No ".git" directory present in the current dir.')
starting_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir)
master_commit = latest_git_hash(args.base_branch)
if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
die('You must setup an auto-build-save dir to use this script.')
if args.gen:
if os.path.lexists(TMP_DIR):
die(f'"{TMP_DIR}" must not exist in the current directory.')
gen_files = get_gen_files()
os.mkdir(TMP_DIR, 0o700)
for cmd in MAKE_GEN_CMDS:
cmd_chk(cmd)
cmd_chk(['rsync', '-a', *gen_files, f'{TMP_DIR}/master/'])
last_touch = int(time.time())
# Start by finding all patches so that we can load all possible parents.
patches = sorted(list(get_patch_branches(args.base_branch)))
parent_patch = { }
description = { }
for patch in patches:
branch = f"patch/{args.base_branch}/{patch}"
desc = ''
proc = cmd_pipe(['git', 'diff', '-U1000', f"{args.base_branch}...{branch}", '--', f"PATCH.{patch}"])
in_diff = False
for line in proc.stdout:
if in_diff:
if not re.match(r'^[ +]', line):
continue
line = line[1:]
m = re.search(r'patch -p1 <patches/(\S+)\.diff', line)
if m and m[1] != patch:
parpat = parent_patch[patch] = m[1]
if not parpat in patches:
die(f"Parent of {patch} is not a local branch: {parpat}")
desc += line
elif re.match(r'^@@ ', line):
in_diff = True
description[patch] = desc
proc.communicate()
if args.patch_files: # Limit the list of patches to actually process
valid_patches = patches
patches = [ ]
for fn in args.patch_files:
name = re.sub(r'\.diff$', '', re.sub(r'.+/', '', fn))
if name not in valid_patches:
die(f"Local branch not available for patch: {name}")
patches.append(name)
completed = set()
for patch in patches:
if patch in completed:
continue
if not update_patch(patch):
break
if args.gen:
shutil.rmtree(TMP_DIR)
while last_touch >= int(time.time()):
time.sleep(1)
cmd_chk(['git', 'checkout', starting_branch])
cmd_chk(['packaging/prep-auto-dir'], discard='output')
def update_patch(patch):
global last_touch
completed.add(patch) # Mark it as completed early to short-circuit any (bogus) dependency loops.
parent = parent_patch.get(patch, None)
if parent:
if parent not in completed:
if not update_patch(parent):
return 0
based_on = parent = f"patch/{args.base_branch}/{parent}"
else:
parent = args.base_branch
based_on = master_commit
print(f"======== {patch} ========")
while args.gen and last_touch >= int(time.time()):
time.sleep(1)
branch = f"patch/{args.base_branch}/{patch}"
s = cmd_run(['git', 'checkout', branch])
if s.returncode != 0:
return 0
s = cmd_run(['git', 'merge', based_on])
ok = s.returncode == 0
skip_shell = False
if not ok or args.cmd or args.make or args.shell:
cmd_chk(['packaging/prep-auto-dir'], discard='output')
if not ok:
print(f'"git merge {based_on}" incomplete -- please fix.')
if not run_a_shell(parent, patch):
return 0
if not args.make and not args.cmd:
skip_shell = True
if args.make:
if cmd_run(['packaging/smart-make']).returncode != 0:
if not run_a_shell(parent, patch):
return 0
if not args.cmd:
skip_shell = True
if args.cmd:
if cmd_run(args.cmd).returncode != 0:
if not run_a_shell(parent, patch):
return 0
skip_shell = True
if args.shell and not skip_shell:
if not run_a_shell(parent, patch):
return 0
with open(f"{args.patches_dir}/{patch}.diff", 'w', encoding='utf-8') as fh:
fh.write(description[patch])
fh.write(f"\nbased-on: {based_on}\n")
if args.gen:
gen_files = get_gen_files()
for cmd in MAKE_GEN_CMDS:
cmd_chk(cmd)
cmd_chk(['rsync', '-a', *gen_files, f"{TMP_DIR}/{patch}/"])
else:
gen_files = [ ]
last_touch = int(time.time())
proc = cmd_pipe(['git', 'diff', based_on])
skipping = False
for line in proc.stdout:
if skipping:
if not re.match(r'^diff --git a/', line):
continue
skipping = False
elif re.match(r'^diff --git a/PATCH', line):
skipping = True
continue
if not re.match(r'^index ', line):
fh.write(line)
proc.communicate()
if args.gen:
e_tmp_dir = re.escape(TMP_DIR)
diff_re = re.compile(r'^(diff -Nurp) %s/[^/]+/(.*?) %s/[^/]+/(.*)' % (e_tmp_dir, e_tmp_dir))
minus_re = re.compile(r'^\-\-\- %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
plus_re = re.compile(r'^\+\+\+ %s/[^/]+/([^\t]+)\t.*' % e_tmp_dir)
if parent == args.base_branch:
parent_dir = 'master'
else:
m = re.search(r'([^/]+)$', parent)
parent_dir = m[1]
proc = cmd_pipe(['diff', '-Nurp', f"{TMP_DIR}/{parent_dir}", f"{TMP_DIR}/{patch}"])
for line in proc.stdout:
line = diff_re.sub(r'\1 a/\2 b/\3', line)
line = minus_re.sub(r'--- a/\1', line)
line = plus_re.sub(r'+++ b/\1', line)
fh.write(line)
proc.communicate()
return 1
def run_a_shell(parent, patch):
m = re.search(r'([^/]+)$', parent)
parent_dir = m[1]
os.environ['PS1'] = f"[{parent_dir}] {patch}: "
while True:
s = cmd_run([os.environ.get('SHELL', '/bin/sh')])
if s.returncode != 0:
ans = input("Abort? [n/y] ")
if re.match(r'^y', ans, flags=re.I):
return False
continue
cur_branch, is_clean, status_txt = check_git_status(0)
if is_clean:
break
print(status_txt, end='')
cmd_run('rm -f build/*.o build/*/*.o')
return True
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Turn a git branch back into a diff files in the patches dir.", 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('--skip-check', action='store_true', help="Skip the check that ensures starting with a clean branch.")
parser.add_argument('--make', '-m', action='store_true', help="Run the smart-make script in every patch branch.")
parser.add_argument('--cmd', '-c', help="Run a command in every patch branch.")
parser.add_argument('--shell', '-s', action='store_true', help="Launch a shell for every patch/BASE/* branch updated, not just when a conflict occurs.")
parser.add_argument('--gen', metavar='DIR', nargs='?', const='', help='Include generated files. Optional DIR value overrides the default of using the "patches" dir.')
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()
if args.gen == '':
args.gen = args.patches_dir
elif args.gen is not None:
args.patches_dir = args.gen
main()
# vim: sw=4 et ft=python

View File

@@ -32,7 +32,7 @@ def _tweak_opts(cmd, opts, **maybe_set_args):
opts = opts.copy()
_maybe_set(opts, **maybe_set_args)
if type(cmd) == str:
if isinstance(cmd, str):
_maybe_set(opts, shell=True)
want_raw = opts.pop('raw', False)
@@ -170,17 +170,6 @@ def get_patch_branches(base_branch):
return branches
def mandate_gensend_hook():
hook = '.git/hooks/pre-push'
if not os.path.exists(hook):
print('Creating hook file:', hook)
cmd_chk(['./rsync', '-a', 'packaging/pre-push', hook])
else:
ct = cmd_txt(['fgrep', 'make gensend', hook], discard='output')
if ct.rc:
die('Please add a "make gensend" into your', hook, 'script.')
# Snag the GENFILES values out of the Makefile file and return them as a list.
def get_gen_files(want_dir_plus_list=False):
cont_re = re.compile(r'\\\n')

View File

@@ -1,16 +0,0 @@
#!/bin/bash -e
cat >/dev/null # Just discard stdin data
if [[ -f /proc/$PPID/cmdline ]]; then
while read -d $'\0' arg ; do
if [[ "$arg" == '--tags' ]] ; then
exit 0
fi
done </proc/$PPID/cmdline
fi
branch=`git rev-parse --abbrev-ref HEAD`
if [[ "$branch" = master && "$*" == *github* ]]; then
make gensend
fi

View File

@@ -1,395 +0,0 @@
#!/usr/bin/env -S python3 -B
# This script expects the directory ~/samba-rsync-ftp to exist and to be a
# copy of the /home/ftp/pub/rsync dir on samba.org. When the script is done,
# the git repository in the current directory will be updated, and the local
# ~/samba-rsync-ftp dir will be ready to be rsynced to samba.org.
import os, sys, re, argparse, glob, shutil, signal
from datetime import datetime
from getpass import getpass
sys.path = ['packaging'] + sys.path
from pkglib import *
os.environ['LESS'] = 'mqeiXR'; # Make sure that -F is turned off and -R is turned on.
dest = os.environ['HOME'] + '/samba-rsync-ftp'
ORIGINAL_PATH = os.environ['PATH']
def main():
if not os.path.isfile('packaging/release-rsync'):
die('You must run this script from the top of your rsync checkout.')
now = datetime.now()
cl_today = now.strftime('* %a %b %d %Y')
year = now.strftime('%Y')
ztoday = now.strftime('%d %b %Y')
today = ztoday.lstrip('0')
mandate_gensend_hook()
curdir = os.getcwd()
signal.signal(signal.SIGINT, signal_handler)
if cmd_txt_chk(['packaging/prep-auto-dir']).out == '':
die('You must setup an auto-build-save dir to use this script.');
auto_dir, gen_files = get_gen_files(True)
gen_pathnames = [ os.path.join(auto_dir, fn) for fn in gen_files ]
dash_line = '=' * 74
print(f"""\
{dash_line}
== This will release a new version of rsync onto an unsuspecting world. ==
{dash_line}
""")
with open('build/rsync.1') as fh:
for line in fh:
if line.startswith(r'.\" prefix='):
doc_prefix = line.split('=')[1].strip()
if doc_prefix != '/usr':
warn(f"*** The documentation was built with prefix {doc_prefix} instead of /usr ***")
die("*** Read the md2man script for a way to override this. ***")
break
if line.startswith('.P'):
die("Failed to find the prefix comment at the start of the rsync.1 manpage.")
if not os.path.isdir(dest):
die(dest, "dest does not exist")
if not os.path.isdir('.git'):
die("There is no .git dir in the current directory.")
if os.path.lexists('a'):
die('"a" must not exist in the current directory.')
if os.path.lexists('b'):
die('"b" must not exist in the current directory.')
if os.path.lexists('patches.gen'):
die('"patches.gen" must not exist in the current directory.')
check_git_state(args.master_branch, True, 'patches')
curversion = get_rsync_version()
# All version values are strings!
lastversion, last_protocol_version, pdate = get_NEWS_version_info()
protocol_version, subprotocol_version = get_protocol_versions()
version = curversion
m = re.search(r'pre(\d+)', version)
if m:
version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version)
else:
version = version.replace('dev', 'pre1')
ans = input(f"Please enter the version number of this release: [{version}] ")
if ans == '.':
version = re.sub(r'pre\d+', '', version)
elif ans != '':
version = ans
if not re.match(r'^[\d.]+(pre\d+)?$', version):
die(f'Invalid version: "{version}"')
v_ver = 'v' + version
rsync_ver = 'rsync-' + version
if os.path.lexists(rsync_ver):
die(f'"{rsync_ver}" must not exist in the current directory.')
out = cmd_txt_chk(['git', 'tag', '-l', v_ver]).out
if out != '':
print(f"Tag {v_ver} already exists.")
ans = input("\nDelete tag or quit? [Q/del] ")
if not re.match(r'^del', ans, flags=re.I):
die("Aborted")
cmd_chk(['git', 'tag', '-d', v_ver])
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
if 'pre' in version and not curversion.endswith('dev'):
lastversion = curversion
ans = input(f"Enter the previous version to produce a patch against: [{lastversion}] ")
if ans != '':
lastversion = ans
lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion)
rsync_lastver = 'rsync-' + lastversion
if os.path.lexists(rsync_lastver):
die(f'"{rsync_lastver}" must not exist in the current directory.')
m = re.search(r'(pre\d+)', version)
pre = m[1] if m else ''
release = '0.1' if pre else '1'
ans = input(f"Please enter the RPM release number of this release: [{release}] ")
if ans != '':
release = ans
if pre:
release += '.' + pre
finalversion = re.sub(r'pre\d+', '', version)
proto_changed = protocol_version != last_protocol_version
if proto_changed:
if finalversion in pdate:
proto_change_date = pdate[finalversion]
else:
while True:
ans = input("On what date did the protocol change to {protocol_version} get checked in? (dd Mmm yyyy) ")
if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans):
break
proto_change_date = ans
else:
proto_change_date = ' ' * 11
if 'pre' in lastversion:
if not pre:
die("You should not diff a release version against a pre-release version.")
srcdir = srcdiffdir = lastsrcdir = 'src-previews'
skipping = ' ** SKIPPING **'
elif pre:
srcdir = srcdiffdir = 'src-previews'
lastsrcdir = 'src'
skipping = ' ** SKIPPING **'
else:
srcdir = lastsrcdir = 'src'
srcdiffdir = 'src-diffs'
skipping = ''
print(f"""
{dash_line}
version is "{version}"
lastversion is "{lastversion}"
dest is "{dest}"
curdir is "{curdir}"
srcdir is "{srcdir}"
srcdiffdir is "{srcdiffdir}"
lastsrcdir is "{lastsrcdir}"
release is "{release}"
About to:
- tweak SUBPROTOCOL_VERSION in rsync.h, if needed
- tweak the version in version.h and the spec files
- tweak NEWS.md to ensure header values are correct
- generate configure.sh, config.h.in, and proto.h
- page through the differences
""")
ans = input("<Press Enter to continue> ")
specvars = {
'Version:': finalversion,
'Release:': release,
'%define fullversion': f'%{{version}}{pre}',
'Released': version + '.',
'%define srcdir': srcdir,
}
tweak_files = 'version.h rsync.h NEWS.md'.split()
tweak_files += glob.glob('packaging/*.spec')
tweak_files += glob.glob('packaging/*/*.spec')
for fn in tweak_files:
with open(fn, 'r', encoding='utf-8') as fh:
old_txt = txt = fh.read()
if fn == 'version.h':
txt = f'#define RSYNC_VERSION "{version}"\n'
elif '.spec' in fn:
for var, val in specvars.items():
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
txt = replace_or_die(x_re, var + ' ' + val, txt, f"Unable to update {var} in {fn}")
x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
txt = replace_or_die(x_re, r'%s \1' % cl_today, txt, f"Unable to update ChangeLog header in {fn}")
elif fn == 'rsync.h':
x_re = re.compile('(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
repl = lambda m: m[1] + ' ' + ('0' if not pre or not proto_changed else '1' if m[2] == '0' else m[2])
txt = replace_or_die(x_re, repl, txt, f"Unable to find SUBPROTOCOL_VERSION define in {fn}")
elif fn == 'NEWS.md':
efv = re.escape(finalversion)
x_re = re.compile(r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
+ r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?')
rel_day = 'UNRELEASED' if pre else today
repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n'
+ '## Changes in this version:\n')
if proto_changed:
repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n'
good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1)
msg = f"The top lines of {fn} are not in the right format. It should be:\n" + good_top
txt = replace_or_die(x_re, repl, txt, msg)
x_re = re.compile(r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv, re.M)
repl = lambda m: m[1] + (m[2] if pre else ztoday) + m[3] + proto_change_date + m[4] + protocol_version + m[5]
txt = replace_or_die(x_re, repl, txt, f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}')
else:
die(f"Unrecognized file in tweak_files: {fn}")
if txt != old_txt:
print(f"Updating {fn}")
with open(fn, 'w', encoding='utf-8') as fh:
fh.write(txt)
cmd_chk(['packaging/year-tweak'])
print(dash_line)
cmd_run("git diff")
srctar_name = f"{rsync_ver}.tar.gz"
pattar_name = f"rsync-patches-{version}.tar.gz"
diff_name = f"{rsync_lastver}-{version}.diffs.gz"
srctar_file = os.path.join(dest, srcdir, srctar_name)
pattar_file = os.path.join(dest, srcdir, pattar_name)
diff_file = os.path.join(dest, srcdiffdir, diff_name)
lasttar_file = os.path.join(dest, lastsrcdir, rsync_lastver + '.tar.gz')
print(f"""\
{dash_line}
About to:
- git commit all changes
- generate the manpages
- merge the {args.master_branch} branch into the patch/{args.master_branch}/* branches
- update the files in the "patches" dir and OPTIONALLY (if you type 'y') to
run patch-update with the --make option (which opens a shell on error)
""")
ans = input("<Press Enter OR 'y' to continue> ")
s = cmd_run(['git', 'commit', '-a', '-m', f'Preparing for release of {version}'])
if s.returncode:
die('Aborting')
cmd_chk('make gen')
print(f'Creating any missing patch branches.')
s = cmd_run(f'packaging/branch-from-patch --branch={args.master_branch} --add-missing')
if s.returncode:
die('Aborting')
print('Updating files in "patches" dir ...')
s = cmd_run(f'packaging/patch-update --branch={args.master_branch}')
if s.returncode:
die('Aborting')
if re.match(r'^y', ans, re.I):
print(f'\nRunning smart-make on all "patch/{args.master_branch}/*" branches ...')
cmd_run(f"packaging/patch-update --branch={args.master_branch} --skip-check --make")
if os.path.isdir('patches/.git'):
s = cmd_run(f"cd patches && git commit -a -m 'The patches for {version}.'")
if s.returncode:
die('Aborting')
print(f"""\
{dash_line}
About to:
- create signed tag for this release: {v_ver}
- create release diffs, "{diff_name}"
- create release tar, "{srctar_name}"
- generate {rsync_ver}/patches/* files
- create patches tar, "{pattar_name}"
- update top-level README.md, NEWS.md, TODO, and ChangeLog
- update top-level rsync*.html manpages
- gpg-sign the release files
- update hard-linked top-level release files{skipping}
""")
ans = input("<Press Enter to continue> ")
# TODO: is there a better way to ensure that our passphrase is in the agent?
cmd_run("touch TeMp; gpg --sign TeMp; rm TeMp*")
out = cmd_txt(f"git tag -s -m 'Version {version}.' {v_ver}", capture='combined').out
print(out, end='')
if 'bad passphrase' in out or 'failed' in out:
die('Aborting')
if os.path.isdir('patches/.git'):
out = cmd_txt(f"cd patches && git tag -s -m 'Version {version}.' {v_ver}", capture='combined').out
print(out, end='')
if 'bad passphrase' in out or 'failed' in out:
die('Aborting')
os.environ['PATH'] = ORIGINAL_PATH
# Extract the generated files from the old tar.
tweaked_gen_files = [ os.path.join(rsync_lastver, fn) for fn in gen_files ]
cmd_run(['tar', 'xzf', lasttar_file, *tweaked_gen_files])
os.rename(rsync_lastver, 'a')
print(f"Creating {diff_file} ...")
cmd_chk(['rsync', '-a', *gen_pathnames, 'b/'])
sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # CAUTION: must not contain any single quotes!
cmd_chk(f"(git diff v{lastversion} {v_ver} -- ':!.github'; diff -upN a b | sed -r '{sed_script}') | gzip -9 >{diff_file}")
shutil.rmtree('a')
os.rename('b', rsync_ver)
print(f"Creating {srctar_file} ...")
cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | tar xf -")
cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver}/")
cmd_chk(['fakeroot', 'tar', 'czf', srctar_file, '--exclude=.github', rsync_ver])
shutil.rmtree(rsync_ver)
print(f'Updating files in "{rsync_ver}/patches" dir ...')
os.mkdir(rsync_ver, 0o755)
os.mkdir(f"{rsync_ver}/patches", 0o755)
cmd_chk(f"packaging/patch-update --skip-check --branch={args.master_branch} --gen={rsync_ver}/patches".split())
print(f"Creating {pattar_file} ...")
cmd_chk(['fakeroot', 'tar', 'chzf', pattar_file, rsync_ver + '/patches'])
shutil.rmtree(rsync_ver)
print(f"Updating the other files in {dest} ...")
md_files = 'README.md NEWS.md INSTALL.md'.split()
html_files = [ fn for fn in gen_pathnames if fn.endswith('.html') ]
cmd_chk(['rsync', '-a', *md_files, *html_files, dest])
cmd_chk(["./md-convert", "--dest", dest, *md_files])
cmd_chk(f"git log --name-status | gzip -9 >{dest}/ChangeLog.gz")
for fn in (srctar_file, pattar_file, diff_file):
asc_fn = fn + '.asc'
if os.path.lexists(asc_fn):
os.unlink(asc_fn)
res = cmd_run(['gpg', '--batch', '-ba', fn])
if res.returncode != 0 and res.returncode != 2:
die("gpg signing failed")
if not pre:
for find in f'{dest}/rsync-*.gz {dest}/rsync-*.asc {dest}/src-previews/rsync-*diffs.gz*'.split():
for fn in glob.glob(find):
os.unlink(fn)
top_link = [
srctar_file, f"{srctar_file}.asc",
pattar_file, f"{pattar_file}.asc",
diff_file, f"{diff_file}.asc",
]
for fn in top_link:
os.link(fn, re.sub(r'/src(-\w+)?/', '/', fn))
print(f"""\
{dash_line}
Local changes are done. When you're satisfied, push the git repository
and rsync the release files. Remember to announce the release on *BOTH*
rsync-announce@lists.samba.org and rsync@lists.samba.org (and the web)!
""")
def replace_or_die(regex, repl, txt, die_msg):
m = regex.search(txt)
if not m:
die(die_msg)
return regex.sub(repl, txt, 1)
def signal_handler(sig, frame):
die("\nAborting due to SIGINT.")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Prepare a new release of rsync in the git repo & ftp dir.", add_help=False)
parser.add_argument('--branch', '-b', dest='master_branch', default='master', help="The branch to release. Default: master.")
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

703
packaging/release.py Executable file
View File

@@ -0,0 +1,703 @@
#!/usr/bin/env python3
# Step-based release script for rsync. Each step is a separate invocation
# selected by a --step-N-XX option, so the maintainer drives the release
# manually one piece at a time.
#
# All persistent state and working files live in ../release/ (a sibling of
# the rsync git checkout):
#
# ../release/rsync-ftp/ mirror of samba.org:/home/ftp/pub/rsync
# ../release/rsync-html/ git checkout of rsync-web (the html site)
# ../release/work/ scratch space for tarball / diff staging
# ../release/release-state.json info shared between steps
#
# The rsync-patches archive is no longer maintained and has been dropped.
#
# Run "packaging/release.py --list" to see the step list.
import os, sys, re, argparse, glob, shutil, json, signal, subprocess
from datetime import datetime
sys.path = ['packaging'] + sys.path
from pkglib import (
warn, die, cmd_run, cmd_chk, cmd_txt, cmd_txt_chk, cmd_pipe,
check_git_state, get_rsync_version,
get_NEWS_version_info, get_protocol_versions,
)
# ---------- Paths ----------
RELEASE_DIR = os.path.realpath('../release')
FTP_DIR = os.path.join(RELEASE_DIR, 'rsync-ftp')
HTML_DIR = os.path.join(RELEASE_DIR, 'rsync-html')
WORK_DIR = os.path.join(RELEASE_DIR, 'work')
STATE_FILE = os.path.join(RELEASE_DIR, 'release-state.json')
# Local rsync-web checkout (sibling of rsync-git) is the source-of-truth for
# the git-tracked html content. The maintainer pulls/commits/pushes there;
# step-1-fetch just snapshots it into HTML_DIR for the release flow.
HTML_SRC = os.path.realpath('../rsync-web')
FTP_REMOTE_PATH = '/home/ftp/pub/rsync'
HTML_REMOTE_PATH = '/home/httpd/html/rsync'
# Files that ./configure + make produce and that the release tarball / diff
# need to bundle alongside the git-tracked source. Mirrors the GENFILES
# definition in Makefile.in (with rrsync.1{,.html} since we always configure
# --with-rrsync in --step-4-build).
GEN_FILES = [
'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',
'rrsync.1', 'rrsync.1.html',
]
# ---------- Step registry ----------
STEPS = [
('step-1-fetch', 'mirror ../release/rsync-ftp from samba.org and snapshot ../release/rsync-html from ../rsync-web'),
('step-2-prepare', 'gather release info interactively and write release-state.json'),
('step-3-tweak', 'update version.h, rsync.h, NEWS.md, and packaging/*.spec'),
('step-4-build', 'run smart-make + make gen'),
('step-5-commit', 'git commit -a (commit the prepared release changes)'),
('step-6-tag', 'create the gpg-signed git tag'),
('step-7-tarball', 'build the source tarball and diffs.gz against the previous release'),
('step-8-update-ftp', 'refresh README/NEWS/INSTALL/html in the ftp dir, regen ChangeLog.gz, gpg-sign tarballs'),
('step-9-toplinks', 'hard-link top-level release files (final releases only)'),
('step-10-push-ftp', 'rsync ../release/rsync-ftp/ to samba.org'),
('step-11-push-html', 'rsync ../release/rsync-html/ to samba.org (after any manual edits)'),
('step-12-push-git', 'print the git push commands for you to run'),
]
STEP_FLAGS = [s[0] for s in STEPS]
DASH_LINE = '=' * 74
# ---------- State helpers ----------
def load_state():
if not os.path.isfile(STATE_FILE):
die(f"{STATE_FILE} not found. Run --step-2-prepare first.")
with open(STATE_FILE, 'r', encoding='utf-8') as fh:
return json.load(fh)
def save_state(state):
os.makedirs(RELEASE_DIR, exist_ok=True)
with open(STATE_FILE, 'w', encoding='utf-8') as fh:
json.dump(state, fh, indent=2, sort_keys=True)
fh.write('\n')
def require_samba_host():
host = os.environ.get('RSYNC_SAMBA_HOST', '')
if not host.endswith('.samba.org'):
die("Set RSYNC_SAMBA_HOST in your environment to the samba hostname (e.g. hr3.samba.org).")
return host
def require_top_of_checkout():
if not os.path.isfile('packaging/release.py'):
die("Run this script from the top of your rsync checkout.")
if not os.path.isdir('.git'):
die("There is no .git dir in the current directory.")
def replace_or_die(regex, repl, txt, die_msg):
m = regex.search(txt)
if not m:
die(die_msg)
return regex.sub(repl, txt, 1)
def section(title):
print(f"\n{DASH_LINE}\n== {title}\n{DASH_LINE}")
def confirm(prompt, default_no=True):
suffix = '[n] ' if default_no else '[y] '
ans = input(f"{prompt} {suffix}").strip().lower()
if default_no:
return ans.startswith('y')
return ans == '' or ans.startswith('y')
# ---------- Step 1: fetch ftp + html ----------
def step_1_fetch(args):
host = require_samba_host()
os.makedirs(RELEASE_DIR, exist_ok=True)
os.makedirs(WORK_DIR, exist_ok=True)
section(f"Fetching ftp dir into {FTP_DIR}")
if not os.path.isdir(FTP_DIR):
os.makedirs(FTP_DIR)
# The .filt file lives in the ftp dir on the server; mirror down using the
# transmitted filter, falling back to no filter on the very first pull.
filt = os.path.join(FTP_DIR, '.filt')
if os.path.exists(filt):
opts = ['-aivOHP', f'-f:_{filt}']
else:
opts = ['-aivOHP']
cmd_chk(['rsync', *opts, f'{host}:{FTP_REMOTE_PATH}/', f'{FTP_DIR}/'])
section(f"Snapshotting html dir from {HTML_SRC} into {HTML_DIR}")
if not os.path.isdir(HTML_SRC):
die(f"{HTML_SRC} not found. Clone the rsync-web repo there first.")
if not os.path.isdir(os.path.join(HTML_SRC, '.git')):
die(f"{HTML_SRC} exists but is not a git checkout.")
print(f"(Make sure {HTML_SRC} is up to date — this script does not 'git pull' for you.)")
os.makedirs(HTML_DIR, exist_ok=True)
cmd_chk(['rsync', '-aiv', '--exclude=/.git',
f'{HTML_SRC}/', f'{HTML_DIR}/'])
# Then mirror non-git html content from the server (mirroring samba-rsync's
# behavior: skip files that the html git already provides).
filt = os.path.join(HTML_DIR, 'filt')
if os.path.exists(filt):
tmp_filt = os.path.join(HTML_DIR, 'tmp-filt')
cmd_chk(f"sed -n -e 's/[-P]/H/p' '{filt}' >'{tmp_filt}'")
cmd_chk(['rsync', '-aivOHP', f'-f._{tmp_filt}',
f'{host}:{HTML_REMOTE_PATH}/', f'{HTML_DIR}/'])
os.unlink(tmp_filt)
print(f"\nFetch complete. Local dirs are now in {RELEASE_DIR}.")
# ---------- Step 2: prepare ----------
def step_2_prepare(args):
require_top_of_checkout()
os.makedirs(RELEASE_DIR, exist_ok=True)
if not os.path.isdir(FTP_DIR):
die(f"{FTP_DIR} does not exist. Run --step-1-fetch first.")
now = datetime.now().astimezone()
cl_today = now.strftime('* %a %b %d %Y')
year = now.strftime('%Y')
ztoday = now.strftime('%d %b %Y')
today = ztoday.lstrip('0')
tz_now = now.strftime('%z')
tz_num = tz_now[0:1].replace('+', '') + str(float(tz_now[1:3]) + float(tz_now[3:]) / 60)
curversion = get_rsync_version()
lastversion, last_protocol_version, pdate = get_NEWS_version_info()
protocol_version, subprotocol_version = get_protocol_versions()
# Default next version: bump preN, or move dev -> pre1.
version = curversion
m = re.search(r'pre(\d+)', version)
if m:
version = re.sub(r'pre\d+', 'pre' + str(int(m[1]) + 1), version)
else:
version = version.replace('dev', 'pre1')
print(f"\nCurrent version (version.h): {curversion}")
print(f"Last released version (NEWS.md): {lastversion}")
print(f"Current protocol version: {protocol_version} (last released: {last_protocol_version})")
ans = input(f"\nVersion to release [{version}, '.' to drop the preN suffix]: ").strip()
if ans == '.':
version = re.sub(r'pre\d+', '', version)
elif ans:
version = ans
if not re.match(r'^[\d.]+(pre\d+)?$', version):
die(f'Invalid version: "{version}"')
version = re.sub(r'[-.]*pre[-.]*', 'pre', version)
if 'pre' in version and not curversion.endswith('dev'):
lastversion = curversion
ans = input(f"Previous version to diff against [{lastversion}]: ").strip()
if ans:
lastversion = ans
lastversion = re.sub(r'[-.]*pre[-.]*', 'pre', lastversion)
m = re.search(r'(pre\d+)', version)
pre = m[1] if m else ''
finalversion = re.sub(r'pre\d+', '', version)
release = '0.1' if pre else '1'
ans = input(f"RPM release number [{release}]: ").strip()
if ans:
release = ans
if pre:
release += '.' + pre
proto_changed = protocol_version != last_protocol_version
if proto_changed:
if finalversion in pdate:
proto_change_date = pdate[finalversion]
else:
while True:
ans = input(f"Date the protocol changed to {protocol_version} (dd Mmm yyyy): ").strip()
if re.match(r'^\d\d \w\w\w \d\d\d\d$', ans):
break
proto_change_date = ans
else:
proto_change_date = ' ' * 11
if 'pre' in lastversion:
if not pre:
die("Refusing to diff a release version against a pre-release version.")
srcdir = srcdiffdir = lastsrcdir = 'src-previews'
elif pre:
srcdir = srcdiffdir = 'src-previews'
lastsrcdir = 'src'
else:
srcdir = lastsrcdir = 'src'
srcdiffdir = 'src-diffs'
state = {
'version': version,
'lastversion': lastversion,
'finalversion': finalversion,
'pre': pre,
'release': release,
'protocol_version': protocol_version,
'subprotocol_version': subprotocol_version,
'proto_changed': proto_changed,
'proto_change_date': proto_change_date,
'srcdir': srcdir,
'srcdiffdir': srcdiffdir,
'lastsrcdir': lastsrcdir,
'today': today,
'ztoday': ztoday,
'cl_today': cl_today,
'year': year,
'tz_num': tz_num,
'master_branch': args.master_branch,
}
save_state(state)
section("Release info")
for k in ('version', 'lastversion', 'release', 'srcdir', 'srcdiffdir', 'lastsrcdir',
'protocol_version', 'proto_changed', 'proto_change_date'):
print(f" {k}: {state[k]}")
print(f"\nWrote {STATE_FILE}. Re-run --step-2-prepare to change anything.")
# ---------- Step 3: tweak version files ----------
def step_3_tweak(args):
require_top_of_checkout()
state = load_state()
version = state['version']
finalversion = state['finalversion']
pre = state['pre']
release = state['release']
today = state['today']
ztoday = state['ztoday']
cl_today = state['cl_today']
year = state['year']
tz_num = state['tz_num']
proto_changed = state['proto_changed']
proto_change_date = state['proto_change_date']
protocol_version = state['protocol_version']
srcdir = state['srcdir']
specvars = {
'Version:': finalversion,
'Release:': release,
'%define fullversion': f'%{{version}}{pre}',
'Released': version + '.',
'%define srcdir': srcdir,
}
tweak_files = ['version.h', 'rsync.h', 'NEWS.md']
tweak_files += glob.glob('packaging/*.spec')
tweak_files += glob.glob('packaging/*/*.spec')
for fn in tweak_files:
with open(fn, 'r', encoding='utf-8') as fh:
old_txt = txt = fh.read()
if fn == 'version.h':
x_re = re.compile(r'^(#define RSYNC_VERSION).*', re.M)
txt = replace_or_die(x_re, r'\1 "%s"' % version, txt,
f"Unable to update RSYNC_VERSION in {fn}")
x_re = re.compile(r'^(#define MAINTAINER_TZ_OFFSET).*', re.M)
txt = replace_or_die(x_re, r'\1 ' + tz_num, txt,
f"Unable to update MAINTAINER_TZ_OFFSET in {fn}")
elif fn == 'rsync.h':
x_re = re.compile(r'(#define\s+SUBPROTOCOL_VERSION)\s+(\d+)')
repl = lambda m: m[1] + ' ' + (
'0' if not pre or not proto_changed
else '1' if m[2] == '0'
else m[2])
txt = replace_or_die(x_re, repl, txt,
f"Unable to find SUBPROTOCOL_VERSION in {fn}")
elif fn == 'NEWS.md':
efv = re.escape(finalversion)
x_re = re.compile(
r'^# NEWS for rsync %s \(UNRELEASED\)\s+## Changes in this version:\n' % efv
+ r'(\n### PROTOCOL NUMBER:\s+- The protocol number was changed to \d+\.\n)?')
rel_day = 'UNRELEASED' if pre else today
repl = (f'# NEWS for rsync {finalversion} ({rel_day})\n\n'
+ '## Changes in this version:\n')
if proto_changed:
repl += f'\n### PROTOCOL NUMBER:\n\n - The protocol number was changed to {protocol_version}.\n'
good_top = re.sub(r'\(.*?\)', '(UNRELEASED)', repl, 1)
msg = (f"The top of {fn} is not in the right format. It should be:\n" + good_top)
txt = replace_or_die(x_re, repl, txt, msg)
x_re = re.compile(
r'^(\| )(\S{2} \S{3} \d{4})(\s+\|\s+%s\s+\| ).{11}(\s+\| )\S{2}(\s+\|+)$' % efv,
re.M)
repl = lambda m: (m[1] + (m[2] if pre else ztoday) + m[3]
+ proto_change_date + m[4] + protocol_version + m[5])
txt = replace_or_die(x_re, repl, txt,
f'Unable to find "| ?? ??? {year} | {finalversion} | ... |" line in {fn}')
elif '.spec' in fn:
for var, val in specvars.items():
x_re = re.compile(r'^%s .*' % re.escape(var), re.M)
txt = replace_or_die(x_re, var + ' ' + val, txt,
f"Unable to update {var} in {fn}")
x_re = re.compile(r'^\* \w\w\w \w\w\w \d\d \d\d\d\d (.*)', re.M)
txt = replace_or_die(x_re, r'%s \1' % cl_today, txt,
f"Unable to update ChangeLog header in {fn}")
else:
die(f"Unrecognized file in tweak_files: {fn}")
if txt != old_txt:
print(f"Updating {fn}")
with open(fn, 'w', encoding='utf-8') as fh:
fh.write(txt)
cmd_chk(['packaging/year-tweak'])
section("git diff after tweaks")
cmd_run(['git', '--no-pager', 'diff'])
# ---------- Step 4: build ----------
def step_4_build(args):
require_top_of_checkout()
load_state() # just to ensure we've prepared
section("Running prepare-source + configure --prefix=/usr --with-rrsync + make + make gen")
# Always re-prepare so configure.sh is current; we run configure ourselves
# with the release-required flags rather than relying on the cached
# config.status (which may have been produced with different options).
if os.path.isfile('.fetch'):
cmd_chk(['./prepare-source', 'fetch'])
else:
cmd_chk(['./prepare-source'])
cmd_chk(['./configure', '--prefix=/usr', '--with-rrsync'])
cmd_chk(['make'])
cmd_chk(['make', 'gen'])
# ---------- Step 5: commit ----------
def step_5_commit(args):
require_top_of_checkout()
state = load_state()
version = state['version']
section("git status")
cmd_run(['git', 'status'])
if not confirm("Commit all current changes with the release message?"):
die("Aborted.")
cmd_chk(['git', 'commit', '-a', '-m', f'Preparing for release of {version} [buildall]'])
# ---------- Step 6: tag ----------
def step_6_tag(args):
require_top_of_checkout()
state = load_state()
version = state['version']
v_ver = 'v' + version
out = cmd_txt_chk(['git', 'tag', '-l', v_ver]).out
if out.strip():
if not confirm(f"Tag {v_ver} already exists. Delete and recreate?"):
die("Aborted.")
cmd_chk(['git', 'tag', '-d', v_ver])
# Prime the gpg agent so the actual tag signing won't prompt.
section("Priming gpg agent")
cmd_run("touch TeMp; gpg --sign TeMp; rm -f TeMp TeMp.gpg")
section(f"Creating signed tag {v_ver}")
out = cmd_txt(['git', 'tag', '-s', '-m', f'Version {version}.', v_ver],
capture='combined').out
print(out, end='')
if 'bad passphrase' in out.lower() or 'failed' in out.lower():
die("Tag creation failed.")
# ---------- Step 7: tarball + diff ----------
def step_7_tarball(args):
require_top_of_checkout()
state = load_state()
version = state['version']
lastversion = state['lastversion']
pre = state['pre']
srcdir = state['srcdir']
srcdiffdir = state['srcdiffdir']
lastsrcdir = state['lastsrcdir']
rsync_ver = 'rsync-' + version
rsync_lastver = 'rsync-' + lastversion
v_ver = 'v' + version
srctar_name = f"{rsync_ver}.tar.gz"
diff_name = f"{rsync_lastver}-{version}.diffs.gz"
srctar_file = os.path.join(FTP_DIR, srcdir, srctar_name)
diff_file = os.path.join(FTP_DIR, srcdiffdir, diff_name)
lasttar_file = os.path.join(FTP_DIR, lastsrcdir, rsync_lastver + '.tar.gz')
for d in (os.path.dirname(srctar_file), os.path.dirname(diff_file)):
os.makedirs(d, exist_ok=True)
if not os.path.isfile(lasttar_file):
die(f"Previous tarball not found: {lasttar_file}")
# Stage in ../release/work to keep the source checkout clean.
if os.path.isdir(WORK_DIR):
shutil.rmtree(WORK_DIR)
os.makedirs(WORK_DIR)
a_dir = os.path.join(WORK_DIR, 'a')
b_dir = os.path.join(WORK_DIR, 'b')
# Extract gen files from the previous tarball into work/a/.
tweaked_gen_files = [os.path.join(rsync_lastver, fn) for fn in GEN_FILES]
cmd_chk(['tar', '-C', WORK_DIR, '-xzf', lasttar_file, *tweaked_gen_files])
os.rename(os.path.join(WORK_DIR, rsync_lastver), a_dir)
# Copy current gen files (built in the top-level checkout) into work/b/.
os.makedirs(b_dir)
cmd_chk(['rsync', '-a', *GEN_FILES, b_dir + '/'])
section(f"Creating {diff_file}")
sed_script = r's:^((---|\+\+\+) [ab]/[^\t]+)\t.*:\1:' # no single quotes!
cmd_chk(
f"(git diff v{lastversion} {v_ver} -- ':!.github'; "
f"diff -upN {a_dir} {b_dir} | sed -r '{sed_script}') | gzip -9 >{diff_file}")
section(f"Creating {srctar_file}")
# Reuse work/b/ (which already holds the fresh gen files) as the release
# staging dir, then let "git archive" overlay the git-tracked source files
# on top. That way the tarball ends up with both gen files and source.
rsync_ver_dir = os.path.join(WORK_DIR, rsync_ver)
shutil.rmtree(a_dir)
os.rename(b_dir, rsync_ver_dir)
cmd_chk(f"git archive --format=tar --prefix={rsync_ver}/ {v_ver} | "
f"tar -C {WORK_DIR} -xf -")
cmd_chk(f"support/git-set-file-times --quiet --prefix={rsync_ver_dir}/")
cmd_chk(['fakeroot', 'tar', '-C', WORK_DIR, '-czf', srctar_file,
'--exclude=.github', rsync_ver])
# Leave staging in place; --step-8-update-ftp does its own thing.
print(f"\nCreated:\n {srctar_file}\n {diff_file}")
# ---------- Step 8: update ftp ----------
def step_8_update_ftp(args):
require_top_of_checkout()
state = load_state()
version = state['version']
lastversion = state['lastversion']
srcdir = state['srcdir']
srcdiffdir = state['srcdiffdir']
rsync_ver = 'rsync-' + version
rsync_lastver = 'rsync-' + lastversion
srctar_file = os.path.join(FTP_DIR, srcdir, f"{rsync_ver}.tar.gz")
diff_file = os.path.join(FTP_DIR, srcdiffdir,
f"{rsync_lastver}-{version}.diffs.gz")
section(f"Refreshing top-of-tree files in {FTP_DIR}")
md_files = ['README.md', 'NEWS.md', 'INSTALL.md']
html_files = [fn for fn in GEN_FILES if fn.endswith('.html')]
cmd_chk(['rsync', '-a', *md_files, *html_files, FTP_DIR + '/'])
cmd_chk(['./md-convert', '--dest', FTP_DIR, *md_files])
section(f"Regenerating {FTP_DIR}/ChangeLog.gz")
cmd_chk(f"git log --name-status | gzip -9 >{FTP_DIR}/ChangeLog.gz")
# Prime gpg agent and then sign the tar + diff.
section("Priming gpg agent")
cmd_run("touch TeMp; gpg --sign TeMp; rm -f TeMp TeMp.gpg")
for fn in (srctar_file, diff_file):
if not os.path.isfile(fn):
die(f"Missing file to sign: {fn}. Did --step-7-tarball run successfully?")
asc_fn = fn + '.asc'
if os.path.lexists(asc_fn):
os.unlink(asc_fn)
section(f"GPG-signing {fn}")
res = cmd_run(['gpg', '--batch', '-ba', fn])
if res.returncode not in (0, 2):
die("gpg signing failed.")
# ---------- Step 9: top-level hard links ----------
def step_9_toplinks(args):
require_top_of_checkout()
state = load_state()
pre = state['pre']
if pre:
print("Skipping: pre-releases do not get top-level hard links.")
return
version = state['version']
lastversion = state['lastversion']
srcdir = state['srcdir']
srcdiffdir = state['srcdiffdir']
rsync_ver = 'rsync-' + version
rsync_lastver = 'rsync-' + lastversion
srctar_file = os.path.join(FTP_DIR, srcdir, f"{rsync_ver}.tar.gz")
diff_file = os.path.join(FTP_DIR, srcdiffdir,
f"{rsync_lastver}-{version}.diffs.gz")
section("Removing stale top-level rsync-* files")
for find in [f'{FTP_DIR}/rsync-*.gz',
f'{FTP_DIR}/rsync-*.asc',
f'{FTP_DIR}/src-previews/rsync-*diffs.gz*']:
for fn in glob.glob(find):
os.unlink(fn)
top_link = [
srctar_file, srctar_file + '.asc',
diff_file, diff_file + '.asc',
]
for fn in top_link:
target = re.sub(r'/src(-\w+)?/', '/', fn)
if os.path.lexists(target):
os.unlink(target)
os.link(fn, target)
print(f" linked {target}")
# ---------- Step 10: push ftp ----------
def step_10_push_ftp(args):
host = require_samba_host()
if not os.path.isdir(FTP_DIR):
die(f"{FTP_DIR} does not exist. Run --step-1-fetch first.")
section(f"rsync ftp dir to {host}")
rsync_with_confirm(['-aivOHP', '--chown=:rsync', '--del',
f'-f._{os.path.join(FTP_DIR, ".filt")}',
f'{FTP_DIR}/', f'{host}:{FTP_REMOTE_PATH}/'])
# ---------- Step 11: push html ----------
def step_11_push_html(args):
host = require_samba_host()
if not os.path.isdir(HTML_DIR):
die(f"{HTML_DIR} does not exist. Run --step-1-fetch first.")
section(f"rsync html dir to {host}")
filt = os.path.join(HTML_DIR, 'filt')
rsync_with_confirm(['-aivOHP', '--chown=:rsync', '--del',
f'-f._{filt}',
f'{HTML_DIR}/', f'{host}:{HTML_REMOTE_PATH}/'])
# ---------- Step 12: print push-git instructions ----------
def step_12_push_git(args):
state = load_state()
version = state['version']
master_branch = state['master_branch']
v_ver = 'v' + version
print(f"""\
{DASH_LINE}
Run these from the rsync-git checkout (this script does not push for you):
git push origin {master_branch}
git push origin {v_ver}
If you have a 'samba' remote configured (git.samba.org:/data/git/rsync.git):
git push samba {master_branch}
git push samba {v_ver}
Then upload the tarball + .asc to the GitHub release for {v_ver}, run
packaging/send-news (when convenient), and announce on rsync-announce@,
rsync@, and Discord.
""")
# ---------- shared rsync-with-confirm ----------
def rsync_with_confirm(rsync_args):
"""Run an rsync command in dry-run mode, then ask before running for real."""
cmd_run(['rsync', '--dry-run', *rsync_args])
if confirm("Run without --dry-run?"):
cmd_run(['rsync', *rsync_args])
# ---------- dispatch ----------
STEP_FUNCS = {
'step-1-fetch': step_1_fetch,
'step-2-prepare': step_2_prepare,
'step-3-tweak': step_3_tweak,
'step-4-build': step_4_build,
'step-5-commit': step_5_commit,
'step-6-tag': step_6_tag,
'step-7-tarball': step_7_tarball,
'step-8-update-ftp': step_8_update_ftp,
'step-9-toplinks': step_9_toplinks,
'step-10-push-ftp': step_10_push_ftp,
'step-11-push-html': step_11_push_html,
'step-12-push-git': step_12_push_git,
}
def signal_handler(sig, frame):
die("\nAborting due to SIGINT.")
def main():
parser = argparse.ArgumentParser(
description="Step-based release script for rsync.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="Run --list to see the steps. Each invocation runs exactly one --step-* option.")
parser.add_argument('--branch', '-b', dest='master_branch', default='master',
help="The branch to release (default: master).")
parser.add_argument('--list', action='store_true',
help="List all release steps and exit.")
grp = parser.add_mutually_exclusive_group()
for flag, descr in STEPS:
grp.add_argument('--' + flag, dest='step', action='store_const',
const=flag, help=descr)
args = parser.parse_args()
if args.list:
print("Release steps:")
for flag, descr in STEPS:
print(f" --{flag:18s} {descr}")
return
if not args.step:
parser.error("pick one --step-N-XX option (or --list to see them).")
signal.signal(signal.SIGINT, signal_handler)
os.environ['LESS'] = 'mqeiXR'
STEP_FUNCS[args.step](args)
if __name__ == '__main__':
main()
# vim: sw=4 et ft=python

124
packaging/samba-rsync Executable file
View File

@@ -0,0 +1,124 @@
#!/bin/bash
# This script makes it easy to update the ftp & html directories on the samba.org server.
# It expects the 2 *_DEST directories to contain updated files that need to be sent to
# the remote server. If these directories don't exist yet, they will be copied from the
# remote server (while also making the html dir a git checkout).
FTP_SRC="$HOME/samba-rsync-ftp"
HTML_SRC="$HOME/samba-rsync-html"
FTP_DEST="/home/ftp/pub/rsync"
HTML_DEST="/home/httpd/html/rsync"
HTML_GIT='git.samba.org:/data/git/rsync-web.git'
export RSYNC_PARTIAL_DIR=''
case "$RSYNC_SAMBA_HOST" in
*.samba.org) ;;
*)
echo "You must set RSYNC_SAMBA_HOST in your environment to the samba hostname to use." >&2
exit 1
;;
esac
MODE=''
REVERSE=''
while (( $# )); do
case "$1" in
-R|--reverse) REVERSE=yes ;;
f|ftp) MODE=ftp ;;
h|html) MODE=html ;;
-h|--help)
echo "Usage: [-R] [f|ftp|h|html]"
echo "-R --reverse Copy the files from the server to the local host."
echo " The default is to update the remote files."
echo "-h --help Output this help message."
echo " "
echo "The script will prompt if ftp or html is not specified on the command line."
echo "Only one category can be copied at a time. When pulling html files, a git"
echo "checkout will be either created or updated prior to the rsync copy."
exit
;;
*)
echo "Invalid option: $1" >&2
exit 1
;;
esac
shift
done
while [ ! "$MODE" ]; do
if [ "$REVERSE" = yes ]; then
DIRECTION=FROM
else
DIRECTION=TO
fi
echo -n "Copy which files $DIRECTION the server? ftp or html? "
read ans
case "$ans" in
f*) MODE=ftp ;;
h*) MODE=html ;;
'') exit 1 ;;
*) echo "You must answer f or h to copy the ftp or html data." ;;
esac
done
if [ "$MODE" = ftp ]; then
SRC_DIR="$FTP_SRC"
DEST_DIR="$FTP_DEST"
FILT=".filt"
else
SRC_DIR="$HTML_SRC"
DEST_DIR="$HTML_DEST"
FILT="filt"
fi
function do_rsync {
rsync --dry-run "${@}" | grep -v 'is uptodate$'
echo ''
echo -n "Run without --dry-run? [n] "
read ans
case "$ans" in
y*) rsync "${@}" | grep -v 'is uptodate$' ;;
esac
}
if [ -d "$SRC_DIR" ]; then
REVERSE_RSYNC=do_rsync
else
echo "The directory $SRC_DIR does not exist yet."
echo -n "Do you want to create it? [n] "
read ans
case "$ans" in
y*) ;;
*) exit 1 ;;
esac
REVERSE=yes
REVERSE_RSYNC=rsync
fi
if [ "$REVERSE" = yes ]; then
OPTS='-aivOHP'
TMP_FILT="$SRC_DIR/tmp-filt"
echo "Copying files from $RSYNC_SAMBA_HOST to $SRC_DIR ..."
if [ "$MODE" = html ]; then
if [ $REVERSE_RSYNC = rsync ]; then
git clone "$HTML_GIT" "$SRC_DIR" || exit 1
else
cd "$SRC_DIR" || exit 1
git pull || exit 1
fi
sed -n -e 's/[-P]/H/p' "$SRC_DIR/$FILT" >"$TMP_FILT"
OPTS="${OPTS}f._$TMP_FILT"
else
OPTS="${OPTS}f:_$FILT"
fi
$REVERSE_RSYNC "$OPTS" "$RSYNC_SAMBA_HOST:$DEST_DIR/" "$SRC_DIR/"
rm -f "$TMP_FILT"
exit
fi
cd "$SRC_DIR" || exit 1
echo "Copying files from $SRC_DIR to $RSYNC_SAMBA_HOST ..."
do_rsync -aivOHP --chown=:rsync --del -f._$FILT . "$RSYNC_SAMBA_HOST:$DEST_DIR/"

33
packaging/send-news Executable file
View File

@@ -0,0 +1,33 @@
#!/bin/bash -e
# This script expects the ~/src/rsync directory to contain the rsync
# source that has been updated. It also expects the auto-build-save
# directory to have been created prior to the running of configure so
# that each branch has its own build directory underneath. This supports
# the maintainer workflow for the rsync-patches files maintenace.
FTP_SRC="$HOME/samba-rsync-ftp"
FTP_DEST="/home/ftp/pub/rsync"
MD_FILES="README.md INSTALL.md NEWS.md"
case "$RSYNC_SAMBA_HOST" in
*.samba.org) ;;
*)
echo "You must set RSYNC_SAMBA_HOST in your environment to the samba hostname to use." >&2
exit 1
;;
esac
if [ ! -d "$FTP_SRC" ]; then
packaging/samba-rsync ftp # Ask to initialize the local ftp dir
fi
cd ~/src/rsync
make man
./md-convert --dest="$FTP_SRC" $MD_FILES
rsync -aiic $MD_FILES auto-build-save/master/*.?.html "$FTP_SRC"
cd "$FTP_SRC"
rsync -aiic README.* INSTALL.* NEWS.* *.?.html "$RSYNC_SAMBA_HOST:$FTP_DEST/"

View File

@@ -6,9 +6,10 @@
import os, sys, re, argparse, glob
VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z]\S*\s+.*);', re.M)
VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z][^ \n\t:]*\s+.*);', re.M)
EXTERNS_RE = re.compile(r'^extern\s+(.*);', re.M)
types = { }
sizes = { }
def main():
@@ -68,19 +69,44 @@ def parse_vars(fn, lines):
for line in lines:
line = re.sub(r'\s*\{.*\}', '', line)
line = re.sub(r'\s*\(.*\)', '', line)
for item in re.split(r'\s*,\s*', line):
item = re.sub(r'\s*=.*', '', item)
m = re.search(r'(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
line = re.sub(r'\s*=\s*[^,]*', '', line)
m = re.search(r'^(?:(?:static|extern)\s+)?(?P<type>[^\[,]+?)(?P<vars>\w+([\[,].+)?)$', line)
if not m:
print(f"Bogus match? ({line})")
continue
items = m['vars']
main_type = m['type'].strip()
mt_len = len(main_type)
main_type = main_type.rstrip('*')
first_stars = '*' * (mt_len - len(main_type))
if first_stars:
main_type = main_type.rstrip()
items = first_stars + items
for item in re.split(r'\s*,\s*', items):
m = re.search(r'(?P<stars>\*+\s*)?(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
if not m:
print(f"Bogus match? ({item})")
continue
if m['sz']:
if m['var'] in sizes:
if sizes[m['var']] != m['sz']:
typ = main_type
if m['stars']:
typ = typ + m['stars'].strip()
chk = [
'type', typ, types,
'size', m['sz'], sizes,
]
while chk:
label = chk.pop(0)
new = chk.pop(0)
lst = chk.pop(0)
if label == 'type':
new = ' '.join(new.split()).replace(' *', '*')
if m['var'] in lst:
old = lst[m['var']]
if new != old:
var = m['var']
print(fn, f'has inconsistent size for "{var}":', m['sz'], 'vs', sizes[var])
print(fn, f'has inconsistent {label} for "{var}":', new, 'vs', old)
else:
sizes[m['var']] = m['sz']
lst[m['var']] = new
ret.append(m['var'])
return ret

View File

@@ -7,9 +7,6 @@
import sys, os, re, argparse, subprocess
from datetime import datetime
MAINTAINER_NAME = 'Wayne Davison'
MAINTAINER_SUF = ' ' + MAINTAINER_NAME + "\n"
def main():
latest_year = '2000'
@@ -22,10 +19,6 @@ def main():
m = argparse.Namespace(**m.groupdict())
if m.year > latest_year:
latest_year = m.year
if m.fn.startswith('zlib/') or m.fn.startswith('popt/'):
continue
if re.search(r'\.(c|h|sh|test)$', m.fn):
maybe_edit_copyright_year(m.fn, m.year)
proc.communicate()
fn = 'latest-year.h'
@@ -39,55 +32,8 @@ def main():
fh.write(txt)
def maybe_edit_copyright_year(fn, year):
opening_lines = [ ]
copyright_line = None
with open(fn, 'r', encoding='utf-8') as fh:
for lineno, line in enumerate(fh):
opening_lines.append(line)
if lineno > 3 and not re.search(r'\S', line):
break
m = re.match(r'^(?P<pre>.*Copyright\s+\S+\s+)(?P<year>\d\d\d\d(?:-\d\d\d\d)?(,\s+\d\d\d\d)*)(?P<suf>.+)', line)
if not m:
continue
copyright_line = argparse.Namespace(**m.groupdict())
copyright_line.lineno = len(opening_lines)
copyright_line.is_maintainer_line = MAINTAINER_NAME in copyright_line.suf
copyright_line.txt = line
if copyright_line.is_maintainer_line:
break
if not copyright_line:
return
if copyright_line.is_maintainer_line:
cyears = copyright_line.year.split('-')
if year == cyears[0]:
cyears = [ year ]
else:
cyears = [ cyears[0], year ]
txt = copyright_line.pre + '-'.join(cyears) + MAINTAINER_SUF
if txt == copyright_line.txt:
return
opening_lines[copyright_line.lineno - 1] = txt
else:
if fn.startswith('lib/') or fn.startswith('testsuite/'):
return
txt = copyright_line.pre + year + MAINTAINER_SUF
opening_lines[copyright_line.lineno - 1] += txt
remaining_txt = fh.read()
print(f"Updating {fn} with year {year}")
with open(fn, 'w', encoding='utf-8') as fh:
fh.write(''.join(opening_lines))
fh.write(remaining_txt)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Grab the year of last mod for our c & h files and make sure the Copyright comment is up-to-date.")
parser = argparse.ArgumentParser(description="Grab the year of the last mod for our c & h files and make sure the LATEST_YEAR value is accurate.")
args = parser.parse_args()
main()

View File

@@ -1,55 +0,0 @@
/** \ingroup popt
* \file popt/findme.c
*/
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
file accompanying popt source distributions, available from
ftp://ftp.rpm.org/pub/rpm/dist. */
#include "system.h"
#include "findme.h"
const char * findProgramPath(const char * argv0)
{
char * path = getenv("PATH");
char * pathbuf;
char * start, * chptr;
char * buf;
size_t bufsize;
if (argv0 == NULL) return NULL; /* XXX can't happen */
/* If there is a / in the argv[0], it has to be an absolute path */
if (strchr(argv0, '/'))
return xstrdup(argv0);
if (path == NULL) return NULL;
bufsize = strlen(path) + 1;
start = pathbuf = alloca(bufsize);
if (pathbuf == NULL) return NULL; /* XXX can't happen */
strlcpy(pathbuf, path, bufsize);
bufsize += sizeof "/" - 1 + strlen(argv0);
buf = malloc(bufsize);
if (buf == NULL) return NULL; /* XXX can't happen */
chptr = NULL;
/*@-branchstate@*/
do {
if ((chptr = strchr(start, ':')))
*chptr = '\0';
snprintf(buf, bufsize, "%s/%s", start, argv0);
if (!access(buf, X_OK))
return buf;
if (chptr)
start = chptr + 1;
else
start = NULL;
} while (start && *start);
/*@=branchstate@*/
free(buf);
return NULL;
}

View File

@@ -1,20 +0,0 @@
/** \ingroup popt
* \file popt/findme.h
*/
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
file accompanying popt source distributions, available from
ftp://ftp.rpm.org/pub/rpm/dist. */
#ifndef H_FINDME
#define H_FINDME
/**
* Return absolute path to executable by searching PATH.
* @param argv0 name of executable
* @return (malloc'd) absolute path to executable (or NULL)
*/
/*@null@*/ const char * findProgramPath(/*@null@*/ const char * argv0)
/*@*/;
#endif

959
popt/lookup3.c Normal file
View File

@@ -0,0 +1,959 @@
/* -------------------------------------------------------------------- */
/*
* lookup3.c, by Bob Jenkins, May 2006, Public Domain.
*
* These are functions for producing 32-bit hashes for hash table lookup.
* jlu32w(), jlu32l(), jlu32lpair(), jlu32b(), _JLU3_MIX(), and _JLU3_FINAL()
* are externally useful functions. Routines to test the hash are included
* if SELF_TEST is defined. You can use this free for any purpose. It's in
* the public domain. It has no warranty.
*
* You probably want to use jlu32l(). jlu32l() and jlu32b()
* hash byte arrays. jlu32l() is is faster than jlu32b() on
* little-endian machines. Intel and AMD are little-endian machines.
* On second thought, you probably want jlu32lpair(), which is identical to
* jlu32l() except it returns two 32-bit hashes for the price of one.
* You could implement jlu32bpair() if you wanted but I haven't bothered here.
*
* If you want to find a hash of, say, exactly 7 integers, do
* a = i1; b = i2; c = i3;
* _JLU3_MIX(a,b,c);
* a += i4; b += i5; c += i6;
* _JLU3_MIX(a,b,c);
* a += i7;
* _JLU3_FINAL(a,b,c);
* then use c as the hash value. If you have a variable size array of
* 4-byte integers to hash, use jlu32w(). If you have a byte array (like
* a character string), use jlu32l(). If you have several byte arrays, or
* a mix of things, see the comments above jlu32l().
*
* Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
* then mix those integers. This is fast (you can do a lot more thorough
* mixing with 12*3 instructions on 3 integers than you can with 3 instructions
* on 1 byte), but shoehorning those bytes into integers efficiently is messy.
*/
/* -------------------------------------------------------------------- */
#include <stdint.h>
#if defined(_JLU3_SELFTEST)
# define _JLU3_jlu32w 1
# define _JLU3_jlu32l 1
# define _JLU3_jlu32lpair 1
# define _JLU3_jlu32b 1
#endif
static const union _dbswap {
const uint32_t ui;
const unsigned char uc[4];
} endian = { .ui = 0x11223344 };
# define HASH_LITTLE_ENDIAN (endian.uc[0] == (unsigned char) 0x44)
# define HASH_BIG_ENDIAN (endian.uc[0] == (unsigned char) 0x11)
#ifndef ROTL32
# define ROTL32(x, s) (((x) << (s)) | ((x) >> (32 - (s))))
#endif
/* NOTE: The _size parameter should be in bytes. */
#define _JLU3_INIT(_h, _size) (0xdeadbeef + ((uint32_t)(_size)) + (_h))
/* -------------------------------------------------------------------- */
/*
* _JLU3_MIX -- mix 3 32-bit values reversibly.
*
* This is reversible, so any information in (a,b,c) before _JLU3_MIX() is
* still in (a,b,c) after _JLU3_MIX().
*
* If four pairs of (a,b,c) inputs are run through _JLU3_MIX(), or through
* _JLU3_MIX() in reverse, there are at least 32 bits of the output that
* are sometimes the same for one pair and different for another pair.
* This was tested for:
* * pairs that differed by one bit, by two bits, in any combination
* of top bits of (a,b,c), or in any combination of bottom bits of
* (a,b,c).
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
* the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
* is commonly produced by subtraction) look like a single 1-bit
* difference.
* * the base values were pseudorandom, all zero but one bit set, or
* all zero plus a counter that starts at zero.
*
* Some k values for my "a-=c; a^=ROTL32(c,k); c+=b;" arrangement that
* satisfy this are
* 4 6 8 16 19 4
* 9 15 3 18 27 15
* 14 9 3 7 17 3
* Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
* for "differ" defined as + with a one-bit base and a two-bit delta. I
* used http://burtleburtle.net/bob/hash/avalanche.html to choose
* the operations, constants, and arrangements of the variables.
*
* This does not achieve avalanche. There are input bits of (a,b,c)
* that fail to affect some output bits of (a,b,c), especially of a. The
* most thoroughly mixed value is c, but it doesn't really even achieve
* avalanche in c.
*
* This allows some parallelism. Read-after-writes are good at doubling
* the number of bits affected, so the goal of mixing pulls in the opposite
* direction as the goal of parallelism. I did what I could. Rotates
* seem to cost as much as shifts on every machine I could lay my hands
* on, and rotates are much kinder to the top and bottom bits, so I used
* rotates.
*/
/* -------------------------------------------------------------------- */
#define _JLU3_MIX(a,b,c) \
{ \
a -= c; a ^= ROTL32(c, 4); c += b; \
b -= a; b ^= ROTL32(a, 6); a += c; \
c -= b; c ^= ROTL32(b, 8); b += a; \
a -= c; a ^= ROTL32(c,16); c += b; \
b -= a; b ^= ROTL32(a,19); a += c; \
c -= b; c ^= ROTL32(b, 4); b += a; \
}
/* -------------------------------------------------------------------- */
/**
* _JLU3_FINAL -- final mixing of 3 32-bit values (a,b,c) into c
*
* Pairs of (a,b,c) values differing in only a few bits will usually
* produce values of c that look totally different. This was tested for
* * pairs that differed by one bit, by two bits, in any combination
* of top bits of (a,b,c), or in any combination of bottom bits of
* (a,b,c).
* * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
* the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
* is commonly produced by subtraction) look like a single 1-bit
* difference.
* * the base values were pseudorandom, all zero but one bit set, or
* all zero plus a counter that starts at zero.
*
* These constants passed:
* 14 11 25 16 4 14 24
* 12 14 25 16 4 14 24
* and these came close:
* 4 8 15 26 3 22 24
* 10 8 15 26 3 22 24
* 11 8 15 26 3 22 24
*/
/* -------------------------------------------------------------------- */
#define _JLU3_FINAL(a,b,c) \
{ \
c ^= b; c -= ROTL32(b,14); \
a ^= c; a -= ROTL32(c,11); \
b ^= a; b -= ROTL32(a,25); \
c ^= b; c -= ROTL32(b,16); \
a ^= c; a -= ROTL32(c,4); \
b ^= a; b -= ROTL32(a,14); \
c ^= b; c -= ROTL32(b,24); \
}
#if defined(_JLU3_jlu32w)
uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size);
/* -------------------------------------------------------------------- */
/**
* This works on all machines. To be useful, it requires
* -- that the key be an array of uint32_t's, and
* -- that the size be the number of uint32_t's in the key
*
* The function jlu32w() is identical to jlu32l() on little-endian
* machines, and identical to jlu32b() on big-endian machines,
* except that the size has to be measured in uint32_ts rather than in
* bytes. jlu32l() is more complicated than jlu32w() only because
* jlu32l() has to dance around fitting the key bytes into registers.
*
* @param h the previous hash, or an arbitrary value
* @param *k the key, an array of uint32_t values
* @param size the size of the key, in uint32_ts
* @return the lookup3 hash
*/
/* -------------------------------------------------------------------- */
uint32_t jlu32w(uint32_t h, const uint32_t *k, size_t size)
{
uint32_t a = _JLU3_INIT(h, (size * sizeof(*k)));
uint32_t b = a;
uint32_t c = a;
if (k == NULL)
goto exit;
/*----------------------------------------------- handle most of the key */
while (size > 3) {
a += k[0];
b += k[1];
c += k[2];
_JLU3_MIX(a,b,c);
size -= 3;
k += 3;
}
/*----------------------------------------- handle the last 3 uint32_t's */
switch (size) {
case 3 : c+=k[2];
case 2 : b+=k[1];
case 1 : a+=k[0];
_JLU3_FINAL(a,b,c);
/* fallthrough */
case 0:
break;
}
/*---------------------------------------------------- report the result */
exit:
return c;
}
#endif /* defined(_JLU3_jlu32w) */
#if defined(_JLU3_jlu32l)
uint32_t jlu32l(uint32_t h, const void *key, size_t size);
/* -------------------------------------------------------------------- */
/*
* jlu32l() -- hash a variable-length key into a 32-bit value
* h : can be any 4-byte value
* k : the key (the unaligned variable-length array of bytes)
* size : the size of the key, counting by bytes
* Returns a 32-bit value. Every bit of the key affects every bit of
* the return value. Two keys differing by one or two bits will have
* totally different hash values.
*
* The best hash table sizes are powers of 2. There is no need to do
* mod a prime (mod is sooo slow!). If you need less than 32 bits,
* use a bitmask. For example, if you need only 10 bits, do
* h = (h & hashmask(10));
* In which case, the hash table should have hashsize(10) elements.
*
* If you are hashing n strings (uint8_t **)k, do it like this:
* for (i=0, h=0; i<n; ++i) h = jlu32l(h, k[i], len[i]);
*
* By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
* code any way you wish, private, educational, or commercial. It's free.
*
* Use for hash table lookup, or anything where one collision in 2^^32 is
* acceptable. Do NOT use for cryptographic purposes.
*
* @param h the previous hash, or an arbitrary value
* @param *k the key, an array of uint8_t values
* @param size the size of the key
* @return the lookup3 hash
*/
/* -------------------------------------------------------------------- */
uint32_t jlu32l(uint32_t h, const void *key, size_t size)
{
union { const void *ptr; size_t i; } u;
uint32_t a = _JLU3_INIT(h, size);
uint32_t b = a;
uint32_t c = a;
if (key == NULL)
goto exit;
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
#ifdef VALGRIND
const uint8_t *k8;
#endif
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (size > 12) {
a += k[0];
b += k[1];
c += k[2];
_JLU3_MIX(a,b,c);
size -= 12;
k += 3;
}
/*------------------------- handle the last (probably partial) block */
/*
* "k[2]&0xffffff" actually reads beyond the end of the string, but
* then masks off the part it's not allowed to read. Because the
* string is aligned, the masked-off tail is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
#ifndef VALGRIND
switch (size) {
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
case 8: b += k[1]; a+=k[0]; break;
case 7: b += k[1]&0xffffff; a+=k[0]; break;
case 6: b += k[1]&0xffff; a+=k[0]; break;
case 5: b += k[1]&0xff; a+=k[0]; break;
case 4: a += k[0]; break;
case 3: a += k[0]&0xffffff; break;
case 2: a += k[0]&0xffff; break;
case 1: a += k[0]&0xff; break;
case 0: goto exit;
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch (size) {
case 12: c += k[2]; b+=k[1]; a+=k[0] break;
case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
case 9: c += k8[8]; /* fallthrough */
case 8: b += k[1]; a+=k[0]; break;
case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
case 5: b += k8[4]; /* fallthrough */
case 4: a += k[0]; break;
case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
case 1: a += k8[0]; break;
case 0: goto exit;
}
#endif /* !valgrind */
} 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 (size > 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);
_JLU3_MIX(a,b,c);
size -= 12;
k += 6;
}
/*------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch (size) {
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;
/* fallthrough */
case 10:
c += (uint32_t)k[4];
b += k[2]+(((uint32_t)k[3])<<16);
a += k[0]+(((uint32_t)k[1])<<16);
break;
case 9:
c += (uint32_t)k8[8];
/* fallthrough */
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;
/* fallthrough */
case 6:
b += (uint32_t)k[2];
a += k[0]+(((uint32_t)k[1])<<16);
break;
case 5:
b += (uint32_t)k8[4];
/* fallthrough */
case 4:
a += k[0]+(((uint32_t)k[1])<<16);
break;
case 3:
a += ((uint32_t)k8[2])<<16;
/* fallthrough */
case 2:
a += (uint32_t)k[0];
break;
case 1:
a += (uint32_t)k8[0];
break;
case 0:
goto exit;
}
} 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 (size > 12) {
a += (uint32_t)k[0];
a += ((uint32_t)k[1])<<8;
a += ((uint32_t)k[2])<<16;
a += ((uint32_t)k[3])<<24;
b += (uint32_t)k[4];
b += ((uint32_t)k[5])<<8;
b += ((uint32_t)k[6])<<16;
b += ((uint32_t)k[7])<<24;
c += (uint32_t)k[8];
c += ((uint32_t)k[9])<<8;
c += ((uint32_t)k[10])<<16;
c += ((uint32_t)k[11])<<24;
_JLU3_MIX(a,b,c);
size -= 12;
k += 12;
}
/*---------------------------- last block: affect all 32 bits of (c) */
switch (size) {
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 += (uint32_t)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 += (uint32_t)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 += (uint32_t)k[0];
break;
case 0:
goto exit;
}
}
_JLU3_FINAL(a,b,c);
exit:
return c;
}
#endif /* defined(_JLU3_jlu32l) */
#if defined(_JLU3_jlu32lpair)
/**
* jlu32lpair: return 2 32-bit hash values.
*
* This is identical to jlu32l(), 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)".
*
* @param h the previous hash, or an arbitrary value
* @param *key the key, an array of uint8_t values
* @param size the size of the key in bytes
* @retval *pc, IN: primary initval, OUT: primary hash
* *retval *pb IN: secondary initval, OUT: secondary hash
*/
void jlu32lpair(const void *key, size_t size, uint32_t *pc, uint32_t *pb)
{
union { const void *ptr; size_t i; } u;
uint32_t a = _JLU3_INIT(*pc, size);
uint32_t b = a;
uint32_t c = a;
if (key == NULL)
goto exit;
c += *pb; /* Add the secondary hash. */
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
#ifdef VALGRIND
const uint8_t *k8;
#endif
/*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (size > (size_t)12) {
a += k[0];
b += k[1];
c += k[2];
_JLU3_MIX(a,b,c);
size -= 12;
k += 3;
}
/*------------------------- handle the last (probably partial) block */
/*
* "k[2]&0xffffff" actually reads beyond the end of the string, but
* then masks off the part it's not allowed to read. Because the
* string is aligned, the masked-off tail is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
#ifndef VALGRIND
switch (size) {
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
case 11: c += k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
case 10: c += k[2]&0xffff; b+=k[1]; a+=k[0]; break;
case 9: c += k[2]&0xff; b+=k[1]; a+=k[0]; break;
case 8: b += k[1]; a+=k[0]; break;
case 7: b += k[1]&0xffffff; a+=k[0]; break;
case 6: b += k[1]&0xffff; a+=k[0]; break;
case 5: b += k[1]&0xff; a+=k[0]; break;
case 4: a += k[0]; break;
case 3: a += k[0]&0xffffff; break;
case 2: a += k[0]&0xffff; break;
case 1: a += k[0]&0xff; break;
case 0: goto exit;
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch (size) {
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
case 11: c += ((uint32_t)k8[10])<<16; /* fallthrough */
case 10: c += ((uint32_t)k8[9])<<8; /* fallthrough */
case 9: c += k8[8]; /* fallthrough */
case 8: b += k[1]; a+=k[0]; break;
case 7: b += ((uint32_t)k8[6])<<16; /* fallthrough */
case 6: b += ((uint32_t)k8[5])<<8; /* fallthrough */
case 5: b += k8[4]; /* fallthrough */
case 4: a += k[0]; break;
case 3: a += ((uint32_t)k8[2])<<16; /* fallthrough */
case 2: a += ((uint32_t)k8[1])<<8; /* fallthrough */
case 1: a += k8[0]; break;
case 0: goto exit;
}
#endif /* !valgrind */
} 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 (size > (size_t)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);
_JLU3_MIX(a,b,c);
size -= 12;
k += 6;
}
/*------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch (size) {
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;
/* fallthrough */
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];
/* fallthrough */
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;
/* fallthrough */
case 6:
b += k[2];
a += k[0]+(((uint32_t)k[1])<<16);
break;
case 5:
b += k8[4];
/* fallthrough */
case 4:
a += k[0]+(((uint32_t)k[1])<<16);
break;
case 3:
a += ((uint32_t)k8[2])<<16;
/* fallthrough */
case 2:
a += k[0];
break;
case 1:
a += k8[0];
break;
case 0:
goto exit;
}
} 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 (size > (size_t)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;
_JLU3_MIX(a,b,c);
size -= 12;
k += 12;
}
/*---------------------------- last block: affect all 32 bits of (c) */
switch (size) {
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:
goto exit;
}
}
_JLU3_FINAL(a,b,c);
exit:
*pc = c;
*pb = b;
return;
}
#endif /* defined(_JLU3_jlu32lpair) */
#if defined(_JLU3_jlu32b)
uint32_t jlu32b(uint32_t h, const void *key, size_t size);
/*
* jlu32b():
* This is the same as jlu32w() on big-endian machines. It is different
* from jlu32l() on all machines. jlu32b() takes advantage of
* big-endian byte ordering.
*
* @param h the previous hash, or an arbitrary value
* @param *k the key, an array of uint8_t values
* @param size the size of the key
* @return the lookup3 hash
*/
uint32_t jlu32b(uint32_t h, const void *key, size_t size)
{
union { const void *ptr; size_t i; } u;
uint32_t a = _JLU3_INIT(h, size);
uint32_t b = a;
uint32_t c = a;
if (key == NULL)
return h;
u.ptr = key;
if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
#ifdef VALGRIND
const uint8_t *k8;
#endif
/*-- all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (size > 12) {
a += k[0];
b += k[1];
c += k[2];
_JLU3_MIX(a,b,c);
size -= 12;
k += 3;
}
/*------------------------- handle the last (probably partial) block */
/*
* "k[2]<<8" actually reads beyond the end of the string, but
* then shifts out the part it's not allowed to read. Because the
* string is aligned, the illegal read is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
#ifndef VALGRIND
switch (size) {
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
case 11: c += k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
case 10: c += k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
case 9: c += k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
case 8: b += k[1]; a+=k[0]; break;
case 7: b += k[1]&0xffffff00; a+=k[0]; break;
case 6: b += k[1]&0xffff0000; a+=k[0]; break;
case 5: b += k[1]&0xff000000; a+=k[0]; break;
case 4: a += k[0]; break;
case 3: a += k[0]&0xffffff00; break;
case 2: a += k[0]&0xffff0000; break;
case 1: a += k[0]&0xff000000; break;
case 0: goto exit;
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch (size) { /* all the case statements fall through */
case 12: c += k[2]; b+=k[1]; a+=k[0]; break;
case 11: c += ((uint32_t)k8[10])<<8; /* fallthrough */
case 10: c += ((uint32_t)k8[9])<<16; /* fallthrough */
case 9: c += ((uint32_t)k8[8])<<24; /* fallthrough */
case 8: b += k[1]; a+=k[0]; break;
case 7: b += ((uint32_t)k8[6])<<8; /* fallthrough */
case 6: b += ((uint32_t)k8[5])<<16; /* fallthrough */
case 5: b += ((uint32_t)k8[4])<<24; /* fallthrough */
case 4: a += k[0]; break;
case 3: a += ((uint32_t)k8[2])<<8; /* fallthrough */
case 2: a += ((uint32_t)k8[1])<<16; /* fallthrough */
case 1: a += ((uint32_t)k8[0])<<24; break;
case 0: goto exit;
}
#endif /* !VALGRIND */
} 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 (size > 12) {
a += ((uint32_t)k[0])<<24;
a += ((uint32_t)k[1])<<16;
a += ((uint32_t)k[2])<<8;
a += ((uint32_t)k[3]);
b += ((uint32_t)k[4])<<24;
b += ((uint32_t)k[5])<<16;
b += ((uint32_t)k[6])<<8;
b += ((uint32_t)k[7]);
c += ((uint32_t)k[8])<<24;
c += ((uint32_t)k[9])<<16;
c += ((uint32_t)k[10])<<8;
c += ((uint32_t)k[11]);
_JLU3_MIX(a,b,c);
size -= 12;
k += 12;
}
/*---------------------------- last block: affect all 32 bits of (c) */
switch (size) { /* all the case statements fall through */
case 12: c += k[11]; /* fallthrough */
case 11: c += ((uint32_t)k[10])<<8; /* fallthrough */
case 10: c += ((uint32_t)k[9])<<16; /* fallthrough */
case 9: c += ((uint32_t)k[8])<<24; /* fallthrough */
case 8: b += k[7]; /* fallthrough */
case 7: b += ((uint32_t)k[6])<<8; /* fallthrough */
case 6: b += ((uint32_t)k[5])<<16; /* fallthrough */
case 5: b += ((uint32_t)k[4])<<24; /* fallthrough */
case 4: a += k[3]; /* fallthrough */
case 3: a += ((uint32_t)k[2])<<8; /* fallthrough */
case 2: a += ((uint32_t)k[1])<<16; /* fallthrough */
case 1: a += ((uint32_t)k[0])<<24; /* fallthrough */
break;
case 0:
goto exit;
}
}
_JLU3_FINAL(a,b,c);
exit:
return c;
}
#endif /* defined(_JLU3_jlu32b) */
#if defined(_JLU3_SELFTEST)
/* used for timings */
static void driver1(void)
{
uint8_t buf[256];
uint32_t i;
uint32_t h=0;
time_t a,z;
time(&a);
for (i=0; i<256; ++i) buf[i] = 'x';
for (i=0; i<1; ++i) {
h = jlu32l(h, &buf[0], sizeof(buf[0]));
}
time(&z);
if (z-a > 0) printf("time %d %.8x\n", (int)(z-a), h);
}
/* check that every input bit changes every output bit half the time */
#define HASHSTATE 1
#define HASHLEN 1
#define MAXPAIR 60
#define MAXLEN 70
static void driver2(void)
{
uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
uint32_t x[HASHSTATE],y[HASHSTATE];
uint32_t hlen;
printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
for (hlen=0; hlen < MAXLEN; ++hlen) {
z=0;
for (i=0; i<hlen; ++i) { /*-------------- for each input byte, */
for (j=0; j<8; ++j) { /*--------------- for each input bit, */
for (m=1; m<8; ++m) { /*---- for several possible initvals, */
for (l=0; l<HASHSTATE; ++l)
e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);
/* check that every output bit is affected by that input bit */
for (k=0; k<MAXPAIR; k+=2) {
uint32_t finished=1;
/* keys have one bit different */
for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;}
/* have a and b be two keys differing in only one bit */
a[i] ^= (k<<j);
a[i] ^= (k>>(8-j));
c[0] = jlu32l(m, a, hlen);
b[i] ^= ((k+1)<<j);
b[i] ^= ((k+1)>>(8-j));
d[0] = jlu32l(m, b, hlen);
/* check every bit is 1, 0, set, and not set at least once */
for (l=0; l<HASHSTATE; ++l) {
e[l] &= (c[l]^d[l]);
f[l] &= ~(c[l]^d[l]);
g[l] &= c[l];
h[l] &= ~c[l];
x[l] &= d[l];
y[l] &= ~d[l];
if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0;
}
if (finished) break;
}
if (k>z) z=k;
if (k == MAXPAIR) {
printf("Some bit didn't change: ");
printf("%.8x %.8x %.8x %.8x %.8x %.8x ",
e[0],f[0],g[0],h[0],x[0],y[0]);
printf("i %u j %u m %u len %u\n", i, j, m, hlen);
}
if (z == MAXPAIR) goto done;
}
}
}
done:
if (z < MAXPAIR) {
printf("Mix success %2u bytes %2u initvals ",i,m);
printf("required %u trials\n", z/2);
}
}
printf("\n");
}
/* Check for reading beyond the end of the buffer and alignment problems */
static void driver3(void)
{
uint8_t buf[MAXLEN+20], *b;
uint32_t len;
uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
uint32_t h;
uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
uint32_t i;
uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
uint32_t j;
uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
uint32_t ref,x,y;
uint8_t *p;
uint32_t m = 13;
printf("Endianness. These lines should all be the same (for values filled in):\n");
printf("%.8x %.8x %.8x\n",
jlu32w(m, (const uint32_t *)q, (sizeof(q)-1)/4),
jlu32w(m, (const uint32_t *)q, (sizeof(q)-5)/4),
jlu32w(m, (const uint32_t *)q, (sizeof(q)-9)/4));
p = q;
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
p = &qq[1];
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
p = &qqq[2];
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
p = &qqqq[3];
printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
jlu32l(m, p, sizeof(q)-1), jlu32l(m, p, sizeof(q)-2),
jlu32l(m, p, sizeof(q)-3), jlu32l(m, p, sizeof(q)-4),
jlu32l(m, p, sizeof(q)-5), jlu32l(m, p, sizeof(q)-6),
jlu32l(m, p, sizeof(q)-7), jlu32l(m, p, sizeof(q)-8),
jlu32l(m, p, sizeof(q)-9), jlu32l(m, p, sizeof(q)-10),
jlu32l(m, p, sizeof(q)-11), jlu32l(m, p, sizeof(q)-12));
printf("\n");
for (h=0, b=buf+1; h<8; ++h, ++b) {
for (i=0; i<MAXLEN; ++i) {
len = i;
for (j=0; j<i; ++j)
*(b+j)=0;
/* these should all be equal */
m = 1;
ref = jlu32l(m, b, len);
*(b+i)=(uint8_t)~0;
*(b-1)=(uint8_t)~0;
x = jlu32l(m, b, len);
y = jlu32l(m, b, len);
if ((ref != x) || (ref != y))
printf("alignment error: %.8x %.8x %.8x %u %u\n",ref,x,y, h, i);
}
}
}
/* check for problems with nulls */
static void driver4(void)
{
uint8_t buf[1];
uint32_t h;
uint32_t i;
uint32_t state[HASHSTATE];
buf[0] = ~0;
for (i=0; i<HASHSTATE; ++i)
state[i] = 1;
printf("These should all be different\n");
h = 0;
for (i=0; i<8; ++i) {
h = jlu32l(h, buf, 0);
printf("%2ld 0-byte strings, hash is %.8x\n", (long)i, h);
}
}
int main(int argc, char ** argv)
{
driver1(); /* test that the key is hashed: used for timings */
driver2(); /* test that whole key is hashed thoroughly */
driver3(); /* test that nothing but the key is hashed */
driver4(); /* test hashing multiple buffers (all buffers are null) */
return 1;
}
#endif /* _JLU3_SELFTEST */

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
/** \file popt/popt.h
* \ingroup popt
/** @file
*/
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -13,45 +12,49 @@
#define POPT_OPTION_DEPTH 10
/** \ingroup popt
/**
* \name Arg type identifiers
*/
/*@{*/
#define POPT_ARG_NONE 0 /*!< no arg */
#define POPT_ARG_STRING 1 /*!< arg will be saved as string */
#define POPT_ARG_INT 2 /*!< arg will be converted to int */
#define POPT_ARG_LONG 3 /*!< arg will be converted to long */
#define POPT_ARG_INCLUDE_TABLE 4 /*!< arg points to table */
#define POPT_ARG_CALLBACK 5 /*!< table-wide callback... must be
#define POPT_ARG_NONE 0U /*!< no arg */
#define POPT_ARG_STRING 1U /*!< arg will be saved as string */
#define POPT_ARG_INT 2U /*!< arg ==> int */
#define POPT_ARG_LONG 3U /*!< arg ==> long */
#define POPT_ARG_INCLUDE_TABLE 4U /*!< arg points to table */
#define POPT_ARG_CALLBACK 5U /*!< table-wide callback... must be
set first in table; arg points
to callback, descrip points to
callback data to pass */
#define POPT_ARG_INTL_DOMAIN 6 /*!< set the translation domain
#define POPT_ARG_INTL_DOMAIN 6U /*!< set the translation domain
for this table and any
included tables; arg points
to the domain string */
#define POPT_ARG_VAL 7 /*!< arg should take value val */
#define POPT_ARG_FLOAT 8 /*!< arg will be converted to float */
#define POPT_ARG_DOUBLE 9 /*!< arg will be converted to double */
#define POPT_ARG_VAL 7U /*!< arg should take value val */
#define POPT_ARG_FLOAT 8U /*!< arg ==> float */
#define POPT_ARG_DOUBLE 9U /*!< arg ==> double */
#define POPT_ARG_LONGLONG 10U /*!< arg ==> long long */
#define POPT_ARG_MASK 0x0000FFFF
/*@}*/
#define POPT_ARG_MAINCALL (16U+11U) /*!< EXPERIMENTAL: return (*arg) (argc, argv) */
#define POPT_ARG_ARGV 12U /*!< dupe'd arg appended to realloc'd argv array. */
#define POPT_ARG_SHORT 13U /*!< arg ==> short */
#define POPT_ARG_BITSET (16U+14U) /*!< arg ==> bit set */
/** \ingroup popt
#define POPT_ARG_MASK 0x000000FFU
#define POPT_GROUP_MASK 0x0000FF00U
/**
* \name Arg modifiers
*/
/*@{*/
#define POPT_ARGFLAG_ONEDASH 0x80000000 /*!< allow -longoption */
#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /*!< don't show in help/usage */
#define POPT_ARGFLAG_STRIP 0x20000000 /*!< strip this arg from argv(only applies to long args) */
#define POPT_ARGFLAG_OPTIONAL 0x10000000 /*!< arg may be missing */
#define POPT_ARGFLAG_ONEDASH 0x80000000U /*!< allow -longoption */
#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000U /*!< don't show in help/usage */
#define POPT_ARGFLAG_STRIP 0x20000000U /*!< strip this arg from argv(only applies to long args) */
#define POPT_ARGFLAG_OPTIONAL 0x10000000U /*!< arg may be missing */
#define POPT_ARGFLAG_OR 0x08000000 /*!< arg will be or'ed */
#define POPT_ARGFLAG_NOR 0x09000000 /*!< arg will be nor'ed */
#define POPT_ARGFLAG_AND 0x04000000 /*!< arg will be and'ed */
#define POPT_ARGFLAG_NAND 0x05000000 /*!< arg will be nand'ed */
#define POPT_ARGFLAG_XOR 0x02000000 /*!< arg will be xor'ed */
#define POPT_ARGFLAG_NOT 0x01000000 /*!< arg will be negated */
#define POPT_ARGFLAG_OR 0x08000000U /*!< arg will be or'ed */
#define POPT_ARGFLAG_NOR 0x09000000U /*!< arg will be nor'ed */
#define POPT_ARGFLAG_AND 0x04000000U /*!< arg will be and'ed */
#define POPT_ARGFLAG_NAND 0x05000000U /*!< arg will be nand'ed */
#define POPT_ARGFLAG_XOR 0x02000000U /*!< arg will be xor'ed */
#define POPT_ARGFLAG_NOT 0x01000000U /*!< arg will be negated */
#define POPT_ARGFLAG_LOGICALOPS \
(POPT_ARGFLAG_OR|POPT_ARGFLAG_AND|POPT_ARGFLAG_XOR)
@@ -60,158 +63,126 @@
#define POPT_BIT_CLR (POPT_ARG_VAL|POPT_ARGFLAG_NAND)
/*!< clear arg bit(s) */
#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 /*!< show default value in --help */
#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000U /*!< show default value in --help */
#define POPT_ARGFLAG_RANDOM 0x00400000U /*!< random value in [1,arg] */
#define POPT_ARGFLAG_TOGGLE 0x00200000U /*!< permit --[no]opt prefix toggle */
/*@}*/
/** \ingroup popt
/**
* \name Callback modifiers
*/
/*@{*/
#define POPT_CBFLAG_PRE 0x80000000 /*!< call the callback before parse */
#define POPT_CBFLAG_POST 0x40000000 /*!< call the callback after parse */
#define POPT_CBFLAG_INC_DATA 0x20000000 /*!< use data from the include line,
#define POPT_CBFLAG_PRE 0x80000000U /*!< call the callback before parse */
#define POPT_CBFLAG_POST 0x40000000U /*!< call the callback after parse */
#define POPT_CBFLAG_INC_DATA 0x20000000U /*!< use data from the include line,
not the subtable */
#define POPT_CBFLAG_SKIPOPTION 0x10000000 /*!< don't callback with option */
#define POPT_CBFLAG_CONTINUE 0x08000000 /*!< continue callbacks with option */
/*@}*/
#define POPT_CBFLAG_SKIPOPTION 0x10000000U /*!< don't callback with option */
#define POPT_CBFLAG_CONTINUE 0x08000000U /*!< continue callbacks with option */
/** \ingroup popt
/**
* \name Error return values
*/
/*@{*/
#define POPT_ERROR_NOARG -10 /*!< missing argument */
#define POPT_ERROR_BADOPT -11 /*!< unknown option */
#define POPT_ERROR_UNWANTEDARG -12 /*!< option does not take an argument */
#define POPT_ERROR_OPTSTOODEEP -13 /*!< aliases nested too deeply */
#define POPT_ERROR_BADQUOTE -15 /*!< error in paramter quoting */
#define POPT_ERROR_BADQUOTE -15 /*!< error in parameter quoting */
#define POPT_ERROR_ERRNO -16 /*!< errno set, use strerror(errno) */
#define POPT_ERROR_BADNUMBER -17 /*!< invalid numeric value */
#define POPT_ERROR_OVERFLOW -18 /*!< number too large or too small */
#define POPT_ERROR_BADOPERATION -19 /*!< mutually exclusive logical operations requested */
#define POPT_ERROR_NULLARG -20 /*!< opt->arg should not be NULL */
#define POPT_ERROR_MALLOC -21 /*!< memory allocation failed */
/*@}*/
#define POPT_ERROR_BADCONFIG -22 /*!< config file failed sanity test */
/** \ingroup popt
/**
* \name poptBadOption() flags
*/
/*@{*/
#define POPT_BADOPTION_NOALIAS (1 << 0) /*!< don't go into an alias */
/*@}*/
#define POPT_BADOPTION_NOALIAS (1U << 0) /*!< don't go into an alias */
/** \ingroup popt
/**
* \name poptGetContext() flags
*/
/*@{*/
#define POPT_CONTEXT_NO_EXEC (1 << 0) /*!< ignore exec expansions */
#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /*!< pay attention to argv[0] */
#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /*!< options can't follow args */
#define POPT_CONTEXT_ARG_OPTS (1 << 4) /*!< return args as options with value 0 */
/*@}*/
#define POPT_CONTEXT_NO_EXEC (1U << 0) /*!< ignore exec expansions */
#define POPT_CONTEXT_KEEP_FIRST (1U << 1) /*!< pay attention to argv[0] */
#define POPT_CONTEXT_POSIXMEHARDER (1U << 2) /*!< options can't follow args */
#define POPT_CONTEXT_ARG_OPTS (1U << 4) /*!< return args as options with value 0 */
/** \ingroup popt
/**
*/
struct poptOption {
/*@observer@*/ /*@null@*/
const char * longName; /*!< may be NULL */
char shortName; /*!< may be NUL */
int argInfo;
/*@shared@*/ /*@null@*/
char shortName; /*!< may be '\0' */
unsigned int argInfo; /*!< type of argument expected after the option */
void * arg; /*!< depends on argInfo */
int val; /*!< 0 means don't return, just update flag */
/*@observer@*/ /*@null@*/
int val; /*!< 0 means don't return, just update arg */
const char * descrip; /*!< description for autohelp -- may be NULL */
/*@observer@*/ /*@null@*/
const char * argDescrip; /*!< argument description for autohelp */
const char * argDescrip; /*!< argument description for autohelp -- may be NULL */
};
/** \ingroup popt
/**
* A popt alias argument for poptAddAlias().
*/
struct poptAlias {
/*@owned@*/ /*@null@*/
const char * longName; /*!< may be NULL */
char shortName; /*!< may be NUL */
int argc;
/*@owned@*/
const char ** argv; /*!< must be free()able */
};
/** \ingroup popt
/**
* A popt alias or exec argument for poptAddItem().
*/
/*@-exporttype@*/
typedef struct poptItem_s {
struct poptOption option; /*!< alias/exec name(s) and description. */
int argc; /*!< (alias) no. of args. */
/*@owned@*/
const char ** argv; /*!< (alias) args, must be free()able. */
} * poptItem;
/*@=exporttype@*/
/** \ingroup popt
/**
* \name Auto-generated help/usage
*/
/*@{*/
/**
* Empty table marker to enable displaying popt alias/exec options.
*/
/*@-exportvar@*/
/*@unchecked@*/ /*@observer@*/
extern struct poptOption poptAliasOptions[];
/*@=exportvar@*/
#define POPT_AUTOALIAS { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptAliasOptions, \
0, "Options implemented via popt alias/exec:", NULL },
/**
* Auto help table options.
*/
/*@-exportvar@*/
/*@unchecked@*/ /*@observer@*/
extern struct poptOption poptHelpOptions[];
/*@=exportvar@*/
/*@-exportvar@*/
/*@unchecked@*/ /*@observer@*/
extern struct poptOption * poptHelpOptionsI18N;
/*@=exportvar@*/
#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
0, "Help options:", NULL },
#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
/*@}*/
#define POPT_TABLEEND { NULL, '\0', 0, NULL, 0, NULL, NULL }
/** \ingroup popt
/**
*/
/*@-exporttype@*/
typedef /*@abstract@*/ struct poptContext_s * poptContext;
/*@=exporttype@*/
typedef struct poptContext_s * poptContext;
/** \ingroup popt
/**
*/
#ifndef __cplusplus
/*@-exporttype -typeuse@*/
typedef struct poptOption * poptOption;
/*@=exporttype =typeuse@*/
#endif
/*@-exportconst@*/
/**
*/
enum poptCallbackReason {
POPT_CALLBACK_REASON_PRE = 0,
POPT_CALLBACK_REASON_POST = 1,
POPT_CALLBACK_REASON_OPTION = 2
};
/*@=exportconst@*/
#ifdef __cplusplus
extern "C" {
#endif
/*@-type@*/
/** \ingroup popt
/**
* Table callback prototype.
* @param con context
* @param reason reason for callback
@@ -221,13 +192,18 @@ extern "C" {
*/
typedef void (*poptCallbackType) (poptContext con,
enum poptCallbackReason reason,
/*@null@*/ const struct poptOption * opt,
/*@null@*/ const char * arg,
/*@null@*/ const void * data)
/*@globals internalState @*/
/*@modifies internalState @*/;
const struct poptOption * opt,
const char * arg,
const void * data);
/** \ingroup popt
/**
* Destroy context.
* @param con context
* @return NULL always
*/
poptContext poptFreeContext( poptContext con);
/**
* Initialize popt context.
* @param name context name (usually argv[0] program name)
* @param argc no. of arguments
@@ -236,97 +212,90 @@ typedef void (*poptCallbackType) (poptContext con,
* @param flags or'd POPT_CONTEXT_* bits
* @return initialized popt context
*/
/*@only@*/ /*@null@*/
poptContext poptGetContext(
/*@dependent@*/ /*@keep@*/ const char * name,
int argc, /*@dependent@*/ /*@keep@*/ const char ** argv,
/*@dependent@*/ /*@keep@*/ const struct poptOption * options,
int flags)
/*@*/;
const char * name,
int argc, const char ** argv,
const struct poptOption * options,
unsigned int flags);
/** \ingroup popt
/**
* Destroy context (alternative implementation).
* @param con context
* @return NULL always
*/
poptContext poptFini( poptContext con);
/**
* Initialize popt context (alternative implementation).
* This routine does poptGetContext() and then poptReadConfigFiles().
* @param argc no. of arguments
* @param argv argument array
* @param options address of popt option table
* @param configPaths colon separated file path(s) to read.
* @return initialized popt context (NULL on error).
*/
poptContext poptInit(int argc, const char ** argv,
const struct poptOption * options,
const char * configPaths);
/**
* Reinitialize popt context.
* @param con context
*/
/*@unused@*/
void poptResetContext(/*@null@*/poptContext con)
/*@modifies con @*/;
void poptResetContext(poptContext con);
/** \ingroup popt
/**
* Return value of next option found.
* @param con context
* @return next option val, -1 on last item, POPT_ERROR_* on error
*/
int poptGetNextOpt(/*@null@*/poptContext con)
/*@globals fileSystem, internalState @*/
/*@modifies con, fileSystem, internalState @*/;
int poptGetNextOpt(poptContext con);
/** \ingroup popt
/**
* Return next option argument (if any).
* @param con context
* @return option argument, NULL if no argument is available
*/
/*@observer@*/ /*@null@*/ /*@unused@*/
const char * poptGetOptArg(/*@null@*/poptContext con)
/*@modifies con @*/;
char * poptGetOptArg(poptContext con);
/** \ingroup popt
/**
* Return next argument.
* @param con context
* @return next argument, NULL if no argument is available
*/
/*@observer@*/ /*@null@*/ /*@unused@*/
const char * poptGetArg(/*@null@*/poptContext con)
/*@modifies con @*/;
const char * poptGetArg(poptContext con);
/** \ingroup popt
/**
* Peek at current argument.
* @param con context
* @return current argument, NULL if no argument is available
*/
/*@observer@*/ /*@null@*/ /*@unused@*/
const char * poptPeekArg(/*@null@*/poptContext con)
/*@*/;
const char * poptPeekArg(poptContext con);
/** \ingroup popt
/**
* Return remaining arguments.
* @param con context
* @return argument array, NULL terminated
*/
/*@observer@*/ /*@null@*/
const char ** poptGetArgs(/*@null@*/poptContext con)
/*@modifies con @*/;
const char ** poptGetArgs(poptContext con);
/** \ingroup popt
/**
* Return the option which caused the most recent error.
* @param con context
* @param flags
* @return offending option
*/
/*@observer@*/
const char * poptBadOption(/*@null@*/poptContext con, int flags)
/*@*/;
const char * poptBadOption(poptContext con, unsigned int flags);
/** \ingroup popt
* Destroy context.
* @param con context
* @return NULL always
*/
/*@null@*/
poptContext poptFreeContext( /*@only@*/ /*@null@*/ poptContext con)
/*@modifies con @*/;
/** \ingroup popt
/**
* Add arguments to context.
* @param con context
* @param argv argument array, NULL terminated
* @return 0 on success, POPT_ERROR_OPTSTOODEEP on failure
*/
/*@unused@*/
int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
/*@modifies con @*/;
int poptStuffArgs(poptContext con, const char ** argv);
/** \ingroup popt
/**
* Add alias to context.
* @todo Pass alias by reference, not value.
* @deprecated Use poptAddItem instead.
@@ -335,44 +304,64 @@ int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv)
* @param flags (unused)
* @return 0 on success
*/
/*@unused@*/
int poptAddAlias(poptContext con, struct poptAlias alias, int flags)
/*@modifies con @*/;
int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
/** \ingroup popt
/**
* Add alias/exec item to context.
* @param con context
* @param newItem alias/exec item to add
* @param flags 0 for alias, 1 for exec
* @return 0 on success
*/
int poptAddItem(poptContext con, poptItem newItem, int flags)
/*@modifies con @*/;
int poptAddItem(poptContext con, poptItem newItem, int flags);
/** \ingroup popt
/**
* Test path/file for config file sanity (regular file, permissions etc)
* @param fn file name
* @return 1 on OK, 0 on NOTOK.
*/
int poptSaneFile(const char * fn);
/**
* Read a file into a buffer.
* @param fn file name
* @retval *bp buffer (malloc'd) (or NULL)
* @retval *nbp no. of bytes in buffer (including final NUL) (or NULL)
* @param flags 1 to trim escaped newlines
* return 0 on success
*/
int poptReadFile(const char * fn, char ** bp,
size_t * nbp, int flags);
#define POPT_READFILE_TRIMNEWLINES 1
/**
* Read configuration file.
* @param con context
* @param fn file name to read
* @return 0 on success, POPT_ERROR_ERRNO on failure
*/
int poptReadConfigFile(poptContext con, const char * fn)
/*@globals errno, fileSystem, internalState @*/
/*@modifies con->execs, con->numExecs,
errno, fileSystem, internalState @*/;
int poptReadConfigFile(poptContext con, const char * fn);
/** \ingroup popt
/**
* Read configuration file(s).
* Colon separated files to read, looping over poptReadConfigFile().
* Note that an '@' character preceding a path in the list will
* also perform additional sanity checks on the file before reading.
* @param con context
* @param paths colon separated file name(s) to read
* @return 0 on success, POPT_ERROR_BADCONFIG on failure
*/
int poptReadConfigFiles(poptContext con, const char * paths);
/**
* Read default configuration from /etc/popt and $HOME/.popt.
* @param con context
* @param useEnv (unused)
* @return 0 on success, POPT_ERROR_ERRNO on failure
*/
/*@unused@*/
int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
/*@globals fileSystem, internalState @*/
/*@modifies con->execs, con->numExecs,
fileSystem, internalState @*/;
int poptReadDefaultConfig(poptContext con, int useEnv);
/** \ingroup popt
/**
* Duplicate an argument array.
* @note: The argument array is malloc'd as a single area, so only argv must
* be free'd.
@@ -383,12 +372,11 @@ int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv)
* @retval argvPtr address of returned argument array
* @return 0 on success, POPT_ERROR_NOARG on failure
*/
int poptDupArgv(int argc, /*@null@*/ const char **argv,
/*@null@*/ /*@out@*/ int * argcPtr,
/*@null@*/ /*@out@*/ const char *** argvPtr)
/*@modifies *argcPtr, *argvPtr @*/;
int poptDupArgv(int argc, const char **argv,
int * argcPtr,
const char *** argvPtr);
/** \ingroup popt
/**
* Parse a string into an argument array.
* The parse allows ', ", and \ quoting, but ' is treated the same as " and
* both may include \ quotes.
@@ -400,10 +388,9 @@ int poptDupArgv(int argc, /*@null@*/ const char **argv,
* @retval argvPtr address of returned argument array
*/
int poptParseArgvString(const char * s,
/*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr)
/*@modifies *argcPtr, *argvPtr @*/;
int * argcPtr, const char *** argvPtr);
/** \ingroup popt
/**
* Parses an input configuration file and returns an string that is a
* command line. For use with popt. You must free the return value when done.
*
@@ -418,8 +405,8 @@ bla=bla
this_is = fdsafdas
bad_line=
reall bad line
reall bad line = again
really bad line
really bad line = again
5555= 55555
test = with lots of spaces
\endverbatim
@@ -449,83 +436,82 @@ this_is = fdsafdas
* @return 0 on success
* @see poptParseArgvString
*/
/*@-fcnuse@*/
int poptConfigFileToString(FILE *fp, /*@out@*/ char ** argstrp, int flags)
/*@globals fileSystem @*/
/*@modifies *fp, *argstrp, fileSystem @*/;
/*@=fcnuse@*/
int poptConfigFileToString(FILE *fp, char ** argstrp, int flags);
/** \ingroup popt
/**
* Return formatted error string for popt failure.
* @param error popt error
* @return error string
*/
/*@observer@*/
const char * poptStrerror(const int error)
/*@*/;
const char * poptStrerror(const int error);
/** \ingroup popt
/**
* Limit search for executables.
* @param con context
* @param path single path to search for executables
* @param allowAbsolute absolute paths only?
*/
/*@unused@*/
void poptSetExecPath(poptContext con, const char * path, int allowAbsolute)
/*@modifies con @*/;
void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
/** \ingroup popt
/**
* Print detailed description of options.
* @param con context
* @param fp ouput file handle
* @param fp output file handle
* @param flags (unused)
*/
void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
/*@globals fileSystem @*/
/*@modifies *fp, fileSystem @*/;
void poptPrintHelp(poptContext con, FILE * fp, int flags);
/** \ingroup popt
/**
* Print terse description of options.
* @param con context
* @param fp ouput file handle
* @param fp output file handle
* @param flags (unused)
*/
void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
/*@globals fileSystem @*/
/*@modifies *fp, fileSystem @*/;
void poptPrintUsage(poptContext con, FILE * fp, int flags);
/** \ingroup popt
/**
* Provide text to replace default "[OPTION...]" in help/usage output.
* @param con context
* @param text replacement text
*/
/*@-fcnuse@*/
void poptSetOtherOptionHelp(poptContext con, const char * text)
/*@modifies con @*/;
/*@=fcnuse@*/
void poptSetOtherOptionHelp(poptContext con, const char * text);
/** \ingroup popt
/**
* Return argv[0] from context.
* @param con context
* @return argv[0]
*/
/*@-fcnuse@*/
/*@observer@*/
const char * poptGetInvocationName(poptContext con)
/*@*/;
/*@=fcnuse@*/
const char * poptGetInvocationName(poptContext con);
/** \ingroup popt
/**
* Shuffle argv pointers to remove stripped args, returns new argc.
* @param con context
* @param argc no. of args
* @param argv arg vector
* @return new argc
*/
/*@-fcnuse@*/
int poptStrippedArgv(poptContext con, int argc, char ** argv)
/*@modifies *argv @*/;
/*@=fcnuse@*/
int poptStrippedArgv(poptContext con, int argc, char ** argv);
/**
* Add a string to an argv array.
* @retval *argvp argv array
* @param argInfo (unused)
* @param val string arg to add (using strdup)
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
int poptSaveString(const char *** argvp, unsigned int argInfo,
const char * val);
/**
* Save a long long, performing logical operation with value.
* @warning Alignment check may be too strict on certain platorms.
* @param arg integer pointer, aligned on int boundary.
* @param argInfo logical operation (see POPT_ARGFLAG_*)
* @param aLongLong value to use
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
int poptSaveLongLong(long long * arg, unsigned int argInfo,
long long aLongLong);
/**
* Save a long, performing logical operation with value.
@@ -535,12 +521,17 @@ int poptStrippedArgv(poptContext con, int argc, char ** argv)
* @param aLong value to use
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
/*@-incondefs@*/
/*@unused@*/
int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
/*@modifies *arg @*/
/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
/*@=incondefs@*/
int poptSaveLong(long * arg, unsigned int argInfo, long aLong);
/**
* Save a short integer, performing logical operation with value.
* @warning Alignment check may be too strict on certain platorms.
* @param arg short pointer, aligned on short boundary.
* @param argInfo logical operation (see POPT_ARGFLAG_*)
* @param aLong value to use
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
int poptSaveShort(short * arg, unsigned int argInfo, long aLong);
/**
* Save an integer, performing logical operation with value.
@@ -550,14 +541,40 @@ int poptSaveLong(/*@null@*/ long * arg, int argInfo, long aLong)
* @param aLong value to use
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
/*@-incondefs@*/
/*@unused@*/
int poptSaveInt(/*@null@*/ int * arg, int argInfo, long aLong)
/*@modifies *arg @*/
/*@requires maxSet(arg) >= 0 /\ maxRead(arg) == 0 @*/;
/*@=incondefs@*/
int poptSaveInt(int * arg, unsigned int argInfo, long aLong);
/* The bit set typedef. */
typedef struct poptBits_s {
unsigned int bits[1];
} * poptBits;
#define _POPT_BITS_N 1024U /*!< estimated population */
#define _POPT_BITS_M ((3U * _POPT_BITS_N) / 2U)
#define _POPT_BITS_K 16U /*!< no. of linear hash combinations */
extern unsigned int _poptBitsN;
extern unsigned int _poptBitsM;
extern unsigned int _poptBitsK;
int poptBitsAdd(poptBits bits, const char * s);
int poptBitsChk(poptBits bits, const char * s);
int poptBitsClr(poptBits bits);
int poptBitsDel(poptBits bits, const char * s);
int poptBitsIntersect(poptBits * ap, const poptBits b);
int poptBitsUnion(poptBits * ap, const poptBits b);
int poptBitsArgs(poptContext con, poptBits * ap);
/**
* Save a string into a bit set (experimental).
* @retval *bits bit set (lazily malloc'd if NULL)
* @param argInfo logical operation (see POPT_ARGFLAG_*)
* @param s string to add to bit set
* @return 0 on success, POPT_ERROR_NULLARG/POPT_ERROR_BADOPERATION
*/
int poptSaveBits(poptBits * bitsp, unsigned int argInfo,
const char * s);
/*@=type@*/
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/** \ingroup popt
* \file popt/poptconfig.c
* @file
*/
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -8,54 +8,300 @@
#include "system.h"
#include "poptint.h"
/*@access poptContext @*/
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
static void configLine(poptContext con, char * line)
/*@modifies con @*/
#if defined(HAVE_FNMATCH_H)
#include <fnmatch.h>
#endif
#if defined(HAVE_GLOB_H)
#include <glob.h>
#if !defined(HAVE_GLOB_PATTERN_P)
/* Return nonzero if PATTERN contains any metacharacters.
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
static int
glob_pattern_p (const char * pattern, int quote)
{
size_t nameLength;
const char * p;
int open = 0;
for (p = pattern; *p != '\0'; ++p)
switch (*p) {
case '?':
case '*':
return 1;
break;
case '\\':
if (quote && p[1] != '\0')
++p;
break;
case '[':
open = 1;
break;
case ']':
if (open)
return 1;
break;
}
return 0;
}
#endif /* !defined(__GLIBC__) */
static int poptGlobFlags = 0;
static int poptGlob_error(UNUSED(const char * epath),
UNUSED(int eerrno))
{
return 1;
}
#endif /* HAVE_GLOB_H */
/**
* Return path(s) from a glob pattern.
* @param con context
* @param pattern glob pattern
* @retval *acp no. of paths
* @retval *avp array of paths
* @return 0 on success
*/
static int poptGlob(UNUSED(poptContext con), const char * pattern,
int * acp, const char *** avp)
{
const char * pat = pattern;
int rc = 0; /* assume success */
#if defined(HAVE_GLOB_H)
if (glob_pattern_p(pat, 0)) {
glob_t _g, *pglob = &_g;
if (!(rc = glob(pat, poptGlobFlags, poptGlob_error, pglob))) {
if (acp) {
*acp = (int) pglob->gl_pathc;
pglob->gl_pathc = 0;
}
if (avp) {
*avp = (const char **) pglob->gl_pathv;
pglob->gl_pathv = NULL;
}
globfree(pglob);
} else if (rc == GLOB_NOMATCH) {
*avp = NULL;
*acp = 0;
rc = 0;
} else
rc = POPT_ERROR_ERRNO;
} else
#endif /* HAVE_GLOB_H */
{
if (acp)
*acp = 1;
if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
(*avp)[0] = xstrdup(pat);
}
return rc;
}
int poptSaneFile(const char * fn)
{
struct stat sb;
if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
return 0;
if (stat(fn, &sb) == -1)
return 0;
if (!S_ISREG(sb.st_mode))
return 0;
if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
return 0;
return 1;
}
int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
{
int fdno;
char * b = NULL;
off_t nb = 0;
char * s, * t, * se;
int rc = POPT_ERROR_ERRNO; /* assume failure */
fdno = open(fn, O_RDONLY);
if (fdno < 0)
goto exit;
if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
|| (uintmax_t)nb >= SIZE_MAX
|| lseek(fdno, 0, SEEK_SET) == (off_t)-1
|| (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
|| read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
{
int oerrno = errno;
(void) close(fdno);
if (nb != (off_t)-1 && (uintmax_t)nb >= SIZE_MAX)
errno = -EOVERFLOW;
else
errno = oerrno;
goto exit;
}
if (close(fdno) == -1)
goto exit;
if (b == NULL) {
rc = POPT_ERROR_MALLOC;
goto exit;
}
rc = 0;
/* Trim out escaped newlines. */
if (flags & POPT_READFILE_TRIMNEWLINES)
{
for (t = b, s = b, se = b + nb; *s && s < se; s++) {
switch (*s) {
case '\\':
if (s[1] == '\n') {
s++;
continue;
}
/* fallthrough */
default:
*t++ = *s;
break;
}
}
*t++ = '\0';
nb = (off_t)(t - b);
}
exit:
if (rc != 0) {
if (b)
free(b);
b = NULL;
nb = 0;
}
if (bp)
*bp = b;
else if (b)
free(b);
if (nbp)
*nbp = (size_t)nb;
return rc;
}
/**
* Check for application match.
* @param con context
* @param s config application name
* return 0 if config application matches
*/
static int configAppMatch(poptContext con, const char * s)
{
int rc = 1;
if (con->appName == NULL) /* XXX can't happen. */
return rc;
#if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
if (glob_pattern_p(s, 1)) {
static int flags = FNM_PATHNAME | FNM_PERIOD;
#ifdef FNM_EXTMATCH
flags |= FNM_EXTMATCH;
#endif
rc = fnmatch(s, con->appName, flags);
} else
#endif
rc = strcmp(s, con->appName);
return rc;
}
static int poptConfigLine(poptContext con, char * line)
{
char *b = NULL;
size_t nb = 0;
char * se = line;
const char * appName;
const char * entryType;
const char * opt;
poptItem item = (poptItem) alloca(sizeof(*item));
struct poptItem_s item_buf;
poptItem item = &item_buf;
int i, j;
int rc = POPT_ERROR_BADCONFIG;
if (con->appName == NULL)
return;
nameLength = strlen(con->appName);
goto exit;
/*@-boundswrite@*/
memset(item, 0, sizeof(*item));
if (strncmp(line, con->appName, nameLength)) return;
appName = se;
while (*se != '\0' && !_isspaceptr(se)) se++;
if (*se == '\0')
goto exit;
else
*se++ = '\0';
line += nameLength;
if (*line == '\0' || !isSpace(line)) return;
if (configAppMatch(con, appName)) goto exit;
while (*line != '\0' && isSpace(line)) line++;
entryType = line;
while (*line == '\0' || !isSpace(line)) line++;
*line++ = '\0';
while (*se != '\0' && _isspaceptr(se)) se++;
entryType = se;
while (*se != '\0' && !_isspaceptr(se)) se++;
if (*se != '\0') *se++ = '\0';
while (*line != '\0' && isSpace(line)) line++;
if (*line == '\0') return;
opt = line;
while (*line == '\0' || !isSpace(line)) line++;
*line++ = '\0';
while (*se != '\0' && _isspaceptr(se)) se++;
if (*se == '\0') goto exit;
opt = se;
while (*se != '\0' && !_isspaceptr(se)) se++;
if (opt[0] == '-' && *se == '\0') goto exit;
if (*se != '\0') *se++ = '\0';
while (*line != '\0' && isSpace(line)) line++;
if (*line == '\0') return;
while (*se != '\0' && _isspaceptr(se)) se++;
if (opt[0] == '-' && *se == '\0') goto exit;
/*@-temptrans@*/ /* FIX: line alias is saved */
if (opt[0] == '-' && opt[1] == '-')
item->option.longName = opt + 2;
else if (opt[0] == '-' && opt[2] == '\0')
item->option.shortName = opt[1];
/*@=temptrans@*/
else {
const char * fn = opt;
if (poptParseArgvString(line, &item->argc, &item->argv)) return;
/* XXX handle globs and directories in fn? */
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
goto exit;
if (b == NULL || nb == 0)
goto exit;
/* Append remaining text to the interpolated file option text. */
if (*se != '\0') {
size_t nse = strlen(se) + 1;
if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
goto exit;
(void) stpcpy( stpcpy(&b[nb-1], " "), se);
nb += nse;
}
se = b;
/* Use the basename of the path as the long option name. */
{ const char * longName = strrchr(fn, '/');
if (longName != NULL)
longName++;
else
longName = fn;
if (longName == NULL) /* XXX can't happen. */
goto exit;
/* Single character basenames are treated as short options. */
if (longName[1] != '\0')
item->option.longName = longName;
else
item->option.shortName = longName[0];
}
}
if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
/*@-modobserver@*/
item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
for (i = 0, j = 0; i < item->argc; i++, j++) {
const char * f;
@@ -81,103 +327,183 @@ static void configLine(poptContext con, char * line)
item->argv[j] = NULL;
item->argc = j;
}
/*@=modobserver@*/
/*@=boundswrite@*/
/*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
if (!strcmp(entryType, "alias"))
(void) poptAddItem(con, item, 0);
rc = poptAddItem(con, item, 0);
else if (!strcmp(entryType, "exec"))
(void) poptAddItem(con, item, 1);
/*@=nullstate@*/
rc = poptAddItem(con, item, 1);
exit:
rc = 0; /* XXX for now, always return success */
if (b)
free(b);
return rc;
}
/*@=compmempass@*/
int poptReadConfigFile(poptContext con, const char * fn)
{
const char * file, * chptr, * end;
char * buf;
/*@dependent@*/ char * dst;
int fd, rc;
off_t fileLength;
fd = open(fn, O_RDONLY);
if (fd < 0)
return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO);
fileLength = lseek(fd, 0, SEEK_END);
if (fileLength == -1 || lseek(fd, 0, 0) == -1) {
rc = errno;
(void) close(fd);
errno = rc;
return POPT_ERROR_ERRNO;
}
file = alloca(fileLength + 1);
if (read(fd, (char *)file, fileLength) != fileLength) {
rc = errno;
(void) close(fd);
errno = rc;
return POPT_ERROR_ERRNO;
}
if (close(fd) == -1)
return POPT_ERROR_ERRNO;
/*@-boundswrite@*/
dst = buf = alloca(fileLength + 1);
chptr = file;
end = (file + fileLength);
/*@-infloops@*/ /* LCL: can't detect chptr++ */
while (chptr < end) {
switch (*chptr) {
case '\n':
*dst = '\0';
dst = buf;
while (*dst && isSpace(dst)) dst++;
if (*dst && *dst != '#')
configLine(con, dst);
chptr++;
/*@switchbreak@*/ break;
case '\\':
*dst++ = *chptr++;
if (chptr < end) {
if (*chptr == '\n')
dst--, chptr++;
/* \ at the end of a line does not insert a \n */
else
*dst++ = *chptr++;
}
/*@switchbreak@*/ break;
default:
*dst++ = *chptr++;
/*@switchbreak@*/ break;
}
}
/*@=infloops@*/
/*@=boundswrite@*/
return 0;
}
int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
{
char * fn, * home;
char * b = NULL, *be;
size_t nb = 0;
const char *se;
char *t = NULL, *te;
int rc;
if (con->appName == NULL) return 0;
rc = poptReadConfigFile(con, "/etc/popt");
if (rc) return rc;
if ((home = getenv("HOME"))) {
size_t bufsize = strlen(home) + 20;
fn = alloca(bufsize);
if (fn == NULL) return 0;
snprintf(fn, bufsize, "%s/.popt", home);
rc = poptReadConfigFile(con, fn);
if (rc) return rc;
if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
return (errno == ENOENT ? 0 : rc);
if (b == NULL || nb == 0) {
rc = POPT_ERROR_BADCONFIG;
goto exit;
}
return 0;
if ((t = malloc(nb + 1)) == NULL)
goto exit;
te = t;
be = (b + nb);
for (se = b; se < be; se++) {
switch (*se) {
case '\n':
*te = '\0';
te = t;
while (*te && _isspaceptr(te)) te++;
if (*te && *te != '#')
if ((rc = poptConfigLine(con, te)) != 0)
goto exit;
break;
case '\\':
*te = *se++;
/* \ at the end of a line does not insert a \n */
if (se < be && *se != '\n') {
te++;
*te++ = *se;
}
break;
default:
*te++ = *se;
break;
}
}
rc = 0;
exit:
free(t);
if (b)
free(b);
return rc;
}
int poptReadConfigFiles(poptContext con, const char * paths)
{
char * buf = (paths ? xstrdup(paths) : NULL);
const char * p;
char * pe;
int rc = 0; /* assume success */
for (p = buf; p != NULL && *p != '\0'; p = pe) {
const char ** av = NULL;
int ac = 0;
int i;
int xx;
/* locate start of next path element */
pe = strchr(p, ':');
if (pe != NULL && *pe == ':')
*pe++ = '\0';
else
pe = (char *) (p + strlen(p));
xx = poptGlob(con, p, &ac, &av);
/* work-off each resulting file from the path element */
for (i = 0; i < ac; i++) {
const char * fn = av[i];
if (!poptSaneFile(fn))
continue;
xx = poptReadConfigFile(con, fn);
if (xx && rc == 0)
rc = xx;
free((void *)av[i]);
av[i] = NULL;
}
free(av);
av = NULL;
}
if (buf)
free(buf);
return rc;
}
int poptReadDefaultConfig(poptContext con, UNUSED(int useEnv))
{
char * home;
struct stat sb;
int rc = 0; /* assume success */
if (con->appName == NULL) goto exit;
rc = poptReadConfigFile(con, POPT_SYSCONFDIR "/popt");
if (rc) goto exit;
#if defined(HAVE_GLOB_H)
if (!stat(POPT_SYSCONFDIR "/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
const char ** av = NULL;
int ac = 0;
int i;
if ((rc = poptGlob(con, POPT_SYSCONFDIR "/popt.d/*", &ac, &av)) == 0) {
for (i = 0; rc == 0 && i < ac; i++) {
const char * fn = av[i];
if (!poptSaneFile(fn))
continue;
rc = poptReadConfigFile(con, fn);
free((void *)av[i]);
av[i] = NULL;
}
free(av);
av = NULL;
}
}
if (rc) goto exit;
#endif
if ((home = getenv("HOME"))) {
char * fn = malloc(strlen(home) + 20);
if (fn != NULL) {
(void) stpcpy(stpcpy(fn, home), "/.popt");
rc = poptReadConfigFile(con, fn);
free(fn);
} else
rc = POPT_ERROR_ERRNO;
if (rc) goto exit;
}
exit:
return rc;
}
poptContext
poptFini(poptContext con)
{
return poptFreeContext(con);
}
poptContext
poptInit(int argc, const char ** argv,
const struct poptOption * options, const char * configPaths)
{
poptContext con = NULL;
const char * argv0;
if (argv == NULL || argv[0] == NULL || options == NULL)
return con;
if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
else argv0 = argv[0];
con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
if (con != NULL&& poptReadConfigFiles(con, configPaths))
con = poptFini(con);
return con;
}

View File

File diff suppressed because it is too large Load Diff

194
popt/poptint.c Normal file
View File

@@ -0,0 +1,194 @@
#include "system.h"
#include <stdarg.h>
#include <errno.h>
#ifdef HAVE_LANGINFO_H
#include <langinfo.h>
#endif
#include "poptint.h"
/* Any pair of 32 bit hashes can be used. lookup3.c generates pairs, will do. */
#define _JLU3_jlu32lpair 1
#define jlu32lpair poptJlu32lpair
#include "lookup3.c"
const char *
POPT_prev_char (const char *str)
{
const char *p = str;
while (1) {
p--;
if (((unsigned)*p & 0xc0) != (unsigned)0x80)
return p;
}
}
const char *
POPT_next_char (const char *str)
{
const char *p = str;
while (*p != '\0') {
p++;
if (((unsigned)*p & 0xc0) != (unsigned)0x80)
break;
}
return p;
}
#if !defined(POPT_fprintf) /* XXX lose all the goop ... */
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
/*
* Rebind a "UTF-8" codeset for popt's internal use.
*/
char *
POPT_dgettext(const char * dom, const char * str)
{
char * codeset = NULL;
char * retval = NULL;
if (!dom)
dom = textdomain(NULL);
codeset = bind_textdomain_codeset(dom, NULL);
bind_textdomain_codeset(dom, "UTF-8");
retval = dgettext(dom, str);
bind_textdomain_codeset(dom, codeset);
return retval;
}
#endif
#ifdef HAVE_ICONV
/**
* Return malloc'd string converted from UTF-8 to current locale.
* @param istr input string (UTF-8 encoding assumed)
* @return localized string
*/
static char *
strdup_locale_from_utf8 (char * istr)
{
char * codeset = NULL;
char * ostr = NULL;
iconv_t cd;
if (istr == NULL)
return NULL;
#ifdef HAVE_LANGINFO_H
codeset = nl_langinfo ((nl_item)CODESET);
#endif
if (codeset != NULL && strcmp(codeset, "UTF-8") != 0
&& (cd = iconv_open(codeset, "UTF-8")) != (iconv_t)-1)
{
char * shift_pin = NULL;
size_t db = strlen(istr);
char * dstr = malloc((db + 1) * sizeof(*dstr));
char * dstr_tmp;
char * pin = istr;
char * pout = dstr;
size_t ib = db;
size_t ob = db;
size_t err;
if (dstr == NULL) {
(void) iconv_close(cd);
return NULL;
}
err = iconv(cd, NULL, NULL, NULL, NULL);
while (1) {
*pout = '\0';
err = iconv(cd, &pin, &ib, &pout, &ob);
if (err != (size_t)-1) {
if (shift_pin == NULL) {
shift_pin = pin;
pin = NULL;
ib = 0;
continue;
}
} else
switch (errno) {
case E2BIG:
{ size_t used = (size_t)(pout - dstr);
db *= 2;
dstr_tmp = realloc(dstr, (db + 1) * sizeof(*dstr));
if (dstr_tmp == NULL) {
free(dstr);
(void) iconv_close(cd);
return NULL;
}
dstr = dstr_tmp;
pout = dstr + used;
ob = db - used;
continue;
} break;
case EINVAL:
case EILSEQ:
default:
break;
}
break;
}
(void) iconv_close(cd);
*pout = '\0';
ostr = xstrdup(dstr);
free(dstr);
} else
ostr = xstrdup(istr);
return ostr;
}
#endif
int
POPT_fprintf (FILE * stream, const char * format, ...)
{
char * b = NULL, * ob = NULL;
int rc;
va_list ap;
#if defined(HAVE_VASPRINTF)
va_start(ap, format);
if ((rc = vasprintf(&b, format, ap)) < 0)
b = NULL;
va_end(ap);
#else
size_t nb = (size_t)1;
/* HACK: add +1 to the realloc no. of bytes "just in case". */
/* XXX Likely unneeded, the issues wrto vsnprintf(3) return b0rkage have
* to do with whether the final '\0' is counted (or not). The code
* below already adds +1 for the (possibly already counted) trailing NUL.
*/
while ((b = realloc(b, nb+1)) != NULL) {
va_start(ap, format);
rc = vsnprintf(b, nb, format, ap);
va_end(ap);
if (rc > -1) { /* glibc 2.1 */
if ((size_t)rc < nb)
break;
nb = (size_t)(rc + 1); /* precise buffer length known */
} else /* glibc 2.0 */
nb += (nb < (size_t)100 ? (size_t)100 : nb);
ob = b;
}
#endif
rc = 0;
if (b != NULL) {
#ifdef HAVE_ICONV
ob = strdup_locale_from_utf8(b);
if (ob != NULL) {
rc = fprintf(stream, "%s", ob);
free(ob);
} else
#endif
rc = fprintf(stream, "%s", b);
free (b);
}
return rc;
}
#endif /* !defined(POPT_fprintf) */

View File

@@ -1,5 +1,5 @@
/** \ingroup popt
* \file popt/poptint.h
* @file
*/
/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -9,108 +9,145 @@
#ifndef H_POPTINT
#define H_POPTINT
#include <stdint.h>
/**
* Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
* @param p memory to free
* @retval NULL always
*/
/*@unused@*/ static inline /*@null@*/ void *
_free(/*@only@*/ /*@null@*/ const void * p)
/*@modifies p @*/
static inline void *
_free(const void * p)
{
if (p != NULL) free((void *)p);
return NULL;
}
static inline int
isSpace(const char *ptr)
{
return isspace(*(unsigned char *)ptr);
}
/* Bit mask macros. */
/*@-exporttype -redef @*/
typedef unsigned int __pbm_bits;
/*@=exporttype =redef @*/
#define __PBM_NBITS (8 * sizeof (__pbm_bits))
#define __PBM_IX(d) ((d) / __PBM_NBITS)
#define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
/*@-exporttype -redef @*/
typedef struct {
__pbm_bits bits[1];
} pbm_set;
/*@=exporttype =redef @*/
#define __PBM_BITS(set) ((set)->bits)
#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(pbm_set))
#define PBM_FREE(s) _free(s);
#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
extern void poptJlu32lpair(const void *key, size_t size,
uint32_t *pc, uint32_t *pb);
/** \ingroup popt
* Typedef's for string and array of strings.
*/
typedef const char * poptString;
typedef poptString * poptArgv;
/** \ingroup popt
* A union to simplify opt->arg access without casting.
*/
typedef union poptArg_u {
void * ptr;
int * intp;
short * shortp;
long * longp;
long long * longlongp;
float * floatp;
double * doublep;
const char ** argv;
poptCallbackType cb;
poptOption opt;
} poptArg;
extern unsigned int _poptArgMask;
extern unsigned int _poptGroupMask;
#define poptArgType(_opt) ((_opt)->argInfo & _poptArgMask)
#define poptGroup(_opt) ((_opt)->argInfo & _poptGroupMask)
#define F_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_ARGFLAG_##_FLAG)
#define LF_ISSET(_FLAG) (argInfo & POPT_ARGFLAG_##_FLAG)
#define CBF_ISSET(_opt, _FLAG) ((_opt)->argInfo & POPT_CBFLAG_##_FLAG)
/* XXX sick hack to preserve pretense of a popt-1.x ABI. */
#define poptSubstituteHelpI18N(opt) \
{ if ((opt) == poptHelpOptions) (opt) = poptHelpOptionsI18N; }
struct optionStackEntry {
int argc;
/*@only@*/ /*@null@*/
const char ** argv;
/*@only@*/ /*@null@*/
poptArgv argv;
pbm_set * argb;
int next;
/*@only@*/ /*@null@*/
const char * nextArg;
/*@observer@*/ /*@null@*/
char * nextArg;
const char * nextCharArg;
/*@dependent@*/ /*@null@*/
poptItem currAlias;
int stuffed;
};
struct poptContext_s {
struct optionStackEntry optionStack[POPT_OPTION_DEPTH];
/*@dependent@*/
struct optionStackEntry * os;
/*@owned@*/ /*@null@*/
const char ** leftovers;
poptArgv leftovers;
int numLeftovers;
int allocLeftovers;
int nextLeftover;
/*@keep@*/
const struct poptOption * options;
int restLeftover;
/*@only@*/ /*@null@*/
const char * appName;
/*@only@*/ /*@null@*/
poptItem aliases;
int numAliases;
int flags;
/*@owned@*/ /*@null@*/
unsigned int flags;
poptItem execs;
int numExecs;
/*@only@*/ /*@null@*/
const char ** finalArgv;
char * execFail;
poptArgv finalArgv;
int finalArgvCount;
int finalArgvAlloced;
/*@dependent@*/ /*@null@*/
int (*maincall) (int argc, const char **argv);
poptItem doExec;
/*@only@*/
const char * execPath;
int execAbsolute;
/*@only@*/ /*@relnull@*/
const char * otherHelp;
/*@null@*/
pbm_set * arg_strip;
};
#ifdef HAVE_LIBINTL_H
#if defined(POPT_fprintf)
#define POPT_dgettext dgettext
#else
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
#if defined(HAVE_DCGETTEXT)
char *POPT_dgettext(const char * dom, const char * str);
#endif
FORMAT(printf, 2, 3)
int POPT_fprintf (FILE* stream, const char *format, ...);
#endif /* !defined(POPT_fprintf) */
const char *POPT_prev_char (const char *str);
const char *POPT_next_char (const char *str);
#endif
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H)
#include <libintl.h>
#endif
#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
#if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
#define _(foo) gettext(foo)
#else
#define _(foo) foo
#endif
#if defined(HAVE_DCGETTEXT) && !defined(__LCLINT__)
#define D_(dom, str) dgettext(dom, str)
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H) && defined(HAVE_DCGETTEXT)
#define D_(dom, str) POPT_dgettext(dom, str)
#define POPT_(foo) D_("popt", foo)
#else
#define D_(dom, str) str
@@ -119,4 +156,3 @@ struct poptContext_s {
#define N_(foo) foo
#endif

View File

@@ -1,5 +1,5 @@
/** \ingroup popt
* \file popt/poptparse.c
* @file
*/
/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
@@ -8,11 +8,8 @@
#include "system.h"
#include "poptint.h"
#define POPT_ARGV_ARRAY_GROW_DELTA 5
/*@-boundswrite@*/
int poptDupArgv(int argc, const char **argv,
int * argcPtr, const char *** argvPtr)
{
@@ -34,13 +31,13 @@ int poptDupArgv(int argc, const char **argv,
return POPT_ERROR_MALLOC;
argv2 = (void *) dst;
dst += (argc + 1) * sizeof(*argv);
*dst = '\0';
/*@-branchstate@*/
for (i = 0; i < argc; i++) {
argv2[i] = dst;
dst += strlcpy(dst, argv[i], nb) + 1;
dst = stpcpy(dst, argv[i]);
dst++; /* trailing NUL */
}
/*@=branchstate@*/
argv2[argc] = NULL;
if (argvPtr) {
@@ -53,21 +50,25 @@ int poptDupArgv(int argc, const char **argv,
*argcPtr = argc;
return 0;
}
/*@=boundswrite@*/
/*@-bounds@*/
int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
{
const char * src;
char quote = '\0';
int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
const char ** argv = malloc(sizeof(*argv) * argvAlloced);
const char ** argv_tmp;
int argc = 0;
int buflen = strlen(s) + 1;
char * buf = memset(alloca(buflen), 0, buflen);
size_t buflen = strlen(s) + 1;
char * buf, * bufOrig = NULL;
int rc = POPT_ERROR_MALLOC;
if (argv == NULL) return rc;
buf = bufOrig = calloc((size_t)1, buflen);
if (buf == NULL) {
free(argv);
return rc;
}
argv[argc] = buf;
for (src = s; *src != '\0'; src++) {
@@ -83,13 +84,14 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
if (*src != quote) *buf++ = '\\';
}
*buf++ = *src;
} else if (isSpace(src)) {
} else if (_isspaceptr(src)) {
if (*argv[argc] != '\0') {
buf++, argc++;
if (argc == argvAlloced) {
argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
argv = realloc(argv, sizeof(*argv) * argvAlloced);
if (argv == NULL) goto exit;
argv_tmp = realloc(argv, sizeof(*argv) * argvAlloced);
if (argv_tmp == NULL) goto exit;
argv = argv_tmp;
}
argv[argc] = buf;
}
@@ -97,17 +99,17 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
case '"':
case '\'':
quote = *src;
/*@switchbreak@*/ break;
break;
case '\\':
src++;
if (!*src) {
rc = POPT_ERROR_BADQUOTE;
goto exit;
}
/*@fallthrough@*/
/* fallthrough */
default:
*buf++ = *src;
/*@switchbreak@*/ break;
break;
}
}
@@ -118,29 +120,30 @@ int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
rc = poptDupArgv(argc, argv, argcPtr, argvPtr);
exit:
if (bufOrig) free(bufOrig);
if (argv) free(argv);
return rc;
}
/*@=bounds@*/
/* still in the dev stage.
* return values, perhaps 1== file erro
* return values, perhaps 1== file error
* 2== line to long
* 3== umm.... more?
*/
int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int flags))
int poptConfigFileToString(FILE *fp, char ** argstrp,
UNUSED(int flags))
{
char line[999];
char * argstr;
char * argstr_tmp;
char * p;
char * q;
char * x;
int t;
int argvlen = 0;
size_t t;
size_t argvlen = 0;
size_t maxlinelen = sizeof(line);
size_t linelen;
int maxargvlen = 480;
int linenum = 0;
size_t maxargvlen = (size_t)480;
*argstrp = NULL;
@@ -155,11 +158,10 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
if (argstr == NULL) return POPT_ERROR_MALLOC;
while (fgets(line, (int)maxlinelen, fp) != NULL) {
linenum++;
p = line;
/* loop until first non-space char or EOL */
while( *p != '\0' && isSpace(p) )
while( *p != '\0' && _isspaceptr(p) )
p++;
linelen = strlen(p);
@@ -173,25 +175,29 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
q = p;
while (*q != '\0' && (!isSpace(q)) && *q != '=')
while (*q != '\0' && (!_isspaceptr(q)) && *q != '=')
q++;
if (isSpace(q)) {
if (_isspaceptr(q)) {
/* a space after the name, find next non space */
*q++='\0';
while( *q != '\0' && isSpace(q) ) q++;
while( *q != '\0' && _isspaceptr(q) ) q++;
}
if (*q == '\0') {
/* single command line option (ie, no name=val, just name) */
q[-1] = '\0'; /* kill off newline from fgets() call */
argvlen += (t = q - p) + (sizeof(" --")-1);
argvlen += (t = (size_t)(q - p)) + (sizeof(" --")-1);
if (argvlen >= maxargvlen) {
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
argstr = realloc(argstr, maxargvlen);
if (argstr == NULL) return POPT_ERROR_MALLOC;
argstr_tmp = realloc(argstr, maxargvlen);
if (argstr_tmp == NULL) {
free(argstr);
return POPT_ERROR_MALLOC;
}
argstr = argstr_tmp;
}
strlcat(argstr, " --", maxargvlen);
strlcat(argstr, p, maxargvlen);
strcat(argstr, " --");
strcat(argstr, p);
continue;
}
if (*q != '=')
@@ -201,29 +207,33 @@ int poptConfigFileToString(FILE *fp, char ** argstrp, /*@unused@*/ UNUSED(int fl
*q++ = '\0';
/* find next non-space letter of value */
while (*q != '\0' && isSpace(q))
while (*q != '\0' && _isspaceptr(q))
q++;
if (*q == '\0')
continue; /* XXX silently ignore missing value */
/* now, loop and strip all ending whitespace */
x = p + linelen;
while (isSpace(--x))
*x = 0; /* null out last char if space (including fgets() NL) */
while (_isspaceptr(--x))
*x = '\0'; /* null out last char if space (including fgets() NL) */
/* rest of line accept */
t = x - p;
t = (size_t)(x - p);
argvlen += t + (sizeof("' --='")-1);
if (argvlen >= maxargvlen) {
maxargvlen = (t > maxargvlen) ? t*2 : maxargvlen*2;
argstr = realloc(argstr, maxargvlen);
if (argstr == NULL) return POPT_ERROR_MALLOC;
argstr_tmp = realloc(argstr, maxargvlen);
if (argstr_tmp == NULL) {
free(argstr);
return POPT_ERROR_MALLOC;
}
argstr = argstr_tmp;
}
strlcat(argstr, " --", maxargvlen);
strlcat(argstr, p, maxargvlen);
strlcat(argstr, "=\"", maxargvlen);
strlcat(argstr, q, maxargvlen);
strlcat(argstr, "\"", maxargvlen);
strcat(argstr, " --");
strcat(argstr, p);
strcat(argstr, "=\"");
strcat(argstr, q);
strcat(argstr, "\"");
}
*argstrp = argstr;

View File

@@ -1,134 +1,70 @@
/**
* @file
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined (__GLIBC__) && defined(__LCLINT__)
/*@-declundef@*/
/*@unchecked@*/
extern __const __int32_t *__ctype_tolower;
/*@unchecked@*/
extern __const __int32_t *__ctype_toupper;
/*@=declundef@*/
#endif
#ifdef __TANDEM
# include <floss.h(floss_execvp,floss_read)>
#endif
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
/* XXX isspace(3) has i18n encoding signedness issues on Solaris. */
#define _isspaceptr(_chp) isspace((int)(*(unsigned const char *)(_chp)))
#if HAVE_MCHECK_H
#ifdef HAVE_MCHECK_H
#include <mcheck.h>
#endif
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
# include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef __GNUC__
#define __attribute__(x)
#endif
void * xmalloc (size_t size);
#ifdef __NeXT
/* access macros are not declared in non posix mode in unistd.h -
don't try to use posix on NeXTstep 3.3 ! */
#include <libc.h>
#endif
void * xcalloc (size_t nmemb, size_t size);
#if defined(__LCLINT__)
/*@-declundef -incondefs @*/ /* LCL: missing annotation */
/*@only@*/ /*@out@*/
void * alloca (size_t __size)
/*@ensures MaxSet(result) == (__size - 1) @*/
/*@*/;
/*@=declundef =incondefs @*/
#endif
void * xrealloc (void * ptr, size_t size);
/* AIX requires this to be the first thing in the file. */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
# ifdef _AIX
#pragma alloca
# else
# ifdef HAVE_ALLOCA
# ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca(size_t size);
# endif
# else
# ifdef alloca
# undef alloca
# endif
# define alloca(sz) malloc(sz) /* Kludge this for now */
# endif
# endif
# endif
#elif !defined(alloca)
#define alloca __builtin_alloca
#endif
char * xstrdup (const char *str);
#ifndef HAVE_STRLCPY
size_t strlcpy(char *d, const char *s, size_t bufsize);
#endif
#if !defined(HAVE_STPCPY)
/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */
static inline char * stpcpy (char *dest, const char * src) {
register char *d = dest;
register const char *s = src;
#ifndef HAVE_STRLCAT
size_t strlcat(char *d, const char *s, size_t bufsize);
#endif
#if HAVE_MCHECK_H && defined(__GNUC__)
static inline char *
xstrdup(const char *s)
{
size_t memsize = strlen(s) + 1;
char *ptr = malloc(memsize);
if (!ptr) {
fprintf(stderr, "virtual memory exhausted.\n");
exit(EXIT_FAILURE);
}
strlcpy(ptr, s, memsize);
return ptr;
do
*d++ = *s;
while (*s++ != '\0');
return d - 1;
}
#else
#define xstrdup(_str) strdup(_str)
#endif /* HAVE_MCHECK_H && defined(__GNUC__) */
#endif
#if HAVE___SECURE_GETENV && !defined(__LCLINT__)
/* Memory allocation via macro defs to get meaningful locations from mtrace() */
#if defined(HAVE_MCHECK_H) && defined(__GNUC__)
#define vmefail() (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL)
#define xmalloc(_size) (malloc(_size) ? : vmefail())
#define xcalloc(_nmemb, _size) (calloc((_nmemb), (_size)) ? : vmefail())
#define xrealloc(_ptr, _size) (realloc((_ptr), (_size)) ? : vmefail())
#define xstrdup(_str) (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str)))
#else
#define xmalloc(_size) malloc(_size)
#define xcalloc(_nmemb, _size) calloc((_nmemb), (_size))
#define xrealloc(_ptr, _size) realloc((_ptr), (_size))
#define xstrdup(_str) strdup(_str)
#endif /* defined(HAVE_MCHECK_H) && defined(__GNUC__) */
#if defined(HAVE_SECURE_GETENV)
#define getenv(_s) secure_getenv(_s)
#elif defined(HAVE___SECURE_GETENV)
#define getenv(_s) __secure_getenv(_s)
#endif
#if !defined HAVE_SNPRINTF || !defined HAVE_C99_VSNPRINTF
#define snprintf rsync_snprintf
int snprintf(char *str,size_t count,const char *fmt,...);
#if !defined(__GNUC__) && !defined(__attribute__)
#define __attribute__(x)
#endif
#define UNUSED(x) x __attribute__((__unused__))
#define PACKAGE "rsync"
#define FORMAT(a, b, c) __attribute__((__format__ (a, b, c)))
#define NORETURN __attribute__((__noreturn__))
#include "popt.h"

View File

@@ -3,7 +3,7 @@
*
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2003-2022 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
@@ -56,7 +56,6 @@ extern int inplace;
extern int inplace_partial;
extern int allowed_lull;
extern int delay_updates;
extern int xfersum_type;
extern BOOL want_progress_now;
extern mode_t orig_umask;
extern struct stats stats;
@@ -67,6 +66,11 @@ extern char sender_file_sum[MAX_DIGEST_LEN];
extern struct file_list *cur_flist, *first_flist, *dir_flist;
extern filter_rule_list daemon_filter_list;
extern OFF_T preallocated_len;
extern int fuzzy_basis;
extern struct name_num_item *xfer_sum_nni;
extern int xfer_sum_len;
extern int use_secure_symlinks;
static struct bitbag *delayed_bits = NULL;
static int phase = 0, redoing = 0;
@@ -211,7 +215,12 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
* access to ensure that there is no race condition. They will be
* correctly updated after the right owner and group info is set.
* (Thanks to snabb@epipe.fi for pointing this out.) */
fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
/* When use_secure_symlinks is on (non-chroot daemon with munge_symlinks),
* use secure_mkstemp to prevent symlink race attacks on parent directories. */
if (use_secure_symlinks)
fd = secure_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
else
fd = do_mkstemp(fnametmp, (file->mode|added_perms) & INITACCESSPERMS);
#if 0
/* In most cases parent directories will already exist because their
@@ -240,7 +249,6 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
static char file_sum1[MAX_DIGEST_LEN];
struct map_struct *mapbuf;
struct sum_struct sum;
int sum_len;
int32 len;
OFF_T total_size = F_LENGTH(file);
OFF_T offset = 0;
@@ -280,7 +288,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
} else
mapbuf = NULL;
sum_init(xfersum_type, checksum_seed);
sum_init(xfer_sum_nni, checksum_seed);
if (append_mode > 0) {
OFF_T j;
@@ -310,7 +318,12 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
}
}
while ((i = recv_token(f_in, &data)) != 0) {
while (1) {
data = NULL;
i = recv_token(f_in, &data);
if (i == 0)
break;
if (INFO_GTE(PROGRESS, 1))
show_progress(offset, total_size);
@@ -318,6 +331,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
if (i > 0) {
if (!data) {
rprintf(FERROR, "Invalid literal token with no data [%s]\n", who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
if (DEBUG_GTE(DELTASUM, 3)) {
rprintf(FINFO,"data recv %d at %s\n",
i, big_num(offset));
@@ -335,6 +352,11 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
}
i = -(i+1);
if (i < 0 || i >= sum.count) {
rprintf(FERROR, "Invalid block index %d (count=%ld) [%s]\n",
i, (long)sum.count, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
offset2 = i * (OFF_T)sum.blength;
len = sum.blength;
if (i == (int)sum.count-1 && sum.remainder != 0)
@@ -371,7 +393,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
if (fd != -1 && offset > 0) {
if (sparse_files > 0) {
if (sparse_end(fd, offset) != 0)
if (sparse_end(fd, offset, updating_basis_or_equiv) != 0)
goto report_write_error;
} else if (flush_write_file(fd) < 0) {
report_write_error:
@@ -393,7 +415,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
if (INFO_GTE(PROGRESS, 1))
end_progress(total_size);
sum_len = sum_end(file_sum1);
sum_end(file_sum1);
if (do_fsync && fd != -1 && fsync(fd) != 0) {
rsyserr(FERROR, errno, "fsync failed on %s", full_fname(fname));
@@ -403,10 +425,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
if (mapbuf)
unmap_file(mapbuf);
read_buf(f_in, sender_file_sum, sum_len);
read_buf(f_in, sender_file_sum, xfer_sum_len);
if (DEBUG_GTE(DELTASUM, 2))
rprintf(FINFO,"got file_sum\n");
if (fd != -1 && memcmp(file_sum1, sender_file_sum, sum_len) != 0)
if (fd != -1 && memcmp(file_sum1, sender_file_sum, xfer_sum_len) != 0)
return 0;
return 1;
}
@@ -434,14 +456,13 @@ static void handle_delayed_updates(char *local_name)
}
/* We don't use robust_rename() here because the
* partial-dir must be on the same drive. */
if (do_rename(partialptr, fname) < 0) {
if (do_rename_at(partialptr, fname) < 0) {
rsyserr(FERROR_XFER, errno,
"rename failed for %s (from %s)",
full_fname(fname), partialptr);
} else {
if (remove_source_files
|| (preserve_hard_links && F_IS_HLINKED(file)))
send_msg_int(MSG_SUCCESS, ndx);
if (remove_source_files || (preserve_hard_links && F_IS_HLINKED(file)))
send_msg_success(fname, ndx);
handle_partial_dir(partialptr, PDIR_DELETE);
}
}
@@ -451,7 +472,10 @@ static void handle_delayed_updates(char *local_name)
static void no_batched_update(int ndx, BOOL is_redo)
{
struct file_list *flist = flist_for_ndx(ndx, "no_batched_update");
struct file_struct *file = flist->files[ndx - flist->ndx_start];
struct file_struct *file;
if (ndx < flist->ndx_start)
exit_cleanup(RERR_PROTOCOL);
file = flist->files[ndx - flist->ndx_start];
rprintf(FERROR_XFER, "(No batched update for%s \"%s\")\n",
is_redo ? " resend of" : "", f_name(file, NULL));
@@ -551,6 +575,8 @@ int recv_files(int f_in, int f_out, char *local_name)
progress_init();
while (1) {
const char *basedir = NULL;
cleanup_disable();
/* This call also sets cur_flist. */
@@ -586,6 +612,8 @@ int recv_files(int f_in, int f_out, char *local_name)
if (ndx - cur_flist->ndx_start >= 0)
file = cur_flist->files[ndx - cur_flist->ndx_start];
else if (cur_flist->parent_ndx < 0)
exit_cleanup(RERR_PROTOCOL);
else
file = dir_flist->files[cur_flist->parent_ndx];
fname = local_name ? local_name : f_name(file, fbuf);
@@ -593,10 +621,13 @@ int recv_files(int f_in, int f_out, char *local_name)
if (DEBUG_GTE(RECV, 1))
rprintf(FINFO, "recv_files(%s)\n", fname);
if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')
&& check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) {
rprintf(FERROR, "attempt to hack rsync failed.\n");
exit_cleanup(RERR_PROTOCOL);
if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')) {
int filt_flags = S_ISDIR(file->mode) ? NAME_IS_DIR : NAME_IS_FILE;
if (check_filter(&daemon_filter_list, FLOG, fname, filt_flags) < 0) {
rprintf(FERROR, "ERROR: rejecting file transfer request for daemon excluded file: %s\n",
fname);
exit_cleanup(RERR_PROTOCOL);
}
}
#ifdef SUPPORT_XATTRS
@@ -695,7 +726,7 @@ int recv_files(int f_in, int f_out, char *local_name)
if (!am_server)
discard_receive_data(f_in, file);
if (inc_recurse)
send_msg_int(MSG_SUCCESS, ndx);
send_msg_success(fname, ndx);
continue;
}
@@ -713,28 +744,34 @@ int recv_files(int f_in, int f_out, char *local_name)
fnamecmp = get_backup_name(fname);
break;
case FNAMECMP_FUZZY:
if (fuzzy_basis == 0) {
rprintf(FERROR_XFER, "rsync: refusing malicious fuzzy operation for %s\n", xname);
exit_cleanup(RERR_PROTOCOL);
}
if (file->dirname) {
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, file->dirname, xname);
fnamecmp = fnamecmpbuf;
} else
fnamecmp = xname;
basedir = file->dirname;
}
fnamecmp = xname;
break;
default:
if (fnamecmp_type > FNAMECMP_FUZZY && fnamecmp_type-FNAMECMP_FUZZY <= basis_dir_cnt) {
fnamecmp_type -= FNAMECMP_FUZZY + 1;
if (file->dirname) {
stringjoin(fnamecmpbuf, sizeof fnamecmpbuf,
basis_dir[fnamecmp_type], "/", file->dirname, "/", xname, NULL);
} else
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], xname);
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], file->dirname);
basedir = fnamecmpbuf;
} else {
basedir = basis_dir[fnamecmp_type];
}
fnamecmp = xname;
} else if (fnamecmp_type >= basis_dir_cnt) {
rprintf(FERROR,
"invalid basis_dir index: %d.\n",
fnamecmp_type);
exit_cleanup(RERR_PROTOCOL);
} else
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname);
fnamecmp = fnamecmpbuf;
} else {
basedir = basis_dir[fnamecmp_type];
fnamecmp = fname;
}
break;
}
if (!fnamecmp || (daemon_filter_list.head
@@ -757,25 +794,31 @@ int recv_files(int f_in, int f_out, char *local_name)
}
/* open the file */
fd1 = do_open(fnamecmp, O_RDONLY, 0);
fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
if (fd1 == -1 && protocol_version < 29) {
if (fnamecmp != fname) {
fnamecmp = fname;
fnamecmp_type = FNAMECMP_FNAME;
fd1 = do_open(fnamecmp, O_RDONLY, 0);
fd1 = do_open_nofollow(fnamecmp, O_RDONLY);
}
if (fd1 == -1 && basis_dir[0]) {
/* pre-29 allowed only one alternate basis */
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
basis_dir[0], fname);
fnamecmp = fnamecmpbuf;
basedir = basis_dir[0];
fnamecmp = fname;
fnamecmp_type = FNAMECMP_BASIS_DIR_LOW;
fd1 = do_open(fnamecmp, O_RDONLY, 0);
fd1 = secure_relative_open(basedir, fnamecmp, O_RDONLY, 0);
}
}
if (basedir) {
// for the following code we need the full
// path name as a single string
pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basedir, fnamecmp);
fnamecmp = fnamecmpbuf;
}
one_inplace = inplace_partial && fnamecmp_type == FNAMECMP_PARTIAL_DIR;
updating_basis_or_equiv = one_inplace
|| (inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP));
@@ -836,11 +879,21 @@ int recv_files(int f_in, int f_out, char *local_name)
/* We now check to see if we are writing the file "inplace" */
if (inplace || one_inplace) {
fnametmp = one_inplace ? partialptr : fname;
fd2 = do_open(fnametmp, O_WRONLY|O_CREAT, 0600);
/* When use_secure_symlinks is on (non-chroot daemon),
* use secure open to prevent symlink race attacks where an
* attacker could switch a directory to a symlink between
* path validation and file open. */
if (use_secure_symlinks)
fd2 = secure_relative_open(NULL, fnametmp, O_WRONLY|O_CREAT, 0600);
else
fd2 = do_open(fnametmp, O_WRONLY|O_CREAT, 0600);
#ifdef linux
if (fd2 == -1 && errno == EACCES) {
/* Maybe the error was due to protected_regular setting? */
fd2 = do_open(fname, O_WRONLY, 0600);
if (use_secure_symlinks)
fd2 = secure_relative_open(NULL, fname, O_WRONLY, 0600);
else
fd2 = do_open(fname, O_WRONLY, 0600);
}
#endif
if (fd2 == -1) {
@@ -892,7 +945,7 @@ int recv_files(int f_in, int f_out, char *local_name)
recv_ok = -1;
else if (fnamecmp == partialptr) {
if (!one_inplace)
do_unlink(partialptr);
do_unlink_at(partialptr);
handle_partial_dir(partialptr, PDIR_DELETE);
}
} else if (keep_partial && partialptr && (!one_inplace || delay_updates)) {
@@ -901,7 +954,7 @@ int recv_files(int f_in, int f_out, char *local_name)
"Unable to create partial-dir for %s -- discarding %s.\n",
local_name ? local_name : f_name(file, NULL),
recv_ok ? "completed file" : "partial file");
do_unlink(fnametmp);
do_unlink_at(fnametmp);
recv_ok = -1;
} else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
file, recv_ok, !partial_dir))
@@ -912,7 +965,7 @@ int recv_files(int f_in, int f_out, char *local_name)
} else
partialptr = NULL;
} else if (!one_inplace)
do_unlink(fnametmp);
do_unlink_at(fnametmp);
cleanup_disable();
@@ -923,9 +976,8 @@ int recv_files(int f_in, int f_out, char *local_name)
case 2:
break;
case 1:
if (remove_source_files || inc_recurse
|| (preserve_hard_links && F_IS_HLINKED(file)))
send_msg_int(MSG_SUCCESS, ndx);
if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file)))
send_msg_success(fname, ndx);
break;
case 0: {
enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;

View File

@@ -94,6 +94,11 @@ The ssl helper scripts are affected by the following environment variables:
> rsync-ssl -aiv rsync://example.com:9874/mod/ dest
## THE SERVER SIDE
For help setting up an SSL/TLS supporting rsync, see the [instructions in
rsyncd.conf](rsyncd.conf.5#SSL_TLS_Daemon_Setup).
## SEE ALSO
[**rsync**(1)](rsync.1), [**rsyncd.conf**(5)](rsyncd.conf.5)

View File

File diff suppressed because it is too large Load Diff

15
rsync.c
View File

@@ -437,7 +437,10 @@ int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr, cha
*/
void free_sums(struct sum_struct *s)
{
if (s->sums) free(s->sums);
if (s->sums) {
free(s->sums);
free(s->sum2_array);
}
free(s);
}
@@ -544,7 +547,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
if (am_root >= 0) {
uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
if (do_lchown(fname, uid, gid) != 0) {
if (do_lchown_at(fname, uid, gid) != 0) {
/* We shouldn't have attempted to change uid
* or gid unless have the privilege. */
rsyserr(FERROR_XFER, errno, "%s %s failed",
@@ -642,7 +645,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
#ifdef SUPPORT_ACLS
/* It's OK to call set_acl() now, even for a dir, as the generator
* will enable owner-writability using chmod, if necessary.
*
*
* If set_acl() changes permission bits in the process of setting
* an access ACL, it changes sxp->st.st_mode so we know whether we
* need to chmod(). */
@@ -654,7 +657,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
#ifdef HAVE_CHMOD
if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
int ret = am_root < 0 ? 0 : do_chmod_at(fname, new_mode);
if (ret < 0) {
rsyserr(FERROR_XFER, errno,
"failed to set permissions on %s",
@@ -755,7 +758,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
full_fname(fnametmp), fname);
if (!partialptr || (ret == -2 && temp_copy_name)
|| robust_rename(fnametmp, partialptr, NULL, file->mode) < 0)
do_unlink(fnametmp);
do_unlink_at(fnametmp);
return 0;
}
if (ret == 0) {
@@ -771,7 +774,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME);
if (temp_copy_name) {
if (do_rename(fnametmp, fname) < 0) {
if (do_rename_at(fnametmp, fname) < 0) {
rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\"",
full_fname(fnametmp), fname);
return 0;

55
rsync.h
View File

@@ -92,6 +92,7 @@
#define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */
#define FLAG_TIME_FAILED (1<<11)/* generator */
#define FLAG_MOD_NSEC (1<<12) /* sender/receiver/generator */
#define FLAG_GOT_DIR_FLIST (1<<13)/* sender/receiver/generator - dir_flist only */
/* These flags are passed to functions but not stored. */
@@ -110,7 +111,7 @@
/* Update this if you make incompatible changes and ALSO update the
* SUBPROTOCOL_VERSION if it is not a final (official) release. */
#define PROTOCOL_VERSION 31
#define PROTOCOL_VERSION 32
/* This is used when working on a new protocol version or for any unofficial
* protocol tweaks. It should be a non-zero value for each pre-release repo
@@ -162,6 +163,29 @@
/* For compatibility with older rsyncs */
#define OLD_MAX_BLOCK_SIZE ((int32)1 << 29)
/* Policy ceilings on attacker-controlled wire values. Picked well above any
* legitimate filesystem / protocol traffic but well below sizes that could
* cause integer overflow or DoS-grade allocations. See input_checking.txt.
*
* Note on MAX_WIRE_XATTR_DATALEN: xattr datum size is bounded only by the
* wire-format maximum (signed int32 varint, ~2GB). macOS resource forks
* are transferred as the com.apple.ResourceFork xattr and can legitimately
* be many GB; --max-alloc (default 1GB, configurable) is the real
* allocation cap. read_varint_size() still rejects negative values so a
* hostile peer cannot wrap to ~SIZE_MAX. */
#define MAX_WIRE_XATTR_COUNT 65536
#define MAX_WIRE_XATTR_NAMELEN 4096
#define MAX_WIRE_XATTR_DATALEN ((int32)0x7fffffff)
#define MAX_WIRE_ACL_COUNT 65536
#define MAX_WIRE_NSEC 999999999
/* MAX_WIRE_DEL_STAT is the per-category cap for read_del_stats() in main.c,
* which accumulates 5 wire-supplied counts into the int32 stats.deleted_files
* accumulator. Capped at 2^28 so 5 * 2^28 = 1.34 GB stays under INT32_MAX
* (2.15 GB) with margin -- a higher cap (e.g. 2^30) would let a hostile peer
* supplying 3+ max-sized counts overflow the accumulator, which is signed-int
* UB. 2^28 is still well above any plausible real transfer's deletion count. */
#define MAX_WIRE_DEL_STAT ((int32)1 << 28)
#define ROUND_UP_1024(siz) ((siz) & (1024-1) ? ((siz) | (1024-1)) + 1 : (siz))
#define IOERR_GENERAL (1<<0) /* For backward compatibility, this must == 1 */
@@ -338,6 +362,9 @@ enum delret {
# endif
# include <string.h>
#endif
#ifdef HAVE_BSD_STRING_H
# include <bsd/string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
@@ -363,16 +390,10 @@ enum delret {
#include <sys/socket.h>
#endif
#ifdef TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#endif
#include <time.h>
#endif
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
@@ -823,6 +844,7 @@ extern int uid_ndx;
extern int gid_ndx;
extern int acls_ndx;
extern int xattrs_ndx;
extern int file_sum_extra_cnt;
#ifdef USE_FLEXIBLE_ARRAY
#define FILE_STRUCT_LEN (sizeof (struct file_struct))
@@ -833,7 +855,7 @@ extern int xattrs_ndx;
#define DEV_EXTRA_CNT 2
#define DIRNODE_EXTRA_CNT 3
#define EXTRA64_CNT ((sizeof (union file_extras64) + EXTRA_LEN - 1) / EXTRA_LEN)
#define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
#define SUM_EXTRA_CNT file_sum_extra_cnt
#define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx))
#define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump))
@@ -960,12 +982,12 @@ struct sum_buf {
uint32 sum1; /**< simple checksum */
int32 chain; /**< next hash-table collision */
short flags; /**< flag bits */
char sum2[SUM_LENGTH]; /**< checksum */
};
struct sum_struct {
OFF_T flength; /**< total file length */
struct sum_buf *sums; /**< points to info for each chunk */
char *sum2_array; /**< checksums of length xfer_sum_len */
int32 count; /**< how many chunks */
int32 blength; /**< block_length */
int32 remainder; /**< flength % block_length */
@@ -984,6 +1006,8 @@ struct map_struct {
int status; /* first errno from read errors */
};
#define sum2_at(s, i) ((s)->sum2_array + ((size_t)(i) * xfer_sum_len))
#define NAME_IS_FILE (0) /* filter name as a file */
#define NAME_IS_DIR (1<<0) /* filter name as a dir */
#define NAME_IS_XATTR (1<<2) /* filter name as an xattr */
@@ -1020,6 +1044,7 @@ typedef struct filter_struct {
int slash_cnt;
struct filter_list_struct *mergelist;
} u;
uchar elide;
} filter_rule;
typedef struct filter_list_struct {
@@ -1159,17 +1184,17 @@ typedef struct {
#define NSTR_COMPRESS 1
struct name_num_item {
int num;
const char *name, *main_name;
int num, flags;
const char *name;
struct name_num_item *main_nni;
};
struct name_num_obj {
const char *type;
const char *negotiated_name;
struct name_num_item *negotiated_nni;
uchar *saw;
int saw_len;
int negotiated_num;
struct name_num_item list[10]; /* we'll get a compile error/warning if this is ever too small */
struct name_num_item *list;
};
#ifdef EXTERNAL_ZLIB

View File

@@ -74,25 +74,7 @@ reread the `rsyncd.conf` file. The file is re-read on each client connection.
## GLOBAL PARAMETERS
The first parameters in the file (before a [module] header) are the global
parameters. Rsync also allows for the use of a "[global]" module name to
indicate the start of one or more global-parameter sections (the name must be
lower case).
You may also include any module parameters in the global part of the config
file in which case the supplied value will override the default for that
parameter.
You may use references to environment variables in the values of parameters.
String parameters will have %VAR% references expanded as late as possible (when
the string is first used in the program), allowing for the use of variables
that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
parameters (such as true/false settings) are expanded when read from the config
file. If a variable does not exist in the environment, or if a sequence of
characters is not a valid reference (such as an un-paired percent sign), the
raw characters are passed through unchanged. This helps with backward
compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
string in a path could result in a very unsafe path). The safest way to insert
a literal % into a value is to use %%.
parameters:
[comment]: # (An OL starting at 0 is converted into a DL by the parser.)
@@ -138,6 +120,22 @@ a literal % into a value is to use %%.
You can override the default backlog value when the daemon listens for
connections. It defaults to 5.
You may also include any [MODULE PARAMETERS](#) in the global part of the
config file, in which case the supplied value will override the default for
that parameter.
You may use references to environment variables in the values of parameters.
String parameters will have %VAR% references expanded as late as possible (when
the string is first used in the program), allowing for the use of variables
that rsync sets at connection time, such as RSYNC_USER_NAME. Non-string
parameters (such as true/false settings) are expanded when read from the config
file. If a variable does not exist in the environment, or if a sequence of
characters is not a valid reference (such as an un-paired percent sign), the
raw characters are passed through unchanged. This helps with backward
compatibility and safety (e.g. expanding a non-existent %VAR% to an empty
string in a path could result in a very unsafe path). The safest way to insert
a literal % into a value is to use %%.
## MODULE PARAMETERS
After the global parameters you should define a number of modules, each module
@@ -146,11 +144,17 @@ a module name in square brackets [module] followed by the parameters for that
module. The module name cannot contain a slash or a closing square bracket.
If the name contains whitespace, each internal sequence of whitespace will be
changed into a single space, while leading or trailing whitespace will be
discarded. Also, the name cannot be "global" as that exact name indicates that
global parameters follow (see above).
discarded.
As with GLOBAL PARAMETERS, you may use references to environment variables in
the values of parameters. See the GLOBAL PARAMETERS section for more details.
There is also a special module name of "[global]" that does not define a module
but instead switches back to the global settings context where default
parameters can be specified. Because each defined module gets its full set of
parameters as a combination of the default values that are set at that position
in the config file plus its own parameter list, the use of a "[global]" section
can help to maintain shared config values for multiple modules.
As with [GLOBAL PARAMETERS](#), you may use references to environment variables
in the values of parameters. See that section for details.
0. `comment`
@@ -164,6 +168,16 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
available in this module. You must specify this parameter for each module
in `rsyncd.conf`.
If the value contains a "/./" element then the path will be divided at that
point into a chroot dir and an inner-chroot subdir. If [`use chroot`](#)
is set to false, though, the extraneous dot dir is just cleaned out of the
path. An example of this idiom is:
> path = /var/rsync/./module1
This will (when chrooting) chroot to "/var/rsync" and set the inside-chroot
path to "/module1".
You may base the path's value off of an environment variable by surrounding
the variable name with percent signs. You can even reference a variable
that is set by rsync when the user connects. For example, this would use
@@ -187,29 +201,47 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
path, and of complicating the preservation of users and groups by name (see
below).
As an additional safety feature, you can specify a dot-dir in the module's
"[path](#)" to indicate the point where the chroot should occur. This allows
rsync to run in a chroot with a non-"/" path for the top of the transfer
hierarchy. Doing this guards against unintended library loading (since
those absolute paths will not be inside the transfer hierarchy unless you
have used an unwise pathname), and lets you setup libraries for the chroot
that are outside of the transfer. For example, specifying
"/var/rsync/./module1" will chroot to the "/var/rsync" directory and set
the inside-chroot path to "/module1". If you had omitted the dot-dir, the
chroot would have used the whole path, and the inside-chroot path would
have been "/".
If `use chroot` is not set, it defaults to trying to enable a chroot but
allows the daemon to continue (after logging a warning) if it fails. The
one exception to this is when a module's [`path`](#) has a "/./" chroot
divider in it -- this causes an unset value to be treated as true for that
module.
When both "use chroot" and "[daemon chroot](#)" are false, OR the inside-chroot
path of "use chroot" is not "/", rsync will: (1) munge symlinks by default
for security reasons (see "[munge symlinks](#)" for a way to turn this off, but
only if you trust your users), (2) substitute leading slashes in absolute
paths with the module's path (so that options such as `--backup-dir`,
`--compare-dest`, etc. interpret an absolute path as rooted in the module's
"[path](#)" dir), and (3) trim ".." path elements from args if rsync believes
they would escape the module hierarchy. The default for "use chroot" is
true, and is the safer choice (especially if the module is not read-only).
Prior to rsync 3.2.7, the default value was "true". The new "unset"
default makes it easier to setup an rsync daemon as a non-root user or to
run a daemon on a system where chroot fails. Explicitly setting the value
to "true" in rsyncd.conf will always require the chroot to succeed.
When this parameter is enabled *and* the "[name converter](#)" parameter is
It is also possible to specify a dot-dir in the module's "[path](#)" to
indicate that you want to chdir to the earlier part of the path and then
serve files from inside the latter part of the path (with sanitizing and
default symlink munging). This can be useful if you need some library dirs
inside the chroot (typically for uid & gid lookups) but don't want to put
the lib dir into the top of the served path (even though they can be hidden
with an [`exclude`](#) directive). However, a better choice for a modern
rsync setup is to use a [`name converter`](#)" and try to avoid inner lib
dirs altogether. See also the [`daemon chroot`](#) parameter, which causes
rsync to chroot into its own chroot area before doing any path-related
chrooting.
If the daemon is serving the "/" dir (either directly or due to being
chrooted to the module's path), rsync does not do any path sanitizing or
(default) munging.
When it has to limit access to a particular subdir (either due to chroot
being disabled or having an inside-chroot path set), rsync will munge
symlinks (by default) and sanitize paths. Those that dislike munged
symlinks (and really, really trust their users to not break out of the
subdir) can disable the symlink munging via the "[munge symlinks](#)"
parameter.
When rsync is sanitizing paths, it trims ".." path elements from args that
it believes would escape the module hierarchy. It also substitutes leading
slashes in absolute paths with the module's path (so that options such as
`--backup-dir` & `--compare-dest` interpret an absolute path as rooted in
the module's "[path](#)" dir).
When a chroot is in effect *and* the "[name converter](#)" parameter is
*not* set, the "[numeric ids](#)" parameter will default to being enabled
(disabling name lookups). This means that if you manually setup
name-lookup libraries in your chroot (instead of using a name converter)
@@ -894,7 +926,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
> refuse options = * !a !v !compress*
Don't worry that the "`*`" will refuse certain vital options such as
`--dry-run`, `--server`, `--no-iconv`, `--protect-args`, etc. These
`--dry-run`, `--server`, `--no-iconv`, `--seclude-args`, etc. These
important options are not matched by wild-card, so they must be overridden
by their exact name. For instance, if you're forcing iconv transfers you
could use something like this:
@@ -948,7 +980,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
`--log-file-format`.
- `--sender`: Use "[write only](#)" parameter instead of refusing this.
- `--dry-run`, `-n`: Who would want to disable this?
- `--protect-args`, `-s`: This actually makes transfers safer.
- `--seclude-args`, `-s`: Is the oldest arg-protection method.
- `--from0`, `-0`: Makes it easier to accept/refuse `--files-from` without
affecting this helpful modifier.
- `--iconv`: This is auto-disabled based on "[charset](#)" parameter.
@@ -991,7 +1023,7 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
_not_ displayed if the script returns success. The other programs cannot
send any text to the user. All output except for the `pre-xfer exec`
stdout goes to the corresponding daemon's stdout/stderr, which is typically
discarded. See the `--no-detatch` option for a way to see the daemon's
discarded. See the `--no-detach` option for a way to see the daemon's
output, which can assist with debugging.
Note that the `early exec` command runs before any part of the transfer
@@ -1041,6 +1073,16 @@ the values of parameters. See the GLOBAL PARAMETERS section for more details.
**system()** call's default shell), and use RSYNC_NO_XFER_EXEC to disable
both options completely.
0. `temp dir`
Specifies a directory that rsync should use for temporary files created
during the transfer of updated files. If that directory is on a different
partition, after transfer file is being copied instead of unlinked.
This parameter equals with `--temp-dir` option, so please consult rsync
manpage for further information.
## CONFIG DIRECTIVES
There are currently two config directives available that allow a config file to
@@ -1114,15 +1156,15 @@ SSL proxy.
## SSL/TLS Daemon Setup
When setting up an rsync daemon for access via SSL/TLS, you will need to
configure a proxy (such as haproxy or nginx) as the front-end that handles the
encryption.
configure a TCP proxy (such as haproxy or nginx) as the front-end that handles
the encryption.
- You should limit the access to the backend-rsyncd port to only allow the
proxy to connect. If it is on the same host as the proxy, then configuring
it to only listen on localhost is a good idea.
- You should consider turning on the `proxy protocol` parameter if your proxy
supports sending that information. The examples below assume that this is
enabled.
- You should consider turning on the `proxy protocol` rsync-daemon parameter if
your proxy supports sending that information. The examples below assume that
this is enabled.
An example haproxy setup is as follows:
@@ -1149,14 +1191,17 @@ An example nginx proxy setup is as follows:
> ssl_certificate_key /etc/letsencrypt/example.com/privkey.pem;
>
> proxy_pass localhost:873;
> proxy_protocol on; # Requires "proxy protocol = true"
> proxy_protocol on; # Requires rsyncd.conf "proxy protocol = true"
> proxy_timeout 1m;
> proxy_connect_timeout 5s;
> }
> }
> ```
## EXAMPLES
If rsyncd should be accessible encrypted and unencrypted at the same time make
the proxy listen on port 873 as well and let it handle both streams.
## DAEMON CONFIG EXAMPLES
A simple rsyncd.conf file that allow anonymous rsync to a ftp area at
`/home/ftp` would be:
@@ -1228,7 +1273,7 @@ Rsync is distributed under the GNU General Public License. See the file
[COPYING](COPYING) for details.
An rsync web site is available at <https://rsync.samba.org/> and its github
project is <https://github.com/WayneD/rsync>.
project is <https://github.com/RsyncProject/rsync>.
## THANKS
@@ -1237,8 +1282,8 @@ Thanks to Karsten Thygesen for his many suggestions and documentation!
## AUTHOR
Rsync was written by Andrew Tridgell and Paul Mackerras. Many people have
later contributed to it.
Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
people from around the world have helped to maintain and improve it.
Mailing lists for support and development are available at
<https://lists.samba.org/>.

485
runtests.py Executable file
View File

@@ -0,0 +1,485 @@
#!/usr/bin/env python3
# Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
# Copyright (C) 2003-2022 Wayne Davison
# Copyright (C) 2026 Andrew Tridgell
#
# Rewrite of runtests.sh in Python (runtests.sh is now deprecated).
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version
# 2 as published by the Free Software Foundation.
"""rsync test runner.
Invokes test scripts from testsuite/ and reports results.
Can be called by 'make check' or directly.
Usage:
./runtests.py [options] [TEST ...]
Each TEST is a test name (e.g. 'delete') or glob pattern (e.g. 'xattr*').
If no tests are specified, all tests are run.
"""
import argparse
import concurrent.futures
import glob
import os
import subprocess
import sys
import threading
def parse_args():
p = argparse.ArgumentParser(description='Run rsync test suite')
p.add_argument('tests', nargs='*', metavar='TEST',
help='Test names or patterns to run (default: all)')
p.add_argument('-j', '--parallel', type=int, default=1, metavar='N',
help='Run up to N tests in parallel (default: 1)')
p.add_argument('--valgrind', action='store_true',
help='Run rsync under valgrind (logs to per-process files)')
p.add_argument('--valgrind-opts', default='', metavar='OPTS',
help='Extra valgrind options (e.g. "--leak-check=full")')
p.add_argument('--preserve-scratch', action='store_true',
help='Keep scratch directories after tests complete')
p.add_argument('--log-level', type=int, default=1, metavar='N',
help='Verbosity level 1-10 (default: 1)')
p.add_argument('--always-log', action='store_true',
help='Show test logs even for passing tests')
p.add_argument('--stop-on-fail', action='store_true',
help='Stop after first test failure')
p.add_argument('--timeout', type=int, default=300, metavar='SECS',
help='Per-test timeout in seconds (default: 300)')
p.add_argument('--rsync-bin', default=None, metavar='PATH',
help='Path to rsync binary (default: ./rsync)')
p.add_argument('--tooldir', default=None, metavar='DIR',
help='Tool/build directory (default: cwd)')
p.add_argument('--srcdir', default=None, metavar='DIR',
help='Source directory (default: script directory)')
p.add_argument('--protocol', type=int, default=None, metavar='VER',
help='Force protocol version (adds --protocol=VER to rsync)')
p.add_argument('--expect-skipped', default=None, metavar='LIST',
help='Comma-separated list of expected-skipped tests')
return p.parse_args()
def find_setfacl_nodef(scratchbase):
"""Determine the setfacl command to remove default ACLs."""
for cmd in [
['setacl', '-k', 'u::7,g::5,o:5', scratchbase],
['setfacl', '-k', scratchbase],
['setfacl', '-s', 'u::7,g::5,o:5', scratchbase],
]:
try:
subprocess.run(cmd, capture_output=True, timeout=5)
return cmd[:2] if cmd[0] == 'setacl' else cmd[:2]
except (FileNotFoundError, subprocess.TimeoutExpired):
continue
try:
r = subprocess.run(['setfacl', '--help'], capture_output=True, text=True, timeout=5)
if '-k,' in r.stdout or '-k,' in r.stderr:
return ['setfacl', '-k']
except (FileNotFoundError, subprocess.TimeoutExpired):
pass
return None
def get_tls_args(config_h):
"""Determine TLS_ARGS from config.h."""
args = ''
try:
with open(config_h) as f:
text = f.read()
if '#define HAVE_LUTIMES 1' in text:
args += ' -l'
if '#undef CHOWN_MODIFIES_SYMLINK' in text:
args += ' -L'
except FileNotFoundError:
pass
return args.strip()
def read_shconfig(path):
"""Read shell config variables from shconfig."""
env = {}
try:
with open(path) as f:
for line in f:
line = line.strip()
if line.startswith('#') or line.startswith('export') or not line:
continue
if '=' in line:
k, _, v = line.partition('=')
env[k.strip()] = v.strip().strip('"')
except FileNotFoundError:
pass
return env
def get_testuser():
"""Determine the current test user."""
for cmd in ['/usr/bin/whoami', '/usr/ucb/whoami', '/bin/whoami']:
if os.path.isfile(cmd):
try:
return subprocess.check_output([cmd], text=True).strip()
except subprocess.CalledProcessError:
pass
try:
return subprocess.check_output(['id', '-un'], text=True).strip()
except (FileNotFoundError, subprocess.CalledProcessError):
return os.environ.get('LOGNAME', os.environ.get('USER', 'UNKNOWN'))
def prep_scratch(scratchdir, srcdir, tooldir, setfacl_nodef):
"""Prepare a scratch directory for a test."""
if os.path.isdir(scratchdir):
subprocess.run(['chmod', '-R', 'u+rwX', scratchdir], capture_output=True)
subprocess.run(['rm', '-rf', scratchdir], capture_output=True)
os.makedirs(scratchdir, exist_ok=True)
if setfacl_nodef:
subprocess.run(setfacl_nodef + [scratchdir], capture_output=True)
try:
os.chmod(scratchdir, os.stat(scratchdir).st_mode & ~0o2000) # clear setgid
except OSError:
pass
src_link = os.path.join(scratchdir, 'src')
if not os.path.exists(src_link):
if os.path.isabs(srcdir):
os.symlink(srcdir, src_link)
else:
os.symlink(os.path.join(tooldir, srcdir), src_link)
def collect_tests(suitedir, patterns):
"""Collect test scripts matching the given patterns."""
if not patterns:
tests = sorted(glob.glob(os.path.join(suitedir, '*.test')))
else:
tests = []
for pat in patterns:
if not pat.endswith('.test'):
pat = pat + '.test'
matches = sorted(glob.glob(os.path.join(suitedir, pat)))
tests.extend(matches)
return tests
def build_rsync_cmd(rsync_bin, args, scratchbase):
"""Build the RSYNC command string for tests."""
parts = []
if args.valgrind:
vlog = os.path.join(scratchbase, 'valgrind.%p.log')
vopts = f'--log-file={vlog}'
if args.valgrind_opts:
vopts += ' ' + args.valgrind_opts
parts.append(f'valgrind {vopts}')
parts.append(rsync_bin)
if args.protocol is not None:
parts.append(f'--protocol={args.protocol}')
return ' '.join(parts)
class TestResult:
"""Result of a single test execution."""
__slots__ = ('testbase', 'result', 'output', 'skipped_reason')
def __init__(self, testbase, result, output='', skipped_reason=''):
self.testbase = testbase
self.result = result
self.output = output
self.skipped_reason = skipped_reason
def run_one_test(testscript, testbase, scratchdir, base_env, timeout,
srcdir, tooldir, setfacl_nodef, always_log):
"""Run a single test. Returns a TestResult.
This function is safe to call from multiple threads — it uses only
per-test state (unique scratchdir, copy of env).
"""
prep_scratch(scratchdir, srcdir, tooldir, setfacl_nodef)
env = base_env.copy()
env['scratchdir'] = scratchdir
logfile = os.path.join(scratchdir, 'test.log')
try:
with open(logfile, 'w') as log:
proc = subprocess.run(
['sh', '-e', testscript],
stdout=log, stderr=subprocess.STDOUT,
env=env, timeout=timeout,
cwd=env.get('TOOLDIR', '.')
)
result = proc.returncode
except subprocess.TimeoutExpired:
result = 1
with open(logfile, 'a') as log:
log.write(f"\nTIMEOUT: test took over {timeout} seconds\n")
# Build output text
output_parts = []
show_log = always_log or (result not in (0, 77, 78))
if show_log:
output_parts.append(f'----- {testbase} log follows')
try:
with open(logfile) as f:
output_parts.append(f.read().rstrip())
except FileNotFoundError:
pass
output_parts.append(f'----- {testbase} log ends')
rsyncd_log = os.path.join(scratchdir, 'rsyncd.log')
if os.path.isfile(rsyncd_log):
output_parts.append(f'----- {testbase} rsyncd.log follows')
with open(rsyncd_log) as f:
output_parts.append(f.read().rstrip())
output_parts.append(f'----- {testbase} rsyncd.log ends')
skipped_reason = ''
if result == 0:
output_parts.append(f'PASS {testbase}')
elif result == 77:
whyfile = os.path.join(scratchdir, 'whyskipped')
try:
with open(whyfile) as f:
skipped_reason = f.read().strip()
except FileNotFoundError:
pass
output_parts.append(f'SKIP {testbase} ({skipped_reason})')
elif result == 78:
output_parts.append(f'XFAIL {testbase}')
else:
output_parts.append(f'FAIL {testbase}')
return TestResult(testbase, result, '\n'.join(output_parts), skipped_reason)
# Lock for serializing output in parallel mode
_print_lock = threading.Lock()
def main():
args = parse_args()
# Also accept legacy environment variables
if args.preserve_scratch or os.environ.get('preserve_scratch') == 'yes':
args.preserve_scratch = True
if args.log_level == 1:
args.log_level = int(os.environ.get('loglevel', '1'))
if args.expect_skipped is None:
args.expect_skipped = os.environ.get('RSYNC_EXPECT_SKIPPED', 'IGNORE')
if os.environ.get('whichtests'):
args.tests = [os.environ['whichtests']]
# Determine directories
tooldir = args.tooldir or os.environ.get('TOOLDIR') or os.getcwd()
script_path = os.path.dirname(os.path.abspath(__file__))
srcdir = args.srcdir or script_path
if not srcdir or srcdir == '.':
srcdir = tooldir
rsync_bin = args.rsync_bin or os.environ.get('rsync_bin') or os.path.join(tooldir, 'rsync')
suitedir = os.path.join(srcdir, 'testsuite')
scratchbase = os.path.join(os.environ.get('scratchbase', tooldir), 'testtmp')
os.makedirs(scratchbase, exist_ok=True)
shconfig = read_shconfig(os.path.join(tooldir, 'shconfig'))
tls_args = get_tls_args(os.path.join(tooldir, 'config.h'))
setfacl_nodef = find_setfacl_nodef(scratchbase)
rsync_cmd = build_rsync_cmd(rsync_bin, args, scratchbase)
if not os.path.isfile(rsync_bin):
sys.stderr.write(f"rsync_bin {rsync_bin} is not a file\n")
sys.exit(2)
if not os.path.isdir(srcdir):
sys.stderr.write(f"srcdir {srcdir} is not a directory\n")
sys.exit(2)
# Helper programs the test scripts invoke directly. Missing any of these
# would cause many tests to fail with confusing "not found" errors, so
# check up front and point the user at the make target that builds them.
required_helpers = ['tls', 'trimslash', 't_unsafe', 't_chmod_secure',
't_secure_relpath',
'wildtest', 'getgroups', 'getfsdev']
missing = [h for h in required_helpers
if not os.path.isfile(os.path.join(tooldir, h))]
if missing:
sys.stderr.write(
f"runtests.py: missing test helper program(s) in {tooldir}: "
f"{', '.join(missing)}\n"
f"Build them with: make {' '.join(missing)}\n"
f"or run the full test target: make check\n"
)
sys.exit(2)
testuser = get_testuser()
# Print header
print('=' * 60)
print(f'{sys.argv[0]} running in {tooldir}')
print(f' rsync_bin={rsync_cmd}')
print(f' srcdir={srcdir}')
print(f' TLS_ARGS={tls_args}')
print(f' testuser={testuser}')
print(f' os={subprocess.check_output(["uname", "-a"], text=True).strip()}')
print(f' preserve_scratch={"yes" if args.preserve_scratch else "no"}')
if args.valgrind:
print(f' valgrind=enabled (logs in valgrind.*.log)')
if args.parallel > 1:
print(f' parallel={args.parallel}')
print(f' scratchbase={scratchbase}')
# Build base environment for test scripts
path = os.environ.get('PATH', '')
if os.path.isdir('/usr/xpg4/bin'):
path = '/usr/xpg4/bin:' + path
base_env = os.environ.copy()
base_env.update({
'PATH': path,
'POSIXLY_CORRECT': '1',
'TOOLDIR': tooldir,
'srcdir': srcdir,
'RSYNC': rsync_cmd,
'TLS_ARGS': tls_args,
'RUNSHFLAGS': '-e',
'scratchbase': scratchbase,
'suitedir': suitedir,
'TESTRUN_TIMEOUT': str(args.timeout),
'HOME': scratchbase,
})
for k, v in shconfig.items():
if v:
base_env[k] = v
if setfacl_nodef:
base_env['setfacl_nodef'] = ' '.join(setfacl_nodef)
else:
base_env['setfacl_nodef'] = 'true'
if args.log_level > 8:
base_env['RUNSHFLAGS'] = '-e -x'
# Collect tests
tests = collect_tests(suitedir, args.tests)
full_run = len(args.tests) == 0
# Record test order for consistent skipped-list output
test_order = {os.path.basename(t).replace('.test', ''): i for i, t in enumerate(tests)}
passed = 0
failed = 0
skipped = 0
skipped_list = []
def process_result(tr):
"""Process a TestResult and update counters. Returns True if test failed."""
nonlocal passed, failed, skipped
with _print_lock:
if tr.output:
print(tr.output)
scratchdir = os.path.join(scratchbase, tr.testbase)
if tr.result == 0:
passed += 1
if not args.preserve_scratch and os.path.isdir(scratchdir):
subprocess.run(['rm', '-rf', scratchdir], capture_output=True)
return False
elif tr.result == 77:
skipped_list.append(tr.testbase)
skipped += 1
if not args.preserve_scratch and os.path.isdir(scratchdir):
subprocess.run(['rm', '-rf', scratchdir], capture_output=True)
return False
elif tr.result == 78:
failed += 1
return True
else:
failed += 1
return True
if args.parallel > 1:
# Parallel execution
with concurrent.futures.ThreadPoolExecutor(max_workers=args.parallel) as executor:
futures = {}
for testscript in tests:
testbase = os.path.basename(testscript).replace('.test', '')
scratchdir = os.path.join(scratchbase, testbase)
timeout = 600 if 'hardlinks' in testbase else args.timeout
f = executor.submit(
run_one_test, testscript, testbase, scratchdir,
base_env, timeout, srcdir, tooldir, setfacl_nodef,
args.always_log
)
futures[f] = testbase
for f in concurrent.futures.as_completed(futures):
tr = f.result()
is_fail = process_result(tr)
if is_fail and args.stop_on_fail:
# Cancel pending futures
for pending in futures:
pending.cancel()
break
else:
# Sequential execution
for testscript in tests:
testbase = os.path.basename(testscript).replace('.test', '')
scratchdir = os.path.join(scratchbase, testbase)
timeout = 600 if 'hardlinks' in testbase else args.timeout
tr = run_one_test(
testscript, testbase, scratchdir,
base_env, timeout, srcdir, tooldir, setfacl_nodef,
args.always_log
)
is_fail = process_result(tr)
if is_fail and args.stop_on_fail:
break
# Check valgrind logs for errors
vg_errors = 0
if args.valgrind:
for vlog in sorted(glob.glob(os.path.join(scratchbase, 'valgrind.*.log'))):
try:
with open(vlog) as f:
content = f.read()
for line in content.splitlines():
if 'ERROR SUMMARY:' in line and 'ERROR SUMMARY: 0 errors' not in line:
vg_errors += 1
print(f'----- valgrind errors in {os.path.basename(vlog)}:')
print(content)
break
except FileNotFoundError:
pass
# Summary
print('-' * 60)
print('----- overall results:')
print(f' {passed} passed')
if failed > 0:
print(f' {failed} failed')
if skipped > 0:
print(f' {skipped} skipped')
if vg_errors > 0:
print(f' {vg_errors} valgrind error(s) found (see logs in {scratchbase})')
skipped_str = ','.join(sorted(skipped_list, key=lambda x: test_order.get(x, 0)))
if full_run and args.expect_skipped != 'IGNORE':
print('----- skipped results:')
print(f' expected: {args.expect_skipped}')
print(f' got: {skipped_str}')
else:
skipped_str = ''
args.expect_skipped = ''
print('-' * 60)
exit_code = failed + vg_errors
if exit_code == 0 and skipped_str != args.expect_skipped:
exit_code = 1
print(f'overall result is {exit_code}')
sys.exit(exit_code)
if __name__ == '__main__':
main()

View File

@@ -1,360 +0,0 @@
#! /bin/sh
# Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
# 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 version
# 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# -------------------------------------------------------------------------
# rsync top-level test script -- this invokes all the other more
# detailed tests in order. This script can either be called by `make
# check' or `make installcheck'. `check' runs against the copies of
# the program and other files in the build directory, and
# `installcheck' against the installed copy of the program.
# It can also be called on a single test file using a run like this:
#
# preserve_scratch=yes whichtests=itemize.test ./runtests.sh
# In either case we need to also be able to find the source directory,
# since we read test scripts and possibly other information from
# there.
# Whenever possible, informational messages are written to stdout and
# error messages to stderr. They're separated out by the build farm
# display scripts.
# According to the GNU autoconf manual, the only valid place to set up
# directory locations is through Make, since users are allowed to (try
# to) change their mind on the Make command line. So, Make has to
# pass in all the values we need.
# For other configured settings we read ./config.sh, which tells us
# about shell commands on this machine and similar things.
# rsync_bin gives the location of the rsync binary. This is either
# builddir/rsync if we're testing an uninstalled copy, or
# install_prefix/bin/rsync if we're testing an installed copy. On the
# build farm rsync will be installed, but into a scratch /usr.
# srcdir gives the location of the source tree, which lets us find the
# build scripts. At the moment we assume we are invoked from the
# source directory.
# This script must be invoked from the build directory.
# A scratch directory, 'testtmp', is used in the build directory to
# hold per-test subdirectories.
# This script also uses the $loglevel environment variable. 1 is the
# default value, and 10 the most verbose. You can set this from the
# Make command line. It's also set by the build farm to give more
# detail for failing builds.
# -------------------------------------------------------------------------
# NOTES FOR TEST CASES:
# Each test case runs in its own shell.
# Exit codes from tests:
# 1 tests failed
# 2 error in starting tests
# 77 this test skipped (random value unlikely to happen by chance, same as
# automake)
# HOWEVER, the overall exit code to the farm is different: we return
# the *number of tests that failed*, so that it will show up nicely in
# the overall summary.
# rsync.fns contains some general setup functions and definitions.
# -------------------------------------------------------------------------
# NOTES ON PORTABILITY:
# Both this script and the Makefile have to be pretty conservative
# about which Unix features they use.
# We cannot count on Make exporting variables to commands, unless
# they're explicitly given on the command line.
# Also, we can't count on 'cp -a' or 'mkdir -p', although they're
# pretty handy (see function makepath for the latter).
# I think some of the GNU documentation suggests that we shouldn't
# rely on shell functions. However, the Bash manual seems to say that
# they're in POSIX 1003.2, and since the build farm relies on them
# they're probably working on most machines we really care about.
# You cannot use "function foo {" syntax, but must instead say "foo()
# {", or it breaks on FreeBSD.
# BSD machines tend not to have "head" or "seq".
# You cannot do "export VAR=VALUE" all on one line; the export must be
# separate from the assignment. (SCO SysV)
# Don't rely on grep -q, as that doesn't work everywhere -- just redirect
# stdout to /dev/null to keep it quiet.
# -------------------------------------------------------------------------
# STILL TO DO:
# We need a good protection against tests that hang indefinitely.
# Perhaps some combination of starting them in the background, wait,
# and kill?
# Perhaps we need a common way to cleanup tests. At the moment just
# clobbering the directory when we're done should be enough.
# If any of the targets fail, then (GNU?) Make returns 2, instead of
# the return code from the failing command. This is fine, but it
# means that the build farm just shows "2" for failed tests, not the
# number of tests that actually failed. For more details we might
# need to grovel through the log files to find a line saying how many
# failed.
set -e
. "./shconfig"
RUNSHFLAGS='-e'
export RUNSHFLAGS
# for Solaris
if [ -d /usr/xpg4/bin ]; then
PATH="/usr/xpg4/bin/:$PATH"
export PATH
fi
if [ "x$loglevel" != x ] && [ "$loglevel" -gt 8 ]; then
if set -x; then
# If it doesn't work the first time, don't keep trying.
RUNSHFLAGS="$RUNSHFLAGS -x"
fi
fi
POSIXLY_CORRECT=1
if test x"$TOOLDIR" = x; then
TOOLDIR=`pwd`
fi
srcdir=`dirname $0`
if test x"$srcdir" = x || test x"$srcdir" = x.; then
srcdir="$TOOLDIR"
fi
if test x"$rsync_bin" = x; then
rsync_bin="$TOOLDIR/rsync"
fi
# This allows the user to specify extra rsync options -- use carefully!
RSYNC="$rsync_bin $*"
#RSYNC="valgrind $rsync_bin $*"
TLS_ARGS=''
if grep -E '^#define HAVE_LUTIMES 1' config.h >/dev/null; then
TLS_ARGS="$TLS_ARGS -l"
fi
if grep -E '#undef CHOWN_MODIFIES_SYMLINK' config.h >/dev/null; then
TLS_ARGS="$TLS_ARGS -L"
fi
export POSIXLY_CORRECT TOOLDIR srcdir RSYNC TLS_ARGS
echo "============================================================"
echo "$0 running in $TOOLDIR"
echo " rsync_bin=$RSYNC"
echo " srcdir=$srcdir"
echo " TLS_ARGS=$TLS_ARGS"
if [ -f /usr/bin/whoami ]; then
testuser=`/usr/bin/whoami`
elif [ -f /usr/ucb/whoami ]; then
testuser=`/usr/ucb/whoami`
elif [ -f /bin/whoami ]; then
testuser=`/bin/whoami`
else
testuser=`id -un 2>/dev/null || echo ${LOGNAME:-${USERNAME:-${USER:-'UNKNOWN'}}}`
fi
echo " testuser=$testuser"
echo " os=`uname -a`"
# It must be "yes", not just nonnull
if [ "x$preserve_scratch" = xyes ]; then
echo " preserve_scratch=yes"
else
echo " preserve_scratch=no"
fi
# Check if setacl/setfacl is around and if it supports the -k or -s option.
if setacl -k u::7,g::5,o:5 testsuite 2>/dev/null; then
setfacl_nodef='setacl -k'
elif setfacl --help 2>&1 | grep ' -k,\|\[-[a-z]*k' >/dev/null; then
setfacl_nodef='setfacl -k'
elif setfacl -s u::7,g::5,o:5 testsuite 2>/dev/null; then
setfacl_nodef='setfacl -s u::7,g::5,o:5'
else
# The "true" command runs successfully, but does nothing.
setfacl_nodef=true
fi
export setfacl_nodef
if [ ! -f "$rsync_bin" ]; then
echo "rsync_bin $rsync_bin is not a file" >&2
exit 2
fi
if [ ! -d "$srcdir" ]; then
echo "srcdir $srcdir is not a directory" >&2
exit 2
fi
expect_skipped="${RSYNC_EXPECT_SKIPPED-IGNORE}"
skipped_list=''
skipped=0
missing=0
passed=0
failed=0
# Directory that holds the other test subdirs. We create separate dirs
# inside for each test case, so that they can be left behind in case of
# failure to aid investigation. We don't remove the testtmp subdir at
# the end so that it can be configured as a symlink to a filesystem that
# has ACLs and xattr support enabled (if desired).
scratchbase="${scratchbase:-$TOOLDIR}"/testtmp
echo " scratchbase=$scratchbase"
[ -d "$scratchbase" ] || mkdir "$scratchbase"
suitedir="$srcdir/testsuite"
TESTRUN_TIMEOUT=300
export scratchdir suitedir TESTRUN_TIMEOUT
prep_scratch() {
[ -d "$scratchdir" ] && chmod -R u+rwX "$scratchdir" && rm -rf "$scratchdir"
mkdir "$scratchdir"
# Get rid of default ACLs and dir-setgid to avoid confusing some tests.
$setfacl_nodef "$scratchdir" 2>/dev/null || true
chmod g-s "$scratchdir"
case "$srcdir" in
/*) ln -s "$srcdir" "$scratchdir/src" ;;
*) ln -s "$TOOLDIR/$srcdir" "$scratchdir/src" ;;
esac
return 0
}
maybe_discard_scratch() {
[ x"$preserve_scratch" != xyes ] && [ -d "$scratchdir" ] && rm -rf "$scratchdir"
return 0
}
if [ "x$whichtests" = x ]; then
whichtests="*.test"
full_run=yes
else
full_run=no
fi
for testscript in $suitedir/$whichtests; do
testbase=`echo $testscript | sed -e 's!.*/!!' -e 's/.test\$//'`
scratchdir="$scratchbase/$testbase"
prep_scratch
case "$testscript" in
*hardlinks*) TESTRUN_TIMEOUT=600 ;;
*) TESTRUN_TIMEOUT=300 ;;
esac
set +e
"$TOOLDIR/"testrun $RUNSHFLAGS "$testscript" >"$scratchdir/test.log" 2>&1
result=$?
set -e
if [ "x$always_log" = xyes ] || ( [ $result != 0 ] && [ $result != 77 ] && [ $result != 78 ] )
then
echo "----- $testbase log follows"
cat "$scratchdir/test.log"
echo "----- $testbase log ends"
if [ -f "$scratchdir/rsyncd.log" ]; then
echo "----- $testbase rsyncd.log follows"
cat "$scratchdir/rsyncd.log"
echo "----- $testbase rsyncd.log ends"
fi
fi
case $result in
0)
echo "PASS $testbase"
passed=`expr $passed + 1`
maybe_discard_scratch
;;
77)
# backticks will fill the whole file onto one line, which is a feature
whyskipped=`cat "$scratchdir/whyskipped"`
echo "SKIP $testbase ($whyskipped)"
skipped_list="$skipped_list,$testbase"
skipped=`expr $skipped + 1`
maybe_discard_scratch
;;
78)
# It failed, but we expected that. don't dump out error logs,
# because most users won't want to see them. But do leave
# the working directory around.
echo "XFAIL $testbase"
failed=`expr $failed + 1`
;;
*)
echo "FAIL $testbase"
failed=`expr $failed + 1`
if [ "x$nopersist" = xyes ]; then
exit 1
fi
esac
done
echo '------------------------------------------------------------'
echo "----- overall results:"
echo " $passed passed"
[ "$failed" -gt 0 ] && echo " $failed failed"
[ "$skipped" -gt 0 ] && echo " $skipped skipped"
[ "$missing" -gt 0 ] && echo " $missing missing"
if [ "$full_run" = yes ] && [ "$expect_skipped" != IGNORE ]; then
skipped_list=`echo "$skipped_list" | sed 's/^,//'`
echo "----- skipped results:"
echo " expected: $expect_skipped"
echo " got: $skipped_list"
else
skipped_list=''
expect_skipped=''
fi
echo '------------------------------------------------------------'
# OK, so expr exits with 0 if the result is neither null nor zero; and
# 1 if the expression is null or zero. This is the opposite of what
# we want, and if we just call expr then this script will always fail,
# because -e is set.
result=`expr $failed + $missing || true`
if [ "$result" = 0 ] && [ "$skipped_list" != "$expect_skipped" ]; then
result=1
fi
echo "overall result is $result"
exit $result

View File

@@ -25,11 +25,13 @@
extern int do_xfers;
extern int am_server;
extern int am_daemon;
extern int local_server;
extern int inc_recurse;
extern int log_before_transfer;
extern int stdout_format_has_i;
extern int logfile_format_has_i;
extern int want_xattr_optim;
extern int xfer_sum_len;
extern int csum_length;
extern int append_mode;
extern int copy_links;
@@ -46,11 +48,14 @@ extern int make_backups;
extern int inplace;
extern int inplace_partial;
extern int batch_fd;
extern int use_secure_symlinks;
extern char *module_dir;
extern int write_batch;
extern int file_old_total;
extern BOOL want_progress_now;
extern struct stats stats;
extern struct file_list *cur_flist, *first_flist, *dir_flist;
extern char num_dev_ino_buf[4 + 8 + 8];
BOOL extra_flist_sending_enabled;
@@ -92,10 +97,11 @@ static struct sum_struct *receive_sums(int f)
return(s);
s->sums = new_array(struct sum_buf, s->count);
s->sum2_array = new_array(char, (size_t)s->count * xfer_sum_len);
for (i = 0; i < s->count; i++) {
s->sums[i].sum1 = read_int(f);
read_buf(f, s->sums[i].sum2, s->s2length);
read_buf(f, sum2_at(s, i), s->s2length);
s->sums[i].offset = offset;
s->sums[i].flags = 0;
@@ -134,6 +140,8 @@ void successful_send(int ndx)
return;
flist = flist_for_ndx(ndx, "successful_send");
if (ndx < flist->ndx_start)
exit_cleanup(RERR_PROTOCOL);
file = flist->files[ndx - flist->ndx_start];
if (!change_pathname(file, NULL, 0))
return;
@@ -144,6 +152,13 @@ void successful_send(int ndx)
goto failed;
}
if (local_server
&& (int64)st.st_dev == IVAL64(num_dev_ino_buf, 4)
&& (int64)st.st_ino == IVAL64(num_dev_ino_buf, 4 + 8)) {
rprintf(FERROR_XFER, "ERROR: Skipping sender remove of destination file: %s\n", fname);
return;
}
if (st.st_size != F_LENGTH(file) || st.st_mtime != file->modtime
#ifdef ST_MTIME_NSEC
|| (NSEC_BUMP(file) && (uint32)st.ST_MTIME_NSEC != F_MOD_NSEC(file))
@@ -251,6 +266,8 @@ void send_files(int f_in, int f_out)
if (ndx - cur_flist->ndx_start >= 0)
file = cur_flist->files[ndx - cur_flist->ndx_start];
else if (cur_flist->parent_ndx < 0)
exit_cleanup(RERR_PROTOCOL);
else
file = dir_flist->files[cur_flist->parent_ndx];
if (F_PATHNAME(file)) {
@@ -339,7 +356,25 @@ void send_files(int f_in, int f_out)
exit_cleanup(RERR_PROTOCOL);
}
fd = do_open(fname, O_RDONLY, 0);
if (use_secure_symlinks) {
/* Open from module root to prevent TOCTOU race where
* change_pathname's chdir follows a directory symlink.
* Reconstruct the full path relative to module_dir
* from F_PATHNAME (path) and f_name (fname). */
char secure_path[MAXPATHLEN];
int slen = snprintf(secure_path, sizeof secure_path, "%s%s%s", path, slash, fname);
if (slen >= (int)sizeof secure_path) {
io_error |= IOERR_GENERAL;
rprintf(FERROR_XFER, "path too long: %s%s%s\n", path, slash, fname);
free_sums(s);
if (protocol_version >= 30)
send_msg_int(MSG_NO_SEND, ndx);
continue;
}
fd = secure_relative_open(module_dir, secure_path, O_RDONLY, 0);
} else {
fd = do_open_checklinks(fname);
}
if (fd == -1) {
if (errno == ENOENT) {
enum logcode c = am_daemon && protocol_version < 28 ? FERROR : FWARNING;

View File

@@ -68,8 +68,8 @@
#endif
// Missing from the headers on gcc 6 and older, clang 8 and older
typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(1)));
typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(1)));
typedef long long __m128i_u __attribute__((__vector_size__(16), __may_alias__, __aligned__(16)));
typedef long long __m256i_u __attribute__((__vector_size__(32), __may_alias__, __aligned__(16)));
/* Compatibility macros to let our SSSE3 algorithm run with only SSE2.
These used to be neat individual functions with target attributes switching between SSE2 and SSSE3 implementations
@@ -347,8 +347,7 @@ __attribute__ ((target("avx2"))) MVSTATIC int32 get_checksum1_avx2_64(schar* buf
__m128i tmp = _mm_load_si128((__m128i*) mul_t1_buf);
__m256i mul_t1 = _mm256_cvtepu8_epi16(tmp);
__m256i mul_const = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(4 | (3 << 8) | (2 << 16) | (1 << 24)));
__m256i mul_one;
mul_one = _mm256_abs_epi8(_mm256_cmpeq_epi16(mul_one,mul_one)); // set all vector elements to 1
__m256i mul_one = _mm256_set1_epi8(1);
for (; i < (len-64); i+=64) {
// Load ... 4*[int8*16]
@@ -548,6 +547,118 @@ int main() {
#pragma clang optimize on
#endif /* BENCHMARK_SIMD_CHECKSUM1 */
#ifdef TEST_SIMD_CHECKSUM1
static uint32 checksum_via_default(char *buf, int32 len)
{
uint32 s1 = 0, s2 = 0;
get_checksum1_default_1((schar*)buf, len, 0, &s1, &s2);
return (s1 & 0xffff) + (s2 << 16);
}
static uint32 checksum_via_sse2(char *buf, int32 len)
{
int32 i;
uint32 s1 = 0, s2 = 0;
i = get_checksum1_sse2_32((schar*)buf, len, 0, &s1, &s2);
get_checksum1_default_1((schar*)buf, len, i, &s1, &s2);
return (s1 & 0xffff) + (s2 << 16);
}
static uint32 checksum_via_ssse3(char *buf, int32 len)
{
int32 i;
uint32 s1 = 0, s2 = 0;
i = get_checksum1_ssse3_32((schar*)buf, len, 0, &s1, &s2);
get_checksum1_default_1((schar*)buf, len, i, &s1, &s2);
return (s1 & 0xffff) + (s2 << 16);
}
static uint32 checksum_via_avx2(char *buf, int32 len)
{
int32 i;
uint32 s1 = 0, s2 = 0;
#ifdef USE_ROLL_ASM
i = get_checksum1_avx2_asm((schar*)buf, len, 0, &s1, &s2);
#else
i = get_checksum1_avx2_64((schar*)buf, len, 0, &s1, &s2);
#endif
get_checksum1_default_1((schar*)buf, len, i, &s1, &s2);
return (s1 & 0xffff) + (s2 << 16);
}
int main()
{
static const int sizes[] = {1, 4, 31, 32, 33, 63, 64, 65, 128, 129, 256, 700, 1024, 4096, 65536};
int num_sizes = sizeof(sizes) / sizeof(sizes[0]);
int max_size = sizes[num_sizes - 1];
int failures = 0;
/* Allocate with extra bytes for unaligned test */
unsigned char *raw = (unsigned char *)malloc(max_size + 64 + 1);
if (!raw) {
fprintf(stderr, "malloc failed\n");
return 1;
}
/* Fill with deterministic data */
for (int i = 0; i < max_size + 64 + 1; i++)
raw[i] = (i + (i % 3) + (i % 11)) % 256;
/* Test with aligned buffer (64-byte aligned) */
unsigned char *aligned = raw + (64 - ((uintptr_t)raw % 64));
/* Test with unaligned buffer (+1 byte offset) */
unsigned char *unaligned = aligned + 1;
struct { const char *name; unsigned char *buf; } buffers[] = {
{"aligned", aligned},
{"unaligned", unaligned},
};
for (int b = 0; b < 2; b++) {
char *buf = (char *)buffers[b].buf;
const char *bname = buffers[b].name;
for (int s = 0; s < num_sizes; s++) {
int32 len = sizes[s];
uint32 ref = checksum_via_default(buf, len);
uint32 cs_sse2 = checksum_via_sse2(buf, len);
uint32 cs_ssse3 = checksum_via_ssse3(buf, len);
uint32 cs_avx2 = checksum_via_avx2(buf, len);
uint32 cs_auto = get_checksum1(buf, len);
if (cs_sse2 != ref) {
printf("FAIL %-9s size=%5d: SSE2=%08x ref=%08x\n", bname, len, cs_sse2, ref);
failures++;
}
if (cs_ssse3 != ref) {
printf("FAIL %-9s size=%5d: SSSE3=%08x ref=%08x\n", bname, len, cs_ssse3, ref);
failures++;
}
if (cs_avx2 != ref) {
printf("FAIL %-9s size=%5d: AVX2=%08x ref=%08x\n", bname, len, cs_avx2, ref);
failures++;
}
if (cs_auto != ref) {
printf("FAIL %-9s size=%5d: auto=%08x ref=%08x\n", bname, len, cs_auto, ref);
failures++;
}
}
}
free(raw);
if (failures) {
printf("%d checksum mismatches!\n", failures);
return 1;
}
printf("All SIMD checksum tests passed.\n");
return 0;
}
#endif /* TEST_SIMD_CHECKSUM1 */
#endif /* } USE_ROLL_SIMD */
#endif /* } __cplusplus */
#endif /* } __x86_64__ */

View File

@@ -47,21 +47,23 @@ static struct sigaction sigact;
static int sock_exec(const char *prog);
#define PROXY_BUF_SIZE 1024
/* Establish a proxy connection on an open socket to a web proxy by using the
* CONNECT method. If proxy_user and proxy_pass are not NULL, they are used to
* authenticate to the proxy using the "Basic" proxy-authorization protocol. */
static int establish_proxy_connection(int fd, char *host, int port, char *proxy_user, char *proxy_pass)
{
char *cp, buffer[1024];
char *authhdr, authbuf[1024];
char *cp, buffer[PROXY_BUF_SIZE + 1];
char *authhdr, authbuf[PROXY_BUF_SIZE + 1];
int len;
if (proxy_user && proxy_pass) {
stringjoin(buffer, sizeof buffer,
stringjoin(buffer, PROXY_BUF_SIZE,
proxy_user, ":", proxy_pass, NULL);
len = strlen(buffer);
if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
if ((len*8 + 5) / 6 >= PROXY_BUF_SIZE - 3) {
rprintf(FERROR,
"authentication information is too long\n");
return -1;
@@ -74,14 +76,14 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
authhdr = "";
}
len = snprintf(buffer, sizeof buffer, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
assert(len > 0 && len < (int)sizeof buffer);
len = snprintf(buffer, PROXY_BUF_SIZE, "CONNECT %s:%d HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
assert(len > 0 && len < PROXY_BUF_SIZE);
if (write(fd, buffer, len) != len) {
rsyserr(FERROR, errno, "failed to write to proxy");
return -1;
}
for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE - 1]; cp++) {
if (read(fd, cp, 1) != 1) {
rsyserr(FERROR, errno, "failed to read from proxy");
return -1;
@@ -90,11 +92,13 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
break;
}
if (*cp != '\n')
cp++;
*cp-- = '\0';
if (*cp == '\r')
*cp = '\0';
if (cp == &buffer[PROXY_BUF_SIZE - 1]) {
rprintf(FERROR, "proxy response line too long\n");
return -1;
}
*cp = '\0';
if (cp > buffer && cp[-1] == '\r')
cp[-1] = '\0';
if (strncmp(buffer, "HTTP/", 5) != 0) {
rprintf(FERROR, "bad response from proxy -- %s\n",
buffer);
@@ -110,7 +114,7 @@ static int establish_proxy_connection(int fd, char *host, int port, char *proxy_
}
/* throw away the rest of the HTTP header */
while (1) {
for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE]; cp++) {
if (read(fd, cp, 1) != 1) {
rsyserr(FERROR, errno,
"failed to read from proxy");

View File

@@ -1,42 +1,63 @@
#!/usr/bin/env perl
#
#!/usr/bin/env python3
# This script finds all CVS/Entries files in the current directory and below
# and creates a local .cvsinclude file with non-inherited rules including each
# checked-in file. Then, use this option whenever using --cvs-exclude (-C):
#
# -f ': .cvsinclude'
# -f ': .cvsinclude'
#
# That ensures that all checked-in files/dirs are included in the transfer.
# (You could alternately put ": .cvsinclude" into an .rsync-filter file and
# use the -F option, which is easier to type.)
#
# The downside is that you need to remember to re-run cvs2includes whenever
# you add a new file to the project.
use strict;
# CVS gets an added or removed file. Maybe just run it before every copy.
open(FIND, 'find . -name CVS -type d |') or die $!;
while (<FIND>) {
chomp;
s#^\./##;
import os, argparse
my $entries = "$_/Entries";
s/CVS$/.cvsinclude/;
my $filter = $_;
INC_NAME = '.cvsinclude'
open(ENTRIES, $entries) or die "Unable to open $entries: $!\n";
my @includes;
while (<ENTRIES>) {
push(@includes, $1) if m#/(.+?)/#;
}
close ENTRIES;
if (@includes) {
open(FILTER, ">$filter") or die "Unable to write $filter: $!\n";
print FILTER map "+ /$_\n", @includes;
close FILTER;
print "Updated $filter\n";
} elsif (-f $filter) {
unlink($filter);
print "Removed $filter\n";
}
}
close FIND;
def main():
if args.dir:
os.chdir(args.dir)
cvs_includes = set()
for root, dirs, files in os.walk('.'):
if INC_NAME in files:
cvs_includes.add((root + '/' + INC_NAME)[2:])
if root.endswith('/CVS') and 'Entries' in files:
entries = root[2:] + '/Entries'
includes = [ ]
with open(entries) as fh:
for line in fh:
if line.startswith(('/', 'D/')):
includes.append(line.split('/', 2)[1])
if includes:
inc = root[2:-3] + INC_NAME
cvs_includes.discard(inc)
try:
with open(inc) as fh:
old_txt = fh.read()
except OSError:
old_txt = ''
txt = ''.join(f"+ /{x}\n" for x in includes)
if txt == old_txt:
print("Unchanged", inc)
else:
print("Updating", inc)
with open(inc, 'w') as fh:
fh.write(txt)
dirs.sort()
for inc in sorted(cvs_includes):
print("Removing", inc)
os.unlink(inc)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=f"Transform CVS/Entries into {INC_NAME} files.", add_help=False)
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
parser.add_argument("dir", nargs='?', help="The top CVS dir. Defaults to the current directory.")
args = parser.parse_args()
main()
# vim: sw=4 et

View File

@@ -1,27 +1,37 @@
#!/usr/bin/env perl
# This script takes an input of filenames and outputs a set of
# include/exclude directives that can be used by rsync to copy
# just the indicated files using an --exclude-from=FILE option.
use strict;
#!/usr/bin/env python3
# This script takes an input of filenames and outputs a set of include/exclude
# directives that can be used by rsync to copy just the indicated files using
# an --exclude-from=FILE or -f'. FILE' option. To be able to delete files on
# the receiving side, either use --delete-excluded or change the exclude (-)
# rules to hide filter rules (H) that only affect the sending side.
my %hash;
import os, fileinput, argparse
while (<>) {
chomp;
s#^/+##;
my $path = '/';
while (m#([^/]+/)/*#g) {
$path .= $1;
print "+ $path\n" unless $hash{$path}++;
}
if (m#([^/]+)$#) {
print "+ $path$1\n";
} else {
delete $hash{$path};
}
}
def main():
paths = set()
for line in fileinput.input(args.files):
dirs = line.strip().lstrip('/').split('/')
if not dirs:
continue
for j in range(1, len(dirs)):
if dirs[j] == '':
continue
path = '/' + '/'.join(dirs[:j]) + '/'
if path not in paths:
print('+', path)
paths.add(path)
print('+', '/' + '/'.join(dirs))
foreach (sort keys %hash) {
print "- $_*\n";
}
print "- /*\n";
for path in sorted(paths):
print('-', path + '*')
print('-', '/*')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Transform a list of files into a set of include/exclude rules.", add_help=False)
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
parser.add_argument("files", metavar="FILE", default='-', nargs='*', help="The file(s) that hold the pathnames to translate. Defaults to stdin.")
args = parser.parse_args()
main()
# vim: sw=4 et

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import os, re, argparse, subprocess
from datetime import datetime
from datetime import datetime, UTC
NULL_COMMIT_RE = re.compile(r'\0\0commit [a-f0-9]{40}$|\0$')
@@ -74,7 +74,7 @@ def print_line(fn, mtime, commit_time):
if args.list > 1:
ts = str(commit_time).rjust(10)
else:
ts = datetime.utcfromtimestamp(commit_time).strftime("%Y-%m-%d %H:%M:%S")
ts = datetime.fromtimestamp(commit_time, UTC).strftime("%Y-%m-%d %H:%M:%S")
chg = '.' if mtime == commit_time else '*'
print(chg, ts, fn)

45
support/idmap Executable file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# This helper script makes it easy to use a passwd or group file to map values
# in a LOCAL transfer. For instance, if you mount a backup that does not have
# the same passwd setup as the local machine, you can do a copy to/from the
# backup area as follows and get the differing ID values mapped just like a
# remote transfer to/from the backed-up machine would do:
#
# rsync -av --usermap=`idmap --to /mnt/backup/etc/passwd` \
# --groupmap=`idmap --to /mnt/backup/etc/group` \
# /some/src/ /mnt/backup/some/dest/
#
# rsync -av --usermap=`idmap --from /mnt/backup/etc/passwd` \
# --groupmap=`idmap --from /mnt/backup/etc/group` \
# /mnt/backup/some/src/ /some/dest/
import re, fileinput, argparse
NAME_ID_RE = re.compile(r'^(\w+):[^:]+:(\d+)')
def main():
maps = [ ]
for line in fileinput.input(args.files):
m = NAME_ID_RE.match(line)
if not m:
continue
if args.to:
pair = (m[1], m[2])
else:
pair = (m[2], m[1])
maps.append(':'.join(pair))
print(','.join(maps))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Output usermap or groupmap args for rsync.", add_help=False)
action = parser.add_argument_group()
action = parser.add_mutually_exclusive_group(required=True)
action.add_argument("--from", action="store_true", help="Output the map for use on the sending side.")
action.add_argument("--to", action="store_true", help="Output the map for use on the receiving side.")
parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
parser.add_argument("files", metavar="FILE", default='-', nargs='*', help="The file(s) that hold the name & id pairs. Defaults to stdin.")
args = parser.parse_args()
main()
# vim: sw=4 et

11
support/install_deps_ubuntu.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# install script for build dependencies for ubuntu/debian systems
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

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