Compare commits

...
2358 Commits
Author SHA1 Message Date
Andrew Tridgell 2358081d3d fleettest: mac2-hfs runs the backup-dir ownership race too
operator-path-backup-chown probes for a cross-device directory, because
make_backup() renames into a same-filesystem backup dir and never reaches
the set_file_attrs() path the test is about.  On macOS there is normally
no such directory, so the test is a macOS-wide expected skip -- but this
target puts the scratch trees on a separate HFS+ volume, which supplies
exactly the condition it was looking for.

Same reason backup-crossdev-copy and chmod-temp-dir are already omitted
here.  The test PASSES on the target (10.9s); only the expected-skip
bookkeeping was wrong.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 1030929cba vfs: adapt the merged-in base changes to the VFS layer
The base gained 13 commits.  Three needed hand-porting because the change
lands on code the VFS split moved or renamed:

- 31130112 (--confine-root) is almost entirely in syscall.c, which does
  not exist here.  abspath_excluded_by_module() becomes
  abspath_outside_confinement() in vfs/dirstack.c, taking its root from
  vfs.module_dir when we are a daemon and from confine_root otherwise,
  plus the fd-pin helpers; ona_open() in vfs/owner_walk.c gains the
  getcwd() seed and the pin_transit exemption.  The VFS passes is_operator
  as an argument where the base reads operator_path_resolve, so the
  refusal takes it from there rather than the deleted global.

- d09edb85 (--link-dest hard-link fallback) keeps the VFS call
  vfs_link_at(cmpbuf, fname, !am_daemon ? VFS_OPERATOR_PATH : 0, 0) and
  adopts the base's cannot_hardlink/match_level fallback around it.

- t_stub.c gains the confine_root/confine_rootlen stubs beside the VFS
  curr_dir note.

Tree is byte-identical to the merge oracle (tag merge-reference-11).
2026-08-13 10:20:13 +10:00
Andrew Tridgell 893e88ade1 testsuite: cover the backup-dir ownership set under a parent swap
The previous commit gave set_file_attrs()' path-based chmod/chown the
operator ownership walk, but nothing exercised the chown side: the
instrumented branch was reached 92 times by the existing suite, all of
them chmod.  That is the same gap that let the upstream fix ship inert.

The uncovered path is a SYMLINK backup.  op_pin cannot pin a symlink --
there is no O_NOFOLLOW open of one -- so make_backup()'s set_file_attrs()
falls through to the full-path lchown.  Without the walk, a backup parent
flipped to an attacker-owned symlink redirects that lchown onto a victim
outside the backup tree and retags it as the attacker's.

Two things make this awkward to test, and both are why it was missed:

  - On one filesystem make_backup() hard-links or renames the item into
    the backup dir and never calls set_file_attrs() at all.  The fixture
    therefore puts the backup dir on tmpfs so link and rename fail EXDEV
    and the recreate path runs.  backup-crossdev-copy probes the same way.

  - A statically planted symlink proves nothing: rsync's own backup-dir
    validation deletes a non-directory component before using it.  The
    plant has to be a live flip, as in operator-path-backup-symlink.

The positive control checks both that the symlink reached the backup tree
and that the backup copy carries the attacker's uid -- i.e. that an lchown
actually ran.  Without that second assertion the race would pass
vacuously on any build where the chown never happens.

RED on the parent of the previous commit (victim outside/f94 retagged
away from root); GREEN here.  Runs on Linux CI as root; registered as an
expected skip on Cygwin (root-only) and macOS (root-only, and no
cross-device tmpfs -- backup-crossdev-copy is listed there for the same
reason).  Full expected-skip oracle passes exactly.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 730e619767 vfs: give set_file_attrs' path-based chmod/chown the operator policy
Base commit 0bfcd3b0 taught do_chmod_at()/do_lchown_at() to resolve an
operator-supplied path through the ownership walk.  The previous commit
ported that into vfs_chmod()/vfs_lchown(), but on this branch the policy
is a per-call argument rather than the ambient operator_path_resolve
global, and set_file_attrs() still passed 0 -- so the ported branch was
unreachable and the fix was inert.

op_pin already covers a reg/dir/fifo leaf with a pinned fd, which is
stronger than the walk.  What it does not cover reaches the path-based
fallbacks: a symlink or device leaf never enters op_pin, and a non-root
operator can fail the pin open with an ordinary EACCES and fall through
with op_refuse clear.  Both then resolved the full operator path with a
bare lchown()/chmod().

Derive the flag from ATTRS_OPERATOR_PATH and pass it to the two
VFS_AT_FDCWD fallbacks only; the held-dirfd arms stay at 0, since a
pinned parent already confines them.  vfs_chmod()'s operator branch
skips S_ISLNK on its own, so a symlink-as-object keeps the
lchmod()/setattrlist() path, matching the base.

Also drop ten .gitignore entries the rebase duplicated: this branch's
own test-helper block and the base's new one list the same binaries.

Suite 259/0/83; the operator-path, backup and temp-dir families pass as
root over --use-tcp.  Instrumenting the branch shows it is now reached
92 times across those tests (the chmod side); the lchown side has no
test exercising it, which is how the gap survived upstream.
2026-08-13 10:20:13 +10:00
Andrew Tridgell fca1d10ff5 vfs: adapt the merged-in base changes to the VFS layer
The base gained 99 commits since the last rebase.  Nine files needed a
hand-port because the change lands on code the VFS split moved or
renamed, and syscall.c no longer exists here:

- backup.c: make_path() now runs on a private copy of backup_dir_buf
  (c933f622), so vfs_make_path() takes dirbuf and drops the restore.
- clientserver.c: keep both the module-root snapshot and the new
  daemon_config_filter_file window.
- exclude.c: the peer-driven merge-file confinement is the
  vfs_open_owner_walk() is_operator argument, not a global.
- fileio.c: the coalesced --sparse writer's new helpers use
  vfs_lseek()/vfs_punch_hole().
- generator.c/vfs/mknod.c: gen_entry_mknod() falls back through
  vfs_mknod(); the atfd path keys its mknodat() off HAVE_MKNODAT.
- receiver.c: secure_recv_open() passes VFS_OPERATOR_PATH instead of
  toggling operator_path_resolve; open_readonly_inplace() uses the VFS
  stat/chmod/open wrappers.
- sender.c: absolute --relative cleanup anchors at "/" via
  vfs_resolve_open(), the copy-links walk uses
  vfs_resolve_open_at_beneath(), and the source removal goes through
  vfs_unlink().
- vfs/chmod.c, vfs/chown.c: VFS_OPERATOR_PATH now takes the ownership
  walk, and the no-follow chmod grows the non-Linux fd path.
- vfs/secure_open.c: the fd-anchored resolver splits into a shared
  internal with an allow-dotdot entry point, and secure_walk_at() routes
  a literal "."/".." through ds_descend() before the leaf fast paths.

Tree is byte-identical to the merge oracle (tag merge-reference-10).
2026-08-13 10:20:13 +10:00
Andrew Tridgell 97d7cfc5cb vfs: adapt the merged-in base changes to the VFS layer
Linear-rebase counterpart of the conflict resolutions made when the
sec-fixes base was re-integrated (see the merge for reference).  Three of
the base's new commits touch code this branch relocated or reworked:

- options.c (3fe1ed51 "rsync: confine the daemon files-from open to the
  module root"): the base wraps the files-from open in the
  operator_path_resolve global, which no longer exists here.  Pass the
  operator context explicitly instead: vfs_open_owner_walk(..., 1).

- receiver.c (cfd40f55 "receiver: confine peer-selected partial basis
  paths"): the new relative-partial-basis branch uses the VFS resolver
  name, vfs_resolve_open().

- vfs/chmod.c (7b16872e "syscall: silence scan-build dead-store in
  do_fchmodat_nofollow fallback"): syscall.c is deleted here, so the
  mode masking move and the unused-arg casts land in the relocated
  do_fchmodat_nofollow.

Tree is byte-identical to the validated merge result.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 359baac8eb vfs: fail loud in the held-fd lstat no-AT_SYMLINK_NOFOLLOW arm; make the compile-check atomic
Two fixes from codex review of the no-AT_FDCWD port:

- vfs/stat.c: the held-dirfd vfs_lstat branch fell back to
  fstatat(dirfd, path, st, 0) when AT_SYMLINK_NOFOLLOW is unavailable,
  which FOLLOWS the leaf and breaks lstat's no-follow contract.  On a
  system with SUPPORT_LINKS but no AT_SYMLINK_NOFOLLOW, return ENOSYS
  instead (mirroring the held-fd vfs_lchown arm) so a symlink-sensitive
  caller fails loud rather than silently following; the !SUPPORT_LINKS
  arm keeps fstatat(...,0) since there is nothing to follow.  The CI
  compile-check config also undefines AT_FDCWD so no held fd is produced
  there; this hardens the standalone "no AT_SYMLINK_NOFOLLOW" shape.

- Makefile.in: the vfs-no-at-fdcwd.o compile loop wrote every object to
  $@, so a mid-loop failure left a fresh-timestamped $@ and a retry could
  skip the check.  Compile to $@.tmp and mv to $@ only after the whole
  loop succeeds (rm the stale target up front); clean the .tmp too.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 39c6c31bfc vfs: adapt the merged-in base changes to the VFS layer
Linear-rebase counterpart of the conflict resolutions made when the
sec-fixes base was re-integrated (see the merge for reference).  The base
gained 5cb4b829 ("syscall: build without AT_SYMLINK_NOFOLLOW") and its CI
compile-check, which touch code this branch relocated into vfs/:

- Move syscall.c's RSYNC_TEST_NO_AT_FDCWD undef block into vfs/vfs.h,
  before the VFS_AT_FDCWD sentinel binds (so the sentinel takes its
  no-AT_FDCWD value rather than dangling on the undefined AT_FDCWD).
- Port the AT_SYMLINK_NOFOLLOW-absent fallbacks into the relocated code:
  vfs/chown.c (vfs__lchown_secure + held-fd vfs_lchown gate on
  AT_SYMLINK_NOFOLLOW), vfs/stat.c (do_xstat_at's unused-arg casts, the
  vfs_lstat AT_SYMLINK_NOFOLLOW-absent arm, held-fd gate), vfs/mkdir.c
  (guard rand_bytes on AT_FDCWD, its only caller).
- Retarget the CHECK_COMPILE_OBJS compile-check from syscall.c to a
  portable shell loop over the vfs sources built with
  -DRSYNC_TEST_NO_AT_FDCWD (Makefile.in).

Tree is byte-identical to the validated merge result.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 518c09fccf gitignore: ignore the remaining test-helper binaries
t_hashtable_overflow, t_iwildmatch, t_clean_fname, and t_safe_arg were
built by the suite but missing from .gitignore, so a stray git add -A
sweeps them into a commit (as happened during this rebase round).  List
them alongside the other t_* harnesses.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 01e0de397b testsuite: keep daemon test ports out of the 13000+ bloatware range
daemon-exclude-namebased bound its daemon on 13010, and the
setup_chroot_inner helper hashed into 12940-13139 -- both reach into
13000+, where ASUS Armoury Crate on the Cygwin CI host parks localhost
listeners (13010, 13030-13032), making the port probe fail the test.
The helper also used str hash(), which is per-process randomized
(PYTHONHASHSEED), so its port wandered run to run.

Move the fixed port to 12931 and the helper to a deterministic
crc32-based slot in the otherwise-unused 12800-12859 band.
2026-08-13 10:20:13 +10:00
Andrew Tridgell ea03fe0e9a vfs: fix the no-AT_FDCWD fallback in vfs__symlink_secure
The #else arm for platforms without AT_FDCWD called the four-argument
vfs_symlink() with two arguments -- a compile error on such systems.
Call vfs__symlink_plain(), matching the base's do_symlink() fallback.
Found by codex review; predates this rebase round.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 4c142f671b vfs: adapt the merged-in base changes to the VFS layer
Linear-rebase counterpart of the conflict resolutions made when the
sec-fixes base was integrated (see the merge for reference):

- Port ddda7ba5's operator-path confinement of do_symlink_at and
  do_rmdir_at into the relocated VFS code.  vfs__symlink_secure gains
  the VFS_OPERATOR_PATH ownership-walk branch (parent confined via
  vfs_owner_walk_parent, shared leaf-creation preserved so fake-super
  emulation still applies); vfs__unlink_secure extends its existing
  operator branch to the rmdir/AT_REMOVEDIR case.  Callers pass the
  policy explicitly where the base set operator_path_resolve: the
  keep_backup symlink create (backup.c), the backup-tree rmdir in
  delete_item (delete.c, DEL_FOR_BACKUP), and handle_partial_dir's
  rmdir (util1.c).
- Port 1f8f89c2's robust_rename EXDEV-fallback confinement into
  vfs/robust.c: an absolute --temp-dir/--partial-dir operand routes the
  copy_file dest-write and the source-unlink through the ownership walk
  (VFS_OPERATOR_PATH), so a raced parent symlink can't redirect either
  out of the module.
- Drop the stale "no ownership-walk branch" notes in vfs/vfs.h and
  vfs/symlink.c now that symlink and rmdir carry the branch.

Tree is byte-identical to the validated merge result.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 331188aaf6 acls: use vfs_relpath_active in the Solaris ACL branch
The HAVE_SOLARIS_ACLS facl(2) paths called secure_relpath_active() -- the
base-branch name -- which does not exist on the VFS branch (here the gate is
vfs_relpath_active()).  Linux/BSD never compile that branch, so it stayed latent;
a real Solaris build fails with an implicit-declaration error.  Rename both call
sites (set_rsync_acl default-ACL delete and the access/default set fallback).
2026-08-13 10:20:13 +10:00
Andrew Tridgell 1a8c81d9d7 github: run the ASan suite under --enable-strict-confinement
Enable the strict confinement assertion in the ASan/UBSan CI build so the suite
enforces "no confined-regime raw path metadata op" on every run.
2026-08-13 10:20:13 +10:00
Andrew Tridgell fc23ad74c9 rsync,generator: assert the xattr/ACL pin invariant under STRICT_CONFINEMENT
Guard the two receiver decision points that drive the xattr/ACL setters down a
raw path-based branch -- set_file_attrs() and gen_entry_copy_xattrs().  In a
strict build, a confined pinnable non-operator leaf that reached the setters
without a confined fd (held_fd/xfd < 0 and not refused) aborts: that is exactly
the copy-xattrs fallback class.  The guards mirror the pin/re-pin conditions, so
they cannot false-abort a legitimate transfer.

Verified: under --enable-strict-confinement the suite is 210/0 with zero aborts;
neutering the re-pin makes copy-xattrs-symlink-race abort at the guard.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 531f941c7d vfs: add STRICT_CONFINEMENT build-time confinement assertion
Add a CI/dev hardening mode (--enable-strict-confinement) that turns any
confined-regime raw path-based metadata op into a hard abort, so a reintroduced
copy-xattrs-class fallback fails the test suite instead of silently escaping
through a flipped parent symlink.

vfs_must_be_confined() is the predicate: the modern *at/O_NOFOLLOW primitives are
present, the path is non-operator, relative and multi-component (a parent the
attacker could flip), and vfs_relpath_active().  vfs_strict_confine_fail() logs
and aborts.  Both are absent/no-op without STRICT_CONFINEMENT, so there is no
production behaviour change.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 5210c5fd53 vfs: review fixes
cleanups from review comments by Sam James. Thanks!
2026-08-13 10:20:13 +10:00
Andrew Tridgell eab19ec7f9 rsync: never path-resolve a confined receiver's xattr/ACL write (copy-xattrs race)
set_file_attrs() pins the entry via the cached held dir fd and drives the xattr
/ACL ops off that fd (fsetxattr).  But when the pin missed -- vfs_cached_dirfd()
returns -1 (its dir isn't the held one, or the path is deeper than the dirfd
cache), or the leaf openat() loses a race -- held_fd stayed -1 and
set_stat_xattr()/set_xattr()/get_acl_fdat()/set_acl_fdat() fell through to the
path-based branch (sys_lsetxattr(fname,...)).  Unlike the chmod/chown/times path
wrappers (which secure-resolve), that raw lsetxattr re-resolves the parent, so a
concurrent flip of a dest parent component to a symlink->outside lands the xattr
OUTSIDE the destination tree (the intermittent copy-xattrs-symlink-race escape
that surfaces under -j load, which widens the open->setxattr window).

Re-pin through the secure resolver when the cached pin misses on a confined,
non-operator receiver path, so the xattr/ACL ops always use a confined fd -- NOT
a raw path lsetxattr; if the re-pin also fails (a genuinely raced parent/leaf
symlink) skip the path-based ops (xattr_refuse) rather than redirecting them.
The re-pin passes O_DIRECTORY for a directory leaf (the secure resolver refuses a
bare dir open with EISDIR), so dir xattrs/ACLs are still preserved on a cache
miss.  Apply the same re-pin/refuse to gen_entry_copy_xattrs() (the dir xattr
copy), whose dfd<0 path likewise fell to copy_xattrs() with dest_fd==-1.
chmod/chown/times are unchanged (already confined via their *at wrappers);
operator paths keep op_pin/op_refuse.

Confirmed: under heavy parallel load the dangerous lsetxattr fallback was reached
52+ times before and 0 times after; dir xattrs preserved; suite 210/0/66,
root metadata PoC tests pass, builds +/-xattr.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 955115c5a5 testsuite: per-operand policy regression for vfs_rename_at (PR #30)
Extend the t_rename_secure harness with PS-refuse/PS-follow: rename to the SAME
operator-owned escaping symlink path (oplink -> ../trap) with the SAME operator
old-side flag, differing ONLY in the new-side flag.  The ownership walk (operator)
follows the operator's own uid0/euid symlink; the secure receiver resolve
(transfer, flag 0) refuses it because it leaves the cwd anchor.  PS-refuse must be
rejected and PS-follow must succeed -- proving the new side's confinement is
independent of the old side's policy.

Deterministic, non-root, no race.  Verified RED on the old whole-call behaviour
(emulated by OR-ing both operands' flags, the new side then follows oplink and
escapes to ../trap) and GREEN with the per-operand split.  The driver adds the
oplink fixture and the per-side source files.

Suggested-by: Zen Dodd <mail@steadytao.com>
2026-08-13 10:20:13 +10:00
Andrew Tridgell 62122367a4 vfs: split two-path ops to per-operand policy flags (rename/link)
vfs_rename_at() and vfs_link_at() took a single vfs_flags applied to BOTH
operands, so VFS_OPERATOR_PATH (ownership walk: follow uid0/euid symlinks, refuse
foreign) was applied to a transfer operand even when only one side was operator-
supplied -- relaxing the secure receiver resolve that a transfer/file-list path
should get.  The default branch confined each side by absolute-vs-relative, but
that missed a *relative* operator path (--backup-dir=bdir, --partial-dir=.rsync,
relative --link-dest).  Reported in the rsync-private PR #30 review.

Take old_flags and new_flags separately and resolve each operand under its own
policy via a shared vfs_twopath_side() helper (operator walk if the side is
VFS_OPERATOR_PATH or absolute; secure receiver resolve if relative-with-slash;
AT_FDCWD for a bare name).  An operator basis/backup path on one side can no
longer relax the other side's confinement.

Callers now pass the correct per-side policy:
- backup link/rename: source (live dest file) = transfer (0), backup target = operator.
- receiver partial-dir rename: partialptr = operator, final dest = transfer.
- hard_link_one / generator link-dest: basis source = operator (non-daemon), dest = transfer.
- finish_transfer / gen_entry_rename / robust fallback: both transfer (0,0; default per-side).
- robust_unlink ETXTBSY sibling rename: both share the caller's policy.
Also drops the operator flag on backup's "just in case" robust_unlink of the
transfer-side source.

Behaviour-preserving for the operator side (the backup/partial-dir parents still
owner-walk); it only tightens the transfer side back to the secure resolve.
Builds +/-xattr; suite 210/0/66; root operator-path PoC tests all pass.

Suggested-by: Zen Dodd <mail@steadytao.com>
2026-08-13 10:20:13 +10:00
Andrew Tridgell 139c305814 delete: confine the backup-tree unlink via the operator ownership walk
A backup-tree delete (delete_item with DEL_FOR_BACKUP -- removing an existing
leaf under an absolute --backup-dir before the new backup is placed) fell through
del_unlink() to robust_unlink(fbuf, 0), which resolves the leaf's parent by path
with no ownership walk.  A local attacker who flips a backup-path parent to a
symlink in that window could redirect the unlink outside the backup tree.

The base confines this: make_backup() wraps make_backup_inner() in
operator_path_resolve, so the same unlink reaches do_unlink_at()'s owner-walk
branch.  The VFS refactor replaced the global with explicit vfs_flags but left
del_unlink() passing 0, dropping the confinement.  Thread VFS_OPERATOR_PATH into
del_unlink()'s path-based fallback when DEL_FOR_BACKUP is set (the exact cases
that ran under the base's operator wrap); a held-dirfd delete is already confined
and ignores it.  The rmdir path keeps no owner-walk branch, matching base.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 4099664250 vfs: round-3 operator-path reconciliation (snap to merge oracle)
The -X theirs rebase replayed the 47 VFS commits onto the new base, letting each
VFS commit win its refactor on conflict; that drops the round-3 base changes that
overlap the operator-path code (they were authored against the deleted syscall.c
and the pre-vfs_flags model).  This commit snaps those files byte-for-byte from
the validated merge oracle (merge-reference-3 = 6afdd389), so the branch tip is
tree-identical to the merge that built clean +/-xattr and passed the suite +
root operator-path PoC tests.

Reconciled: the syscall.c->vfs/ ports (secure_open/owner_walk/dirstack/rename/
link/chmod/chown/times/copy_file + vfs.h/vfs_internal.h), the set_file_attrs
op_pin via ATTRS_OPERATOR_PATH (rsync.c/rsync.h + backup.c callers), the
secure_basis_open/basis_link_stat operator branches (receiver.c/generator.c), the
copy_file/gen_entry_copy_xattrs held-fd source reads, the change_dir/sender
opt-out sites (util1.c/sender.c/clientserver.c), and Makefile.in.
2026-08-13 10:20:13 +10:00
Andrew Tridgell fbfa40fe4d vfs: port 37dbb263's operator-path mknod FIFO/socket fallback
Linear-rebase counterpart of the round-2 merge resolution: 37dbb263 fixed
do_mknod_at()'s operator branch in syscall.c (deleted on this branch) to
fall back to mkfifoat() for a FIFO and EOPNOTSUPP for a nested socket when
mknodat() can't make a special file on the BSDs/macOS/Solaris.  Re-express
it in the VFS_OPERATOR_PATH branch of vfs__mknod_secure() in vfs/mknod.c,
mirroring the secure-relpath branch already in that function.

Tree is byte-identical to the validated round-2 merge result.
2026-08-13 10:20:13 +10:00
Andrew Tridgell e0f2f52670 vfs: adapt the merged-in base changes to the VFS layer
Linear-rebase counterpart of the two conflict resolutions made when the
sec-fixes base was integrated (see the merge for reference):

- Port 8a7e9a16's fixed-array dirstack into the relocated VFS code:
  vfs/vfs_internal.h (DS_MAXDEPTH + inline int fds[]) and vfs/dirstack.c
  (drop malloc/realloc/free; ENOMEM past the cap).  8a7e9a16 changed the
  dirstack in syscall.c, which this branch deleted, so the fix is
  re-expressed in vfs/.
- Map the base's new F_XATTR ndx<0 guard (1d36a565) from the old
  do_chmod_at() to the unified vfs_chmod(VFS_AT_FDCWD, fname, ..., 0).

Tree is byte-identical to the validated merge result.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 18e30dd23e vfs: codex review fixes for the unified stat/chmod/lchown
Three issues from the post-unification review:

- The !SUPPORT_XATTRS x_stat/x_lstat macros hardcoded VFS_ALLOW_SYMLINK,
  silently downgrading an operator-path stat (backup-dir) from the ownership
  walk + module confinement to plain follow-stat in no-xattr builds (a
  pre-existing gap the unified vfs_stat now lets us close).  Forward the
  caller's vfs_flags instead, so no-xattr builds get the same policy as
  xattr builds.

- Held-fd ".." was accepted by the stat/chmod/lchown dispatchers, which
  would operate on the PARENT of the pinned dirfd -- the wrong default for a
  security boundary even though no current call site passes it.  Reject ".."
  while still allowing "." (a stat/chmod/chown of the dir itself, which
  link_stat_at and set_file_attrs legitimately do for directory entries).

- Two stale comment/test strings still said vfs_chmod_at (now vfs_chmod).

Verified with a local --disable-xattr build; full suite 190/50.
2026-08-13 10:20:13 +10:00
Andrew Tridgell af68dac222 vfs: update the !SUPPORT_XATTRS x_stat/x_lstat macros for the unified stat
vfs_stat/vfs_lstat are now vfs_<op>(int dirfd, path, st, int flags); the
no-xattr fallback macros still expanded to the old 2-arg vfs_stat(fn,fst),
breaking the build without xattr support.  Map them to the unified plain
form: vfs_stat(VFS_AT_FDCWD, fn, fst, VFS_ALLOW_SYMLINK) (and lstat), which
preserves the prior plain-stat behaviour.  Caught by the fleettest on
openbsd; verified with a local --disable-xattr build.
2026-08-13 10:20:13 +10:00
Andrew Tridgell b61342e4c9 vfs: docs pass for the unified dirfd+flags API
Rewrite the vfs.h header contract to describe the current model: the two-layer
vfs/ structure (primitives + compounds), the single vfs_<op>(int dirfd, ...,
int flags) form, the meaning of VFS_AT_FDCWD / VFS_ALLOW_SYMLINK /
VFS_OPERATOR_PATH / VFS_REMOVEDIR, that the operator policy is an explicit
per-call flag (not ambient state), and which ops keep explicit forms
(rename/link two-path, open variants, fd-based fstat/fileio).

Update the stale threat-model cross-references that pointed at the old
vfs_chmod_at() (now the static vfs__chmod_secure()) and the two
vfs_mknod_at()/vfs_symlink_at() mentions left by the unification.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 1214c4a2f9 vfs: unify stat/lstat, chmod, lchown into dirfd+flags form
Collapse the remaining three-form ops into one call each, matching
mkdir/mknod/symlink/unlink:
  vfs_stat(int dirfd, path, st, flags)
  vfs_lstat(int dirfd, path, st, flags)
  vfs_chmod(int dirfd, path, mode, flags)
  vfs_lchown(int dirfd, path, owner, group, flags)
dirfd == VFS_AT_FDCWD resolves the path (VFS_ALLOW_SYMLINK = plain libc op,
default 0 = secure receiver resolve, VFS_OPERATOR_PATH = ownership walk for
stat); a real held dirfd operates on a single component under it.  The old
plain/_at/_atfd bodies become static helpers behind the dispatchers;
vfs_fstat stays (fd-based).  chmod/lchown keep their no-owner-walk behaviour
(VFS_OPERATOR_PATH resolves the same as the default secure walk).

Pure API-narrowing -- the operator policy was already explicit; no global is
involved.  This is behaviour-preserving (plain sites -> VFS_ALLOW_SYMLINK,
_at -> the flag they already carried, _atfd -> held-fd 0).

Held-fd validation differs by op: mkdir/mknod/symlink/unlink reject "."/".."
(creating/removing them is nonsensical), but stat/chmod/lchown ALLOW "."
(a read or metadata op on the dir itself is legitimate -- link_stat_at and
set_file_attrs do it for directory entries); only empty and multi-component
("/") names are rejected there.  Caught by the suite (chmod/metadata/
ownership-depth + the implied-"." dir mkdir path).

Full suite 190/50.
2026-08-13 10:20:13 +10:00
Andrew Tridgell f436561d08 vfs: fix the !SUPPORT_XATTRS x_stat/x_lstat macros for the vfs_flags arg
The stat/lstat threading added a vfs_flags argument to x_stat()/x_lstat(),
but on a build without xattr support those are 3-arg macros in rsync.h
(mapping to plain vfs_stat/vfs_lstat), so the 4-arg call sites failed to
compile ("too many arguments to macro").  Give the macros the extra
(ignored) parameter.  Caught by the fleettest on openbsd; verified with a
local --disable-xattr build.
2026-08-13 10:20:13 +10:00
Andrew Tridgell b226005967 vfs: delete the operator_path_resolve global
With every operator-context call site now passing VFS_OPERATOR_PATH
explicitly, nothing reads vfs.operator_path_resolve any more.  Remove the
four now-dead set/clear blocks (make_backup, handle_partial_dir, and the two
generator in-place-backup paths) and delete the field from struct vfs.

The operator-supplied path resolution policy -- which selects the ownership
walk and the daemon module-root confinement for --backup-dir/--temp-dir/
--partial-dir/--link-dest operations -- is no longer ambient dynamic-scope
state poked into the VFS from mainline.  It travels as an explicit per-call
VFS_OPERATOR_PATH flag through the primitives and compounds, resolving the
layering violation that motivated this work.

Full suite 190/50.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 871d1974f1 vfs: thread the operator flag through stat/lstat + x_stat/x_lstat
vfs_stat_at / vfs_lstat_at (and their shared do_xstat_at helper) gain a
vfs_flags argument so the ownership-walk branch reads VFS_OPERATOR_PATH
instead of vfs.operator_path_resolve -- this was the last primitive still
reading the global.  The metadata wrappers x_stat()/x_lstat() (which stay in
xattrs.c, above the VFS) gain a vfs_flags argument forwarded to the
primitives.

Call sites classified: every backup-dir stat (validate_backup_dir,
copy_valid_path's x_stat, make_backup_inner's x_lstat + bak lstat) and the
partial-dir lstat (handle_partial_dir) pass VFS_OPERATOR_PATH; the transfer
enumeration (flist link_stat + friends), the in-place dir stat (generator),
and the set_stat_xattr lstat pass 0.  generator's non-daemon --link-dest
basis lookup passes 0 too (is_operator only gates the daemon module
confinement, a no-op there).

With this, NOTHING reads vfs.operator_path_resolve any more -- it is now
write-only (set by four now-dead blocks, removed next).  Full suite 190/50;
backup/partial/link-dest/xattr/daemon subset green.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 4a0f20f657 vfs: flag vfs_link_at + thread hard_link_one; retire the hard-link block
Give vfs_link_at an explicit vfs_flags argument (kept two-parent form) so its
ownership-walk branch reads VFS_OPERATOR_PATH instead of the global.

hard_link_one() gains a vfs_flags argument forwarded to vfs_link_at, so the
generator's alt-dest hard-link no longer sets vfs.operator_path_resolve
around the call: it passes (!am_daemon ? VFS_OPERATOR_PATH : 0), matching the
prior op = !am_daemon gating (a non-daemon --link-dest uses the ownership
walk; a daemon keeps the stronger module-anchored vfs_relpath_active path).
That operator_path_resolve block is removed.

Other call sites: the backup link (make_backup link_or_rename) passes
VFS_OPERATOR_PATH; the try_dests alt-dest link and the hlink finish-up
hard_link_one pass 0 (transfer context, as the global was there).

Set-sites: 5 -> 4 (backup make_backup, generator 2085/2130, util1
handle_partial_dir -- all wrapping the not-yet-flagged metadata layer
set_file_attrs/x_stat/x_lstat).  Full suite 190/50; hardlink/link-dest/
backup/daemon subset green.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 5c97ad3403 vfs: flag vfs_open_owner_walk + secure_basis_open; retire two more blocks
Give vfs_open_owner_walk an explicit is_operator argument so its module-root
confinement (abspath_excluded_by_module) reads the policy from the caller
instead of the vfs.operator_path_resolve global -- the last owner-walk
function still reading it.  secure_basis_open gains an is_operator parameter
threaded to it; its gate (owner-walk vs strict resolve) now branches on that
param too.

Faithful conversion (no behaviour change): every direct caller passes the
value the global held at that site -- config/log/motd/early-input/files-from/
batch/connection/exclude/authenticate/params, change_dir's daemon dest-chdir,
and vfs_secure_mkstemp's --temp-dir all pass 0; secure_basis_open passes
VFS_OPERATOR_PATH only for the operator cases (a --partial-dir basis,
fnamecmp_type == FNAMECMP_PARTIAL_DIR; and one_inplace partial-dir staging).
This preserves the existing --temp-dir behaviour (unconfined) by deliberate
choice; hardening that is a separate decision.

The receiver partial-dir-basis and one_inplace operator_path_resolve blocks
are removed (receiver.c is now free of the global).  Set-sites: 7 -> 5
(backup make_backup, generator 1071/2085/2130, util1 handle_partial_dir).

The operator-path-partial-dir-daemon test caught a real regression mid-change
(secure_basis_open's gate still read the global after its set/clear block was
removed, dropping the confinement); fixed by gating on is_operator.

Full suite 190/50.
2026-08-13 10:20:13 +10:00
Andrew Tridgell c89283a500 vfs: fix stale comment in unlink_and_reopen (robust_unlink now flagged)
The comment predated flagging robust_unlink; vfs_flags now reaches both the
robust_unlink and the create.  (codex review nit.)
2026-08-13 10:20:13 +10:00
Andrew Tridgell d037014ff4 vfs: flag vfs_rename_at + retire three operator-path blocks
Give vfs_rename_at an explicit vfs_flags argument (it keeps its two-parent
form, per the kept-explicit scope for rename) so its ownership-walk branch
reads VFS_OPERATOR_PATH instead of the vfs.operator_path_resolve global.

Call sites: the backup link/rename (make_backup) and the in-place/partial
rename (receiver) pass VFS_OPERATOR_PATH; the transfer renames (generator
entry rename, finish_transfer) and robust_rename pass 0; robust_unlink's
ETXTBSY rename-retry now forwards robust_unlink's own vfs_flags -- so
robust_unlink is fully explicit (both its unlink and its rename retry).

With unlink and rename both flagged, three operator_path_resolve set/clear
blocks that wrapped only a single now-flagged op are removed: the receiver
partial-dir rename and the two partial-dir unlinks (generator + receiver).
The remaining seven blocks still wrap not-yet-flagged ops (link, stat/lstat,
chmod/lchown via set_file_attrs, secure_basis_open) and stay for now.

Full suite 190/50; rename/partial/backup/inplace/daemon subset green.
2026-08-13 10:20:13 +10:00
Andrew Tridgell e12eb04e12 vfs: unify the unlink/rmdir family + flag robust_unlink
Collapse vfs_unlink / vfs_unlink_at / vfs_unlink_atfd / vfs_rmdir /
vfs_rmdir_at into one vfs_unlink(int dirfd, const char *path, int flags).
VFS_REMOVEDIR selects rmdir/AT_REMOVEDIR; dirfd == VFS_AT_FDCWD resolves the
path, a real held dirfd removes a single validated component.  The secure
body reads the operator context from VFS_OPERATOR_PATH instead of the
global; the rmdir path keeps its pre-existing no-owner-walk behaviour (it
never read the global -- matched the old vfs_rmdir_at).

robust_unlink() gains a vfs_flags argument threaded to vfs_unlink, so the
operator policy reaches it explicitly: the backup-tree callers (make_backup
link_or_rename, generator in-place backup) and copy_file's dest unlink (when
copying a backup) pass VFS_OPERATOR_PATH; delete and robust_rename's
transfer-context calls pass 0.  Its ETXTBSY rename-retry still uses
vfs_rename_at (the rename primitive isn't flagged yet), which reads the
global -- still set by the surrounding backup blocks -- so behaviour is
unchanged.

Call sites classified: --remove-source-files fallback (sender) follows
symlinks (VFS_ALLOW_SYMLINK); partial-dir unlink/rmdir (handle_partial_dir)
and in-place-backup/partial unlinks operator; the rest secure or held-fd.
Full suite 190/50; delete/backup/partial/remove-source/daemon subset green.
2026-08-13 10:20:13 +10:00
Andrew Tridgell ae819474d7 vfs: flag vfs_open_at + thread vfs_flags through copy_file's dest open
Wiring phase: give vfs_open_at an explicit vfs_flags argument so the
operator-path resolution policy reaches it from the caller instead of the
vfs.operator_path_resolve global, and thread that argument through
copy_file()/unlink_and_reopen() to the dest create.

Call sites classified: the in-place backup open (generator) and the backup
copy (backup.c, generator in-place) pass VFS_OPERATOR_PATH; the copy-dest
basis copy (copy_altdest_file) passes 0 -- it must NOT use the ownership
walk (re-opening the copy_xattrs parent-symlink race, see generator.c
comment), which the explicit argument now expresses directly; robust_rename
runs in transfer context and passes 0.

copy_file's dest UNLINK (robust_unlink) still reads the operator global,
still set by the surrounding backup blocks, so behaviour is unchanged at
every site (flag and global agree).  Full suite 190/50; backup/copy-dest/
link-dest/partial/daemon subset green.
2026-08-13 10:20:13 +10:00
Andrew Tridgell cbe4ae7744 gitignore: ignore the vfs test binaries
These compiled test harnesses (t_*_secure, t_acl, simdtest) were
accidentally committed by a git add -A; ignore them like the other
built test programs (t_unsafe, wildtest, ...) so they stay out of the tree.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 1695148937 vfs: move copy_file + robust_unlink/rename into the VFS compound layer
Relocate the file-copy / robust-unlink / robust-rename cluster from util1.c
into vfs/copy_file.c and vfs/robust.c.  These are filesystem mechanics built
on the vfs_* open/read/write/unlink/rename primitives, so they belong in the
VFS compound layer; this is a verbatim move (no behaviour change).

The cluster is mutually coupled -- robust_rename falls back to copy_file for
the cross-filesystem (EXDEV) case, and copy_file's unlink_and_reopen uses
robust_unlink -- so all four move together.  They call out to the metadata/
protocol layer for what isn't pure filesystem (copy_xattrs for the held-fd
xattr copy, handle_partial_dir for the EXDEV partial-dir path); those stay in
their current modules, keeping the dependency one-way (vfs -> nothing
above it pulled in).  safe_read (a static helper only copy_file used) moves
with it.

Operator context still flows via the vfs.operator_path_resolve field for now
(legitimately VFS-internal once these callers live in vfs/); a later commit
threads it as an explicit vfs_flags argument and removes the field.  The
t_*_secure harnesses don't reference the cluster, so no xattr/protocol
symbols are pulled into them.  Full suite 190/50; copy-dest/backup/partial/
link-dest subset green.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 056a9fe6bc vfs: move make_path into the VFS compound layer as vfs_make_path
First step of the VFS three-layer architecture: filesystem-mechanic helpers
that compose the vfs_* primitives belong inside vfs/, so the operator-path
resolution policy travels as an explicit argument instead of leaking across
the vfs<->mainline boundary as ambient state.

make_path() (recursive mkdir over vfs_mkdir/vfs_stat) moves verbatim from
util1.c to vfs/make_path.c and becomes vfs_make_path(fname, mkp_flags,
vfs_flags): mkp_flags keeps the path-handling bits (MKP_DROP_NAME/
MKP_SKIP_SLASH), vfs_flags carries the resolution policy (VFS_OPERATOR_PATH
for the operator-supplied --backup-dir tree, else 0).  The transitional
MKP_OPERATOR flag added in the mkdir step is dropped in favour of the
explicit vfs_flags argument.

Callers: get_backup_name (backup dir) passes VFS_OPERATOR_PATH; the transfer
callers (main/receiver/generator) pass 0.  Full suite 190/50.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 6e305172e5 vfs: unify the symlink family into vfs_symlink(lnk, dirfd, path, flags)
Collapse vfs_symlink / vfs_symlink_at / vfs_symlink_atfd into one call.
dirfd == VFS_AT_FDCWD resolves `path` (secure receiver resolve by default,
plain follow under VFS_ALLOW_SYMLINK); a real held dirfd makes `path` a
single validated component.  The old bodies become static helpers behind
the public dispatcher.

Unlike mkdir/mknod, the symlink secure path has NO ownership-walk branch --
it never read vfs.operator_path_resolve.  That pre-existing asymmetry is
preserved: VFS_OPERATOR_PATH is accepted but resolves the same as the
default secure walk (only the parent dir is confined; the link target is
stored verbatim and never resolved at creation).  The backup-symlink site
passes flags=0, which exactly reproduces the old vfs_symlink_at behavior.

Full suite 190/50.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 31c3b8f3d0 vfs: unify the mknod family into vfs_mknod(dirfd, path, mode, dev, flags)
Collapse vfs_mknod / vfs_mknod_at / vfs_mknod_atfd into one call, mirroring
vfs_mkdir.  dirfd == VFS_AT_FDCWD resolves the path (secure receiver resolve
by default, ownership walk under VFS_OPERATOR_PATH, plain follow under
VFS_ALLOW_SYMLINK); a real held dirfd makes path a single validated
component.  The old plain/secure/atfd bodies become static helpers
(vfs__mknod_plain/_secure/_atfd) behind the public dispatcher; the secure
body reads the operator context from VFS_OPERATOR_PATH instead of the
vfs.operator_path_resolve global.

Call sites: backup file node (make_backup_inner, operator) ->
VFS_OPERATOR_PATH; transfer device/fifo (generator) -> secure / held-fd.
The t_symlink_secure harness's plain PoC call becomes VFS_ALLOW_SYMLINK
(the vulnerable follow it demonstrates) and its secure calls flags=0.

Full suite 190/50.
2026-08-13 10:20:13 +10:00
Andrew Tridgell e34177b124 vfs: unify the mkdir family into vfs_mkdir(dirfd, path, mode, flags)
Collapse vfs_mkdir / vfs_mkdir_at / vfs_mkdir_atfd into one call.  dirfd ==
VFS_AT_FDCWD resolves the path argument; a real held dirfd makes path a
single component created directly under it (the held-fd form validates
that path is a lone harmless component -- rejects empty, any '/', "." and
".." -- so it cannot reintroduce path resolution under the pinned dir).

The plain-vs-secure choice is now an explicit per-call flag instead of a
function-name choice plus the vfs.operator_path_resolve global:
  VFS_ALLOW_SYMLINK  the call site asserts it is safe to follow symlinks
                     (the old plain vfs_mkdir); checked first, wins over
                     VFS_OPERATOR_PATH (no caller passes both)
  VFS_OPERATOR_PATH  operator-supplied path: ownership walk + module
                     confinement (the old global)
  default 0          secure receiver resolve (the old vfs_mkdir_at)

VFS_AT_FDCWD is a VFS-owned sentinel (maps to AT_FDCWD where available,
else a value that routes the held-fd form to ENOSYS) so mainline code
never has to mention AT_FDCWD directly -- avoids a compile break on
platforms lacking it.

mkdir stops reading vfs.operator_path_resolve.  vfs_owner_walk_parent now
takes the operator context as an is_operator parameter; its non-mkdir
callers (the other _at wrappers, generator hard-link) still pass the
global verbatim and convert in later steps.  make_path() gains MKP_OPERATOR
(translated to VFS_OPERATOR_PATH) so the backup-dir creation stays an
operator path by path-semantics, not by relying on the global being set.

Call sites classified: dest dir (main.c) follows symlinks (user's local
dest); transfer dirs (generator, make_path transfer callers) secure;
backup-dir (backup.c, get_backup_name->make_path) and partial-dir
(handle_partial_dir) operator.  The operator_path_resolve=1 blocks stay
for now since they still wrap non-mkdir ops; the global value and the
explicit flag agree at every converted mkdir site.

Full suite 190/50; backup/partial/daemon/exclude/filter/symlink-race
subset green.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 230d25b32b vfs: thread the operator context into abspath_excluded_by_module
First step of the API redesign.  Define the per-call VFS_* flags
(VFS_ALLOW_SYMLINK / VFS_OPERATOR_PATH / VFS_REMOVEDIR) and stop
abspath_excluded_by_module() from reading the vfs.operator_path_resolve
global directly -- it now takes an explicit is_operator argument.

The strict resolver stays confined beneath its anchor (always within the
module), so its two call sites pass is_operator=0 (the check never fires
there anyway).  The ownership walk is reached in BOTH operator context
(--backup-dir/--temp-dir/--partial-dir) and non-operator context (daemon
log-file/motd/config opens, which may legitimately live outside the
module), so vfs_open_owner_walk()/vfs_owner_walk_parent() capture the
global and pass it down through ona_open().  No behavior change -- the
value still comes from the global; later commits replace that source
with the explicit flag and delete the global.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 068c086287 vfs: snapshot the daemon module root into struct vfs
Fold the served module root (module_dir / module_dirlen / module_dirfd)
into the vfs.module_* snapshot so the confinement checks read the VFS's
own state rather than implicit clientserver.c externs.

clientserver.c calls vfs_set_module_root() in two stages, matching when
the values become final: once right after the module path is settled
(before the daemon opens any operator path -- filter/include files, the
log file), with the root dirfd still -1, and again once that dirfd is
pinned by identity.  The dirfd is borrowed (open_anchor_dirfd dup()s it);
the VFS never closes it.  vfs_init() now clears the whole snapshot so a
forked connection can never inherit a stale module root.

The vfs/ readers (dirstack, secure_open, owner_walk) switch from the
externs to vfs.module_*; clientserver/flist/main keep their own globals.
Behavior is identical: between the two calls vfs.module_dirfd is -1, so
open_anchor_dirfd re-resolves the path exactly as before the pin.
2026-08-13 10:20:13 +10:00
Andrew Tridgell b4417876b4 vfs: document the layer contract in vfs/vfs.h
Refresh the header comment now that the wrappers are vfs_* and syscall.c
is gone, and document the three operation forms callers choose between:
the plain path wrapper, the parent-resolved _at form (race-safe receiver
path), and the _atfd form (single component under a pinned dirfd).
Comment-only; no code change.  Validated at protocol 30 and 29.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 94fa51be8c vfs: remove the now-empty syscall.c
Every filesystem wrapper has moved into vfs/, so syscall.c held nothing
but its includes and an orphaned comment.  Delete it and drop syscall.o
from the rsync link and from each test-harness object list (they reach
the vfs_* symbols through libvfs.a now).  The S_BLKSIZE fallback define,
used by vfs/fileio.c, moves there.  No behavior change.
2026-08-13 10:20:13 +10:00
Andrew Tridgell 7c15f78bc3 vfs: restore platform includes in vfs/chmod.c
The chmod family carries macOS setattrlist() and the Linux SYS_fchmodat2
raw-syscall fast path, but the move into vfs/chmod.c dropped the two
platform headers that syscall.c had included at file scope: <sys/attr.h>
(macOS) and <sys/syscall.h> (Linux).  A Linux build hid this -- with
SYS_fchmodat2 undefined the code silently fell back to fchmodat() -- but
macOS failed to compile setattrlist().  Re-add both, matching the
original syscall.c includes.

Found by fleettest (mac2 BUILD-FAIL).
2026-08-13 10:20:12 +10:00
Andrew Tridgell 2341b7b9a0 vfs: move the file-data ops into vfs/fileio.c
Relocate do_ftruncate/do_lseek/do_fallocate/do_punch_hole out of
syscall.c into vfs/fileio.c as the vfs_* names, declared in vfs/vfs.h.
The SUPPORT_PREALLOCATION / HAVE_FALLOCATE / HAVE_SYS_FALLOCATE /
FALLOC_FL_PUNCH_HOLE guards travel verbatim.  This was the last
operation family: syscall.c no longer defines any filesystem wrapper.
No behavior change.  (Portability-sensitive; wants a fleettest.)
2026-08-13 10:20:12 +10:00
Andrew Tridgell 7f2ebc55f4 vfs: move the times family into vfs/times.c
Relocate the timestamp wrappers (do_utimensat/_at/_atfd, do_lutimes,
do_utimes, do_utime) and the crtime paths (do_setattrlist_times/_crtime,
get_create_time, do_SetFileTime) out of syscall.c into vfs/times.c as the
vfs_* names.  The struct create_time / #pragma pack / Cygwin windows.h
and sys/attr.h includes travel with them, as do the SUPPORT_CRTIMES /
HAVE_SETATTRLIST / HAVE_GETATTRLIST / HAVE_UTIMENSAT / HAVE_LUTIMES /
HAVE_UTIMES / HAVE_UTIME guards.  No behavior change.  (Portability-
sensitive; wants a fleettest.)
2026-08-13 10:20:12 +10:00
Andrew Tridgell 951489db55 vfs: move the mknod family into vfs/mknod.c
Relocate do_mknod/do_mknod_at/do_mknod_atfd out of syscall.c into
vfs/mknod.c as the vfs_* names, declared in vfs/vfs.h.  The
HAVE_MKNOD/HAVE_MKNODAT/HAVE_MKFIFO guards and the AF_UNIX socket-bind
fallback (with its <sys/un.h> include) travel verbatim.  No behavior
change.  (Portability-sensitive family; wants a fleettest.)
2026-08-13 10:20:12 +10:00
Andrew Tridgell 546e04b005 vfs: move the lchown family into vfs/chown.c
Relocate do_lchown/do_lchown_at/do_lchown_atfd out of syscall.c into
vfs/chown.c as the vfs_* names, declared in vfs/vfs.h.  The HAVE_LCHOWN
fallback guard travels verbatim.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 26236b156e vfs: move the mkdir/mkstemp family into vfs/mkdir.c
Relocate do_mkdir/do_mkdir_at/do_mkdir_atfd and do_mkstemp/
do_mkstemp_atfd/secure_mkstemp out of syscall.c into vfs/mkdir.c as the
vfs_* names (secure_mkstemp -> vfs_secure_mkstemp).  The
trim_trailing_slashes path helper (used by trimslash and the mkdir
wrappers) moves with them, keeping its name and now declared in vfs.h.
The static rand_bytes helper, used only by the mkstemp create loop,
moves along too.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell c6e86483b6 vfs: move the link family into vfs/link.c
Relocate do_link/do_link_at/do_link_atfd out of syscall.c into
vfs/link.c as the vfs_* names, declared in vfs/vfs.h.  The
HAVE_LINK/HAVE_LINKAT guards travel verbatim.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell e76b927378 vfs: move the symlink/readlink family into vfs/symlink.c
Relocate do_symlink/do_symlink_at/do_symlink_atfd and do_readlink/
do_readlink_atfd out of syscall.c into vfs/symlink.c as the vfs_* names.
The fake-super (NO_SYMLINK_*XATTRS) placeholder handling travels
verbatim.  vfs_readlink stays a function only in fake-super builds and a
macro -> readlink() otherwise (the rsync.h macro is renamed to match);
its vfs.h declaration is guarded accordingly.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 07dd81b17b vfs: move the chmod family into vfs/chmod.c
Relocate do_chmod/do_chmod_at/do_chmod_atfd (and the leaf-safe
do_fchmodat_nofollow helper) out of syscall.c into vfs/chmod.c as the
vfs_* names, declared in vfs/vfs.h.  The HAVE_CHMOD / HAVE_LCHMOD /
HAVE_SETATTRLIST / SYS_fchmodat2 platform guards travel verbatim.
Function bodies unchanged; no behavior change.

(Portability-sensitive family; wants a fleettest with the rest of the
#ifdef-heavy moves.)
2026-08-13 10:20:12 +10:00
Andrew Tridgell e84b935d3b vfs: move the open family into vfs/open.c
Relocate do_open/do_open_at/do_open_atfd/do_open_nofollow/
do_open_checklinks out of syscall.c into vfs/open.c as the vfs_* names,
declared in vfs/vfs.h.  Function bodies unchanged; no behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 7f03474b2b vfs: move the unlink/rmdir family into vfs/unlink.c
Relocate do_unlink/do_unlink_at/do_unlink_atfd and do_rmdir/do_rmdir_at
out of syscall.c into vfs/unlink.c as the vfs_* names, declared in
vfs/vfs.h.  Function bodies unchanged; no behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 02ea67ab6c vfs: move the rename family into vfs/rename.c
Relocate do_rename / do_rename_at / do_rename_atfd out of syscall.c into
vfs/rename.c as vfs_rename / vfs_rename_at / vfs_rename_atfd, declared in
vfs/vfs.h.  Function bodies unchanged.

Also centralize the option-global externs (dry_run, am_root, am_sender,
inplace, preserve_*, open_noatime, copy_*, insecure_links, module_id, …)
in vfs/vfs_internal.h, replacing syscall.c's local extern block, so each
relocated family picks them up from one place.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell ad5c09ad9e vfs: move the stat family into vfs/stat.c
Relocate the stat/lstat/fstat wrappers out of syscall.c into vfs/stat.c
with the vfs_* names:

  do_stat       -> vfs_stat        do_stat_at   -> vfs_stat_at
  do_lstat      -> vfs_lstat       do_lstat_at  -> vfs_lstat_at
  do_fstat      -> vfs_fstat       do_stat_atfd -> vfs_stat_atfd
                                   do_lstat_atfd-> vfs_lstat_atfd

do_xstat_at stays a file-local helper.  The x_stat/x_lstat/x_fstat
fallback macros in rsync.h now expand to the vfs_* names.  First Phase-3
family move, so the shared RETURN_ERROR_IF* dry-run/read-only guard
macros (and the read_only/list_only externs they expand to) move from
syscall.c into vfs/vfs_internal.h where every vfs/ source can use them.
Function bodies unchanged; no behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 48250edd8d vfs: fold operator_path_resolve into struct vfs
Move the operator-path resolver-mode flag into vfs.operator_path_resolve.
Its definition leaves vfs/owner_walk.c, the extern declarations (in
backup/generator/receiver/util1 and vfs_internal.h) are dropped, and the
uses across the do_*_at wrappers and the vfs/ internals are updated to
the struct field directly.  This completes moving the scattered VFS state
(dirfd cache, curr_dir, operator_path_resolve) into struct vfs.
No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 540245fa61 vfs: fold curr_dir/curr_dir_len into struct vfs
Move the logical-cwd globals into vfs.curr_dir / vfs.curr_dir_len.  The
definition leaves syscall.c, the per-file `extern char curr_dir[]` /
`extern unsigned int curr_dir_len` declarations are dropped (the struct
is reached via the vfs.h `extern struct vfs vfs`), and the uses across
exclude/flist/log/main/util1 and vfs/secure_open are updated directly --
no compatibility alias macros.  change_dir() writes vfs.curr_dir.  The
separate curr_dir_depth global is untouched.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell a76ef875aa vfs: fold the dirfd cache statics into struct vfs
Replace the file-local dpc_* statics in vfs/dircache.c (anchor, base,
fd[], name[][], depth) with the vfs.dpc fields already declared in
struct vfs and initialized by the designated initializer in vfs/vfs.c.
Pure encapsulation: the cache logic is unchanged, DPC_MAXDEPTH becomes
the shared VFS_DPC_MAXDEPTH, and vfs_dircache_reset() now resets vfs.dpc
(consistent with vfs_init()).  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell db6c5ac6f1 vfs: tidy comments and drop unused externs after the core move
Post-relocation cleanup (no behavior change):
 - fix a doubled-prefix typo "vfs_vfs_owner_walk_parent" in the
   vfs/owner_walk.c header comment
 - vfs/vfs.c comment now points at vfs/dircache.c (not syscall.c) for the
   live dpc_* cache
 - drop unused extern decls left from assembling the moved files
   (am_sender/insecure_links in owner_walk.c, curr_dir/curr_dir_len in
   dircache.c)
2026-08-13 10:20:12 +10:00
Andrew Tridgell a5f52ee62e vfs: move the held-dirfd cache into vfs/dircache.c
Relocate the persistent ancestor-dirfd cache out of syscall.c into
vfs/dircache.c with the vfs_* public names:

  open_dir_secure    -> vfs_opendir
  get_dir_fd         -> vfs_get_dirfd
  held_dir_path_fd   -> vfs_path_dirfd
  held_dfd_for       -> vfs_cached_dirfd
  reset_dir_fd_cache -> vfs_dircache_reset

The dpc_* cache statics and dpc_dir_fd stay file-local (they fold into
the vfs struct in a later commit).  Function bodies are unchanged; the
callers in generator/receiver/sender/delete/util1/rsync are updated and
the five entry points declared in vfs/vfs.h.  This completes moving the
security core (resolver, owner-walk, dirfd cache) out of syscall.c.
No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell b5d30f926b vfs: move the operator-path ownership walk into vfs/owner_walk.c
Relocate the operator-supplied-path resolver out of syscall.c into
vfs/owner_walk.c with the vfs_* public names:

  open_no_attacker_symlinks -> vfs_open_owner_walk
  owner_walk_parent         -> vfs_owner_walk_parent

The static helpers ona_open and abspath_step move with them, as does the
operator_path_resolve flag definition (commit-8 will fold it into the vfs
struct).  Function bodies are unchanged; the call sites across the daemon
and option-parsing files are updated and the two entry points declared in
vfs/vfs.h.

With the owner-walk gone, syscall.c no longer references am_daemon, so
the now-dead `extern int am_daemon` declarations (the do_*_at wrappers
delegate to vfs_relpath_active) are dropped.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell e1b4784a08 vfs: move the secure path resolver into vfs/secure_open.c
Relocate the race-safe resolver and its policy gates out of syscall.c
into vfs/secure_open.c, and give them the vfs_* public names that the
mainline code will use going forward:

  secure_relative_open    -> vfs_resolve_open
  secure_relative_open_at -> vfs_resolve_open_at
  secure_relpath_active   -> vfs_relpath_active
  symlink_optout_allowed  -> vfs_symlink_optout_allowed

secure_walk_at stays file-local.  The function bodies are unchanged; the
call sites across receiver/sender/generator/flist/util1/clientserver/
main/options and the test harnesses are updated to the new names, and
the four entry points are declared in vfs/vfs.h.

The resolver's only consumer of am_chrooted in syscall.c left with it,
so the now-dead `am_chrooted` is dropped from the do_*_at wrappers'
local externs (and the file-scope extern), which is otherwise unused.
No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 366bbf68f0 vfs: move the dirstack path-walk primitives into vfs/dirstack.c
First step of relocating the security core.  The component-walk dirfd
stack (struct dirstack + the ds_* helpers) and the module-confinement
helpers (path_has_dotdot_component, abspath_excluded_by_module,
open_anchor_dirfd) move verbatim out of syscall.c into vfs/dirstack.c.
The secure resolver (still in syscall.c) and the held-dirfd cache reach
them through a new private header vfs/vfs_internal.h.

The functions are byte-identical to before; only their linkage changes
(the ones syscall.c still calls become non-static; ds_path_push/
ds_path_pop/ds_push stay file-local).  struct dirstack and
SECURE_OPEN_MAXSYMLINKS now live in vfs_internal.h so both sides see one
definition.  vfs_internal.h also centralizes the option/daemon externs
the VFS internals read.  No behavior change.
2026-08-13 10:20:12 +10:00
Andrew Tridgell 1e4a52e6b4 vfs: scaffold the virtual-filesystem layer
Introduce a vfs/ subtree that will house rsync's filesystem-handling code
(the do_* syscall wrappers, the race-safe path resolver, the held-dirfd
cache, the operator-path ownership walk and daemon module confinement),
separating those security-critical details from the protocol/transfer
logic.  This first commit only stands up the layer; no code is moved yet,
so behavior is identical.

 - vfs/vfs.h: public interface, included by rsync.h just after proto.h.
   Declares "struct vfs" -- the single global that will hold the state
   currently scattered across syscall.c statics (the dirfd cache, curr_dir,
   operator_path_resolve) and the clientserver.c module_* globals -- plus
   vfs_init().
 - vfs/vfs.c: defines the global instance with a designated initializer so
   the cache and module snapshot are safe by construction (a plain
   definition would zero base/module_dirfd, making fd 0 look valid); the
   t_*_secure harnesses never run main(), so this must not rely on
   vfs_init().  vfs_init() resets between transfers (inert for now).
 - Build: bundle the VFS into a static libvfs.a linked last on rsync and
   every test harness, so later commits can move code out of syscall.o
   without breaking a harness link (the linker pulls only what it needs).
   AC_CHECK_TOOL(AR)/AC_PROG_RANLIB added for portable archiving; a
   vfs/dummy config-file output creates vfs/ in VPATH builds.
 - main.c calls vfs_init() early (establishes the call site).
2026-08-13 10:20:12 +10:00
Andrew Tridgell 471e17dc0d Preparing for release of 3.5.0 [buildall] 2026-08-13 10:05:47 +10:00
Andrew Tridgell 7a36355a8c NEWS update for 3.5.0
fixed some typos and add in old 3.4.4 release info
2026-08-13 09:50:33 +10:00
Andrew Tridgell 40c93b173e update web pages and news for 3.5.0
ready for security release
2026-08-13 09:16:21 +10:00
Andrew Tridgell 6dfa73fc90 version.h: bump to 3.5.0 for the release
Drops the "dev" suffix on RSYNC_VERSION ahead of the
2026-08-13 00:00 UTC public release.
2026-08-07 15:25:11 +10:00
Andrew Tridgell d0ededae4e NEWS: finalise the 3.5.0 release entry
Date the release and bring the security section up to the full set: it
described 20 CVEs, and the release fixes 33.

The thirteen later items are added in three groups -- the peer-triggerable
memory-corruption findings from the daemon-protocol fuzzing pass, the daemon
availability and access-control issues, and the two client-side ones.

Several changes were previously described here as carrying no CVE and now do,
so those claims are removed rather than left to contradict the advisories:
rsync-ssl's unverified TLS is CVE-2026-70454, the non-positive MSG_IO_TIMEOUT
is part of CVE-2026-70462, and the early-protocol argument-count bound is part
of CVE-2026-70464.  What is left under "no CVE assigned" is only the proxy
header bounds and the xattr expansion cap.
2026-08-07 15:25:11 +10:00
Andrew Tridgell 46ad34f28d runtests: honour the built tree's backport skip list
A stable-backport branch runs a newer suite than its own code.  fleettest
already reads testsuite/skiplist/backport.txt from the tree being built and
excludes those tests; runtests.py did not, so running the suite directly --
which is what the backport branches' CI job does -- tried to run tests that
base cannot support.

Read the same file from tooldir and drop its names from both the run and the
expected-skip set: an excluded test never runs, so leaving it in the expected
set would make the oracle demand a skip that cannot happen.  A stale name is
an error rather than a silent no-op.
2026-08-06 14:28:03 +10:00
Andrew Tridgell 3d10e84266 docs: distinguish accepted breakage from absent features in backport.txt
Also note that backport CI, once it exists, has to consume these lists the
same way fleettest does -- read backport.txt from the branch being built and
pass it as RSYNC_EXCLUDE -- or it will fail on every entry and get switched
off.
2026-08-05 15:12:44 +10:00
Andrew Tridgell 2d1a91eb0f fleettest: drop backport-excluded tests from the expected-skip set too
A test named in a backport's backport.txt never runs, so it cannot skip either.
If the suite's expected-skip list also names it -- the two --compress-threads
tests are declared as expected skips because they need --use-tcp -- the oracle
waits for a skip that can no longer happen and every pipe cell reports a skip
mismatch.

Emit a '-name' removal for those, as the per-target expect_skip_omit already
does.  Only for names the spec actually contains: runtests rejects a '-name'
that removes a name nothing added, and most of a backport's exclusions (a test
for a feature it lacks) are not expected skips at all.  Deciding that needs the
@FILE references expanded locally, which is what _expand_spec_names does.

v3.4.1 with this: 5/5 cells OK on ubuntu-2404, from 3 OK / 36 not OK across the
fleet before the mechanism existed.
2026-08-05 13:38:47 +10:00
Andrew Tridgell 5cd4c682c8 fleettest: let a backport branch declare tests it cannot run
Running the 3.5.0 suite against an older branch (--repo BACKPORT
--testsuite-repo .) reports a wall of failures that are not regressions: tests
for fixes the branch does not carry, and tests whose unit-test helpers its
Makefile cannot build.  Both backport branches came back 3 OK / 36 not OK with
every distinct failure explained that way, which makes the run useless as an
oracle -- a real regression would not stand out.

A backport now declares those in its own testsuite/skiplist/backport.txt, read
from the tree being BUILT rather than the one providing the suite: only the
built tree knows what it lacks.  The names go to runtests.py as RSYNC_EXCLUDE
rather than as an expected-skip declaration, because some of them fail rather
than skip and an expected-skip list cannot describe a failure.

The overlay that puts a newer testsuite/ onto an older tree is a merge with no
delete, so a file that exists only on the backport survives it.  skiplist-spec
exempts the name from its every-list-must-be-referenced rule, since nothing
references this one by design.
2026-08-05 13:09:20 +10:00
Andrew Tridgell e8c79d2d62 testsuite: cover the empty-dir_flist parent_ndx wild-pointer read
Under inc_recurse the first flist (ndx_start == 1) has no parent entry of its
own, so recv_file_list() trusts the peer's "." entry to be the transfer root and
leaves parent_ndx at the flist_new() default of 0 -- dir_flist->files[0].  Only
S_ISDIR entries are appended to dir_flist, so a peer that sends "." with a
NON-directory mode keeps dir_flist->used at 0 while the basename strcmp still
passes: parent_ndx stays 0 and the consumers index a never-written slot.

Drives a real daemon with the pure-Python sender: an inc_recurse push whose only
flist is a regular file "." plus a regular file "a" (no directory anywhere, and
"." sorts lowest; "a" keeps file_total != 1 so the receiver doesn't divert into
recv_additional_file_list).  The file list alone is what does it -- the
generator crashes in generate_files() before any transfer phase -- reproduced on
released 3.2.7, 3.4.0 and 3.4.1.

The oracle needs both halves: a positive control that the daemon logged
"receiving file list", and the condition-specific refusal.  Accepting any
"rsync error:" line is not enough -- with "." sent as a valid directory and a
bogus file index, that form passes on "File-list index 1000000 not in 0 - 2"
without the crafted transfer root ever reaching the parser.

A fixed daemon has already refused the list and exited by the time the ndx-0
token is sent, so that send and the drain can hit a closed socket; Linux and
FreeBSD swallow it, Solaris, the other BSDs, macOS x86 and Cygwin raise
EPIPE/ECONNRESET.  Treat it as an expected outcome, not a result.

The header records what this does not prove: it gates the attack shape rather
than the parent_ndx clause (only the parse-time transfer-root check fires on a
current build), it does not exercise the receiver-side consumer, and the
dereferenced slot is not guaranteed NULL since dir_flist->files[] comes from
realloc(), not calloc().
2026-08-03 21:03:26 +10:00
Andrew Tridgell 9e40ef0eaf NEWS.md: record the safe_arg() uninitialized-byte leak
The fix shipped in the test10 snapshot but was never written up: safe_arg()'s
filename-mode buffer sizing disagreed with the writer, leaving an uninitialized
heap byte in the argument handed to the remote shell when --protect-args is off.
2026-08-03 21:03:26 +10:00
Andrew Tridgell f50d53d056 testsuite: judge the handshake deadline on the daemon's own clock
The daemon sets its deadline with time(NULL) (set_daemon_handshake_timeout,
io.c), and this test measured the elapsed time with CLOCK_MONOTONIC.  Those
agree on a quiet machine and diverge on a stalled one: a virtualised guest
resyncs its wall clock after the host deschedules it, while monotonic keeps
its own count.  The daemon then closes exactly when it meant to and the test
reports it closed early.

That is what a NetBSD CI run showed -- "closed after 39.55s, before the
expected timeout window (58.75s)" -- and it is the same shape as the macOS
failure that turned out to be the machine sleeping mid-test.

Measure the bound on the clock the daemon decides with.  Monotonic still
drives the poll budget, where the job is only "do not hang forever".

On its own that would trade a false failure for a false pass, which is worse:
a refusal or a crash arriving just as the guest's wall clock caught up would
read as a clean timeout, and no diagnostic would fire because the test would
be green.  So when the two clocks disagree -- wall says on time, monotonic
says early -- neither settles it, and the daemon has to have recorded its own
deadline firing.  The offset of its log is taken before each observation, so a
timeout it logged earlier cannot vouch for this one.

Failures now carry both clocks and that window of the daemon's log, bounded
and with control bytes escaped.  The clock note states the discrepancy without
concluding from it: a stalled host produces it, but so does an NTP step, and
either can accompany a real failure.
2026-08-03 14:49:48 +10:00
Andrew Tridgell 11e43daf90 socket: don't let open_socket_out() hang in connect()
The same kernel-side missed wakeup on the other side of the connection: a
blocking connect() can sleep forever on a connection that is already
established, with the 4-tuple ESTABLISHED at both ends and the listener's
greeting queued unread. Without --contimeout nothing breaks it.

Wait for the connect with poll() in slices rather than blocking in the
kernel, re-checking the socket on each pass, and take the result from
SO_ERROR. A finished slice is not a failure -- looping is what re-examines
the socket and recovers a missed wakeup.

--contimeout is unchanged: the alarm still fires and the caller still
reports RERR_CONTIMEOUT. The per-address errno is now stashed before
close()/alarm() can overwrite it.

Measured the same way, against a real loopback daemon: 20 hangs in 48,000
connects before, 0 in 48,000 after, with equal wall clock. This is the half
of the OpenBSD flakiness that the socketpair_tcp() fix does not cover: the
--use-tcp pass talks to a real rsyncd over a port, so it hangs here rather
than in accept().
2026-08-03 13:02:33 +10:00
Andrew Tridgell ea5a3a277f socket: don't let socketpair_tcp() hang in accept()
On OpenBSD a blocking accept() can sleep forever on a connection the kernel
has already completed: the 4-tuple is ESTABLISHED at both ends, the
connection is queued on the listener, and the accept()ing process is still
asleep in netacc. Nothing bounds that wait, so rsync hangs for good.

Poll the listener instead, with a non-blocking accept(), so a missed wakeup
costs another pass rather than the process. The accepted fd is put back
into blocking mode explicitly because BSD accept() gives it the listener's
non-blocking flag. A time(NULL) deadline bounds the whole wait the way
io.c bounds its own, rather than counting passes -- a signal on every pass
must not extend it and a poll() that returns at once must not consume it.
A listener that reports ready without yielding a connection (the peer can
reset first) pauses rather than spinning.

Measured on an OpenBSD 7.8 VM, driving the real binary through
RSYNC_CONNECT_PROG with 8 concurrent workers, alternating stock/patched
rounds: 111 hangs in 120,000 invocations before, 0 in 120,000 after, with
no change in throughput.

Every daemon test reaches socketpair_tcp() through RSYNC_CONNECT_PROG in
the default transport, so the hang landed on whichever daemon test happened
to be connecting. See dev-notes/openbsd-socketpair-accept-wedge.txt.
2026-08-03 13:02:33 +10:00
Andrew Tridgell d4d183c58c NEWS: note the AVX2 rolling-checksum over-read 2026-08-03 08:48:33 +10:00
Andrew Tridgell 9a382baddb fleettest: build one target with the SIMD/asm optimizations
Nothing in CI or the fleet has ever set --enable-roll-simd, --enable-roll-asm
or --enable-md5-asm, which is why the over-read above sat behind a "fixed"
label for two months, and why the fix applied for it went to the wrong
assembly file.

mac-x86-asm is the same host and OS as mac-x86 with all three on.  Mach-O is
the interesting part -- both problems reported against these flags were
macOS-x86-64 -- and it is the only machine in the fleet that can build the
x86-64 assembly at all.

It needs MacPorts clang 19 through CC/CXX, because Apple clang 10 (the ceiling
on macOS 10.13) rejects configure's target("default") multiversioning probe.
mac-x86 keeps the stock Apple compiler, which is what caught #161, so the two
cover different ground rather than one replacing the other.

simd-checksum is a macOS-wide expected skip, since simdtest is only built when
SIMD is enabled; this target subtracts it, because running it is the point.

Also corrects mac-x86's comment, which claimed the probe "cannot compile here
with any clang".  It is a compiler-version limit: clang 19 on that same box
compiles, links and runs it.
2026-08-03 08:48:33 +10:00
Andrew Tridgell 63fcfa399e testsuite: run the SIMD checksums against an unreadable page
simdtest allocated 64 spare bytes so it could test an unaligned buffer, which
is exactly the slack that hid a 64-byte over-read in the AVX2 assembly for as
long as it existed.  Add a pass that places the buffer flush against a
PROT_NONE page, so a read past the end faults in the test rather than in
somebody's transfer.

Every length from 128 to 4096, so each remainder mod 64 and both alignments
are covered, and all four implementations are checked -- the assembly was the
one at fault here, but the intrinsic paths preload too.

It fails closed.  A guard this test cannot set up means it is not testing what
the caller thinks, so a failed sysconf/mmap/mprotect is a failure rather than a
pass that looks identical to a real one.  And because the dispatcher falls back
on a CPU without AVX2 -- where the guard loop proves nothing about the code
under test -- it says which of the two happened rather than letting a fallback
run read as coverage.

Without the fix this segfaults; the suite's simd-checksum test reports the
non-zero exit.
2026-08-03 08:48:33 +10:00
Andrew Tridgell 2979d8eddc simd: stop the AVX2 rolling checksum reading past its buffer
The loop is software-pipelined: each iteration folds in the 64 bytes it
preloaded last time and preloads the next 64.  Nothing stopped the final
iteration doing that preload, so it always read the 64 bytes after the region
it was asked to checksum.

Not an edge case.  The assembly processes len&~63 and leaves the remainder to
the caller, so the remainder is by construction under 64 bytes and the preload
passed buf+len on every call, by 64 minus the remainder.

It normally landed in slack inside the map_ptr() window and nothing noticed.
Where the buffer ended near an unmapped page it was a SIGSEGV in the middle of
a transfer -- reported on macOS x86-64 by Roland Kletzing, whose `partial` run
died with "connection unexpectedly closed" because the generator had crashed.
A guard page reproduces it on Linux too, so it was latent there, not absent.

Run the pipelined loop one block short and finish the last block in .last,
which does the same arithmetic without the preload.  No per-iteration cost, and
checksums are bit-identical -- simdtest compares every implementation against
the C reference.

The earlier fix for that report, "lib: use .balign in md5 x86-64 asm", was to
the md5 assembly.  It addressed the linker alignment warning that appeared
alongside, not this.
2026-08-03 08:48:33 +10:00
Andrew Tridgell bb6329bc8c NEWS: note the --link-dest hard-link fallback 2026-08-03 07:10:00 +10:00
Andrew Tridgell d12bdb1579 testsuite: cover the --link-dest hard-link refusal, and ask the filesystem
An LD_PRELOAD hook refuses linkat() for a symlink source only, so the arm is
reachable on a filesystem that hard-links symlinks perfectly well.  Three
controls keep it from proving less than it looks:

 - the regular file in the same transfer must still be hard-linked, or "it fell
   back" would also be satisfied by --link-dest having been abandoned;
 - the itemised run must emit exactly one "cL... sym -> some-target" line.  A
   plain -a run cannot see a duplicated itemisation, which is how that defect
   reached an HFS+ target before this was added;
 - EPERM and ENOSYS must fall back too, since errno does not separate "cannot"
   from "may not".

itemize picked its expected change-type letter from the build capability, which
is the wrong question -- the link happens on whichever filesystem holds the test
data.  Ask that one too, and drop the XFAIL the old mismatch needed.

The hook is Linux-only, so the test joins the macOS and Cygwin skip lists,
which are required to be sorted.
2026-08-03 07:10:00 +10:00
Andrew Tridgell d09edb85e6 generator: fall back to a copy when the destination cannot hard-link
CAN_HARDLINK_SYMLINK and CAN_HARDLINK_SPECIAL are decided by configure running
linkat() on whatever filesystem the build tree happened to sit on.  The
destination is free to disagree, and one host can hold both answers: macOS
builds on APFS, which can hard-link a symlink, and backs up to HFS+, which
returns ENOTSUP.

A build that said yes had no fallback left.  try_dests_non() reported the
refusal as a transfer error and returned a matched basis, so the caller created
the entry anyway -- correctly -- and the run still exited 23.  Every
neighbouring case copes: a regular file whose link() fails goes to try_a_copy,
and a build compiled without either macro resorts to --copy-dest behaviour.
This was the same situation, discovered a little later, and the only one
treated as fatal.

Take the existing fallback on any refusal.  Singling out the "cannot" errnos is
not possible: link(2) documents EPERM both for a filesystem with no hard-link
support and for an ordinary permission refusal, and FUSE reports ENOSYS for the
same thing.  It is also what the regular-file path next door has always done
(try_dests_reg -> hard_link_one -> try_a_copy), and consistency between the two
was the point.  Where the errno does matter the surrounding transfer says so
anyway: ENOSPC, EDQUOT and EROFS fail the creation independently, EMLINK and
EXDEV mean the link was never possible.  EIO alone goes unremarked; reporting
it would put a line into --link-dest's itemised output, so it is left out on
purpose.

Returning -3 rather than -2 keeps the caller out of the "already up to date,
skip it" arm, which under --link-dest would drop the entry entirely.  Both
callers give -3 the treatment the compile-time fallback already gets -- clearing
itemizing and code -- because try_dests_non() has itemised the match itself and
would otherwise report the entry twice.

The fallback is silent, matching a build that cannot link these at compile
time; documented under --link-dest instead.
2026-08-03 07:10:00 +10:00
Andrew Tridgell 36be3b4e70 NEWS: note the merge-file confinement half of the filter fix 2026-08-03 05:35:03 +10:00
Andrew Tridgell 1bea181096 testsuite: an rrsync restricted dir must bound merge files
Uses the exclude-only merge form, which leaves no diagnostic to assert on: the
escape shows up as a file silently missing from the transfer, so the test reads
the oracle the same way an attacker would.  Pull mode, so no --delete is
involved.  Each case requires the transfer to have succeeded as well, since
refusing outright would hand the peer a denial of service.

A second escape reaches the source through a symlink, which is what makes
rsync's tracked cwd and the real one disagree -- the shape that catches a
lexical seed.  That one drives --confine-root directly: rrsync rejects the
argument spellings that would carry it, so routing it through the wrapper would
pass either way and prove nothing.

Both controls repeat their escape with an in-tree merge target and require it
to be read AND obeyed, since "the transfer failed" and "every merge file is
refused" would otherwise satisfy the escape assertions on their own.
2026-08-03 05:35:03 +10:00
Andrew Tridgell 314abcc437 rrsync: confine the server's path resolution to the restricted dir
Filter rules arrive over the protocol, long after the wrapper has exec'd rsync,
so no argv-level check can see them.  A client can name a merge file outside
the restricted dir in a dir-merge rule and have the server read it in as filter
rules; on a pull that needs neither --delete nor any verbosity.  Pass
--confine-root so the server bounds the open itself, which is the only end that
can.

Both directions: a dir-merge is read by whichever side its rule applies to, so
unlike --drop-D this is not receiver-only.  Skipped for a "/" restricted dir,
where there is nothing to confine.
2026-08-03 05:35:03 +10:00
Andrew Tridgell 3113011218 rsync: add --confine-root, bounding operator path resolution
The ownership walk that resolves operator-supplied paths asks who planted a
symlink, not where the path came out, so a symlink owned by uid 0 or the euid
is followed wherever it points.  A daemon already narrows that with the served
module root; nothing else has a root to narrow it with.

That leaves a wrapper serving a restricted directory over a remote shell with
no way to bound the resolution.  rrsync can vet the argv it is handed, but
filter rules travel over the protocol instead: a dir-merge rule can name a
merge file outside the restricted dir and the server reads it in as rules.
Redacting the resulting diagnostics does not close it, because an exclude-only
merge produces none -- every line becomes a pattern, so nothing fails to parse,
and the client reads the file's contents off which of its own names went
missing from the file list.

--confine-root gives that wrapper the root the daemon has.  The existing module
check becomes a root check that takes its root from module_dir when we are a
daemon and from the option otherwise, so daemon behaviour is unchanged; a
daemon ignores the option outright, since it arrives in a peer-supplied argv
and could only widen the module.

The tracker is seeded from getcwd() rather than curr_dir, which is only the
lexical name change_dir() was given: descend into a source argument through a
trusted symlink and the two sit at different depths, so a ".." that really
escapes looks like it landed inside.  When the cwd cannot be read there is
nothing to measure against and the open is refused -- an empty tracker does not
deny by itself, because a leading ".." pops nothing from it and an empty path
reads as an ancestor of the root.

An fd pin (/proc/self/fd/N, which rrsync uses so no later symlink can redirect
a validated option path) is spelled outside the root by construction, so the
walk transits the pin namespace and the pin is judged by what it points at.
Only a bare ".../fd/<digits>" is resolved that way, and one that will not
readlink to an absolute path is refused; rrsync's ".../fd/N/<leaf>" spelling
resolves through the magic link and has its remaining components checked
normally.

--insecure-links is refused alongside it: that opt-out returns the legacy open
before the walk that enforces the root runs, so the pair would have quietly
meant no confinement at all.
2026-08-03 05:35:03 +10:00
Andrew Tridgell 0f6f35e522 exclude: give the ":e" self-exclude rule its merge rule's provenance
The exclude-self rule that a ":e" merge synthesizes is built by hand with
new0(), so it inherited no flags.  While the merge file was still being
parsed the global parse state masked that, but once parsing finished the
stored rule looked argument-origin, and report_filter_result() printed its
pattern -- a merge file's own text -- verbatim:

    [sender] hiding file PAT-x9 because of pattern PAT-[x]9 [per-dir ...]

Plain -vv reaches this on a stock client; no --debug is involved.  That is
the fifth site of this shape, and the first to get there by constructing a
rule rather than by printing one, so the redaction helper could not catch it.

Also fix the location a per-directory merge reports.  Its fname points into
dirbuf, which is cut back to the directory before the name was saved, so the
error said "<rule from .../src/ line 1>" instead of naming .rsync-filter --
no leak, but it breaks the "redact what, keep where" bargain the rest of this
work depends on.  Save the name before the truncation.

The rrsync test's claim to close "the rest of the FILTER trace family" was
too strong and is corrected: options.c maps verbosity onto the debug flags,
so -vvv still raises a restricted server to FILTER2 and its trace metadata
comes back.  Rule text stays redacted at every verbosity, which is the
property that matters; -vvv is added to the unaffected-transfer cases.
2026-08-02 20:32:33 +10:00
Andrew Tridgell 631f9bd464 rrsync: refuse a peer-selected --debug
The --debug=FILTER traces print rule text and merge-file names that came
out of a file's contents -- and a word-split per-dir merge (":w- FILE")
turns every word of a file into a merge-file name, so the trace echoes
what the syntax errors no longer do, with nothing failing to parse.

Redacting every trace would mean carrying provenance on each rule, which
a deferred ":" merge does not currently keep.  For a restricted account
the cheaper answer is to deny the peer the switch: server_options() only
ever forwards --info, so no stock client sends --debug to a server and
the only way it arrives is a deliberate -M--debug=.  An operator
debugging their own server is unaffected.

Disabled rather than deleted from the table, because that table is
generated by the cull-options script and a regeneration would put the
line back; the test would then catch it.
2026-08-02 20:32:33 +10:00
Andrew Tridgell bb806288b7 exclude: don't hand a merge file's contents back to the peer
A filter rule that fails to parse was printed back verbatim.  When the
rule came from a file rather than an argument, that text is file
CONTENT, and the peer picks which file gets merged: a per-directory
merge rule travels over the protocol, so no argument of ours ever names
it and nothing a wrapper can see mentions it either.  Any line that is
not valid filter syntax therefore came straight back to the peer -- a
read-any-line oracle over an rrsync restricted account or a daemon
module, neither of which confines the merge open.

The syntax errors turned out to be the smaller half.  The MATCH trace
names the pattern that acted, and report_filter_result() logs at level 1
for a sender or generator, so plain -vv -- no --debug, nothing a stock
client cannot send -- returns a server-side merge file's rules:

  [generator] protecting file X because of pattern <the file's text>

So provenance is carried on the rule itself (FILTRULE_FROM_FILE), not
just in the parser: a deferred ":" merge is processed long after the
file that named it was read, and its own name is file content too.
TEXT_FROM_FILE() consults the parse-time context and the rule, so both
the immediate and the deferred paths redact.

Rather than test the provenance at each message -- which is how the last
few of these were found, one at a time, after the ones before them were
fixed -- every string that is or is built from a rule's own text goes
through rule_text().  It returns the text for an argument-supplied rule
and a description of where it came from otherwise, so a message added
later cannot reintroduce the leak by forgetting to check, and there is
one place to audit.  rule_detail() does the same for the extra detail a
message adds ABOUT the text: a character of it, an offset into it, the
[not found] bit.

Thirteen sites now route through them: the syntax errors; the modifier
character (one byte of the file, a slower oracle but still one); the
failed-open and merge-depth messages, whose pathname is file content
whenever a rule named it -- and errno with them, since it answers "does
this path exist"; both over-long messages, the deferred one of which
needed no verbosity at all; both merge-name overflows; the long-named
directory error; the [not found] openability bit; the match trace; the
add_rule, parse_filter_file and daemon-hidden traces; and the per-dir
mergelist label, which had the name baked in.

rule_detail() covers more than it first looks: the trailing-whitespace
CAUTION is computed from the rule's last byte, and "hidden by daemon
filter" distinguishes a daemon-filter rejection from an ordinary open,
so both would answer questions about text the peer cannot see.

The regression proves the chokepoint rather than the sites: making
rule_text() return its input unconditionally fails the test.  It also
pins what must NOT change for the user's own rules -- the whitespace
warning still fires, and an over-long argument rule is still reported at
full length (the helper buffers at BIGPATHBUFLEN, as rprintf does, so
redaction does not quietly truncate what the user typed).

Bounded and left alone: the numeric rflags in the FILTER2 trace and the
in/exclude wording still describe a file-derived rule without quoting
it, and the daemon's own FLOG line records the name it filtered -- that
one goes to the operator's log, not the peer.

Rules given AS arguments are still echoed in full -- that text is the
user's own, and hiding it would only make ordinary typos harder to fix.
Where a rule did come from a file, the diagnostic names the file and
line instead, which is more useful anyway.

Two things the location itself needed: fname can point into
parse_merge_name()'s static buffer, which a merge rule inside the same
file overwrites while we are still reading it, so a rule after a nested
merge was blamed on the nested file -- keep our own copy.  And a CRLF
pair was counted as two line endings while word-split mode counted
tokens rather than lines, so the number pointed at nothing; consume the
LF of a CRLF (preserving the byte for the next rule if pushback ever
fails), and report word-split sources without a line number.

Not covered, deliberately: a rule's provenance is not serialized by
send_filter_list(), so it does not survive to the far side.  That is
right -- only the client sends that list, and the server already knows
the patterns the peer gave it.
2026-08-02 20:32:33 +10:00
Andrew Tridgell d1756203b9 testsuite: stop the test rsyncd stranding a connection child
A daemon test could leave an orphaned rsyncd squatting its port even when
every test PASSED, so nothing in the results pointed at it. On Cygwin the
orphan then wedged the whole fleet: it kept the ssh session from closing,
so fleettest's run_on() blocked until its 2400s timeout and unrelated
tests failed with 300s timeouts as collateral. One such wedge cost a fleet
run 21 minutes.

Cause: rsyncd forks a child per connection, but _stop_rsyncd only killed
the parent -- the one pid the Popen handle knows. A child still winding up
or down when the test ended survived, inherited the listening socket, and
was reparented to init. Cygwin turned that from untidy into unrecoverable:
its signals are cooperative, delivered by a helper thread inside the
target, so a process sitting in a Windows call ignores even SIGKILL. kill,
killpg and pkill all failed against it, which also defeated the orphan
reapers and fleettest --cleanup.

Snapshot the daemon's children before killing it (once the parent is gone
they are reparented and no longer identifiable as ours) and kill them too,
re-checking with _pid_is_rsync before each signal so a pid recycled in the
meantime is never signalled. Where signals cannot win, fall back to
terminating the winpid via taskkill; fleettest --cleanup gets the same
fallback, so it can no longer report SURVIVED and leave the port squatted.

_reap_group() reports success only once the daemon is confirmed gone
rather than when a signal was merely accepted -- on Cygwin a signal is
routinely accepted by a process that then ignores it -- and confirms with
a bounded poll, because SIGKILL is asynchronous and calling a
still-terminating process "alive" would make _probe_bindable() skip its
retry and fail a test for a port that was about to free itself.

_cleanup_rsyncd() keeps the port's pid record only while it still names a
live rsync. Keying that on the port being busy instead looks safer but is
worse: a port sits in TIME_WAIT after a passing test, so a record naming
an already-dead pid would be retained forever, and nothing clears such a
record -- yet no reaper can use it either, since they all reject it at the
_pid_is_rsync guard, leaving only the hazard that its pid is recycled onto
an unrelated rsync.

The daemon stays in the TEST's process group on purpose: runtests.py
killpg's that group on a per-test timeout, and that is what keeps a
timed-out test from stranding its daemon. An earlier version of this fix
gave the daemon its own group so one killpg would catch the children --
which silently broke that sweep, and a full Cygwin pass then stranded two
parent daemons when variety hit its timeout.

Two residual limitations are documented in the code rather than left to be
rediscovered: _kill_pid's check-then-signal is inherently a TOCTOU
(narrowed to microseconds, not closed; closing it needs pidfd or retained
Windows handles across seven platforms), and _stop_rsyncd cannot collect
children when the parent has already exited on its own, because the
parent-child link it relies on is gone by then.

Measured on a Cygwin VM, 4 proxy/daemon tests x 8 runs at -j4: before 5/8
runs left an orphan (one left two), after 0/10. All tests passed in every
run, before and after -- which is the point: the leak was invisible to the
suite. A test killed by the runner's timeout still leaves no daemon
behind.
2026-08-02 16:05:24 +10:00
Andrew Tridgell d95217fbbe fleettest: run only the daemon tests in the tcp pass
The tcp pass re-ran the whole suite over the same build the pipe pass had
just swept, but --use-tcp is observable through exactly one code path:
RSYNC_TEST_USE_TCP is read once (rsyncfns USE_TCP) and acted on once (in
start_test_daemon). A test that never reaches there cannot tell the two
passes apart, so 186 of the 340 tests were producing the same result
twice.

runtests.py --daemon-tests-only keeps the tests that can reach the daemon
transport, matched against the closure of every rsyncfns helper leading to
USE_TCP/start_rsyncd/claim_ports plus the modules that open a daemon
connection themselves. The token list is deliberately over-broad and an
unreadable test is kept, so the filter can only ever run too much; audited
against the tests it drops, none of which reach the transport (their
"daemon" hits are the unix username, a macOS ACL principal, mount --bind,
and docstrings declaring the test local-only). The dropped count is always
printed rather than left implicit.

The narrowing is only sound as the second half of a pipe+tcp pair, so it
is gated on the pipe pass having run: under --transport tcp that pass is
the only one there is, and narrowing it would drop the other 186 tests
from the run altogether. --full-tcp forces the full sweep either way.

Measured on the full suite: serial work 558s -> 367s.
2026-08-02 16:05:24 +10:00
Andrew Tridgell 653c4e8db9 testsuite: make --race-timeout actually control the race budget
The race tests are the suite's slowest by a wide margin -- a race test is
a negative oracle, so it passes by spending its entire budget. Most of
them wrote `max(RACE_TIMEOUT, 10.0)`, which ignored --race-timeout below
10s: the documented knob did nothing for 10 of the 16 tests.

Replace the floor idiom with race_budget(default), where the per-test
default applies only when the operator did not pass --race-timeout, and
runtests.py exports race_timeout only when the flag was actually given.
Defaults are unchanged (measured identical at 15.3s/10.3s/5.2s).

Validate the value rather than take it on trust. A race test loops
`while monotonic() < deadline`, so a zero, negative or NaN budget runs the
body zero times and the test reports PASS without ever racing, and an
infinite one runs until the unrelated per-test timeout; the old
max(..., 10.0) floor had made all of that unreachable, so removing the
floor had to come with rejecting the input. An unparsable value in the
environment counts as unset for the same reason -- falling back to the 5s
baseline while still counting as "set" would silently halve a 10s or 15s
oracle that nobody asked to shorten.

NB the *_test.py glob spans four committed symlinks (chown-fake,
devices-fake, exclude-lsh, xattrs-hlink); sed -i would replace each with a
copy of its target, so they are rewritten with --follow-symlinks semantics
and left as symlinks.
2026-08-02 16:05:24 +10:00
Andrew Tridgell 501165218c fleettest: add --keep-on-fail, and pass --timing to the targets
A fleet run costs a full configure+build on every machine, and the report
only names the tests that failed -- so seeing WHY one failed meant paying
for a second whole run, against a race test that may not fail the same
way twice.

--keep-on-fail saves the full build/test output of every target that came
back with anything unexpected, and keeps that target's remote run dir
(with the scratch trees the failing tests left). Clean targets are swept
as before.

--timing now also asks each target's runtests.py for its own per-test
table, so a slow cell can be attributed to actual tests rather than just
named as the hold-up.
2026-08-02 16:05:24 +10:00
Andrew Tridgell 6bad3be6fc runtests: report per-test wall-clock with --timing
The suite reported which tests ran, never how long any of them took, so
"the fleet is slow" could not be attributed to anything. Time each test
and, with --timing, print the slowest first.

The footer gives the two bounds that decide what to do about a slow run:
the serial sum (what one worker would take) and the floor set by the
longest single test, which no amount of -j can beat.
2026-08-02 16:05:24 +10:00
Andrew Tridgell 2a20d6ec0a testsuite: list daemon-handshake-timeout in the shared skip list
The test needs a real listening socket to stall, so it require_tcp()s and skips
on the default pipe transport -- like daemon-chroot-acl and the proxy tests
alongside it.  runtests.py compares the skip set against RSYNC_EXPECT_SKIPPED on
a FULL run, so without an entry every pipe-mode CI job reports an unexpected
skip and fails, while the tcp jobs pass.
2026-08-02 13:26:41 +10:00
Andrew Tridgell 4d7c243ca6 daemon: bound the pre-transfer handshake with a timeout
rsyncd.conf(5) says of "timeout": "Using this parameter you can ensure that
rsync won't wait on a dead client forever."  That did not hold before a module
was known.  set_io_timeout() ran at the very end of rsync_module(), so the
greeting, authentication and the whole argument list were read with no I/O
timeout at all -- a peer could stall at any of them and the child waited
indefinitely.  Measured: 20 connections sending "@RSYNCD: 31.0" with no newline
were all still alive well past timeout=5, and only went away when the client
hung up.

The consequence is worse than an idle process.  claim_connection() runs BEFORE
auth_server(), so naming a module is enough to take a slot: an attacker with no
credentials could occupy every "max connections" slot of an authenticated
module and hold them for as long as it kept the sockets open, with the
documented control unable to recover them.  It costs the attacker nothing --
five stalled children measured 0 CPU ticks over 5s -- so this is descriptor and
slot exhaustion, not load.

Bound the handshake at min(configured, 60s).  "timeout" is a Locals parameter,
so lp_timeout(-1) reads the global section -- the same -1 idiom start_daemon()
already uses for lp_reverse_lookup().

Both halves of that minimum matter.  "timeout" DEFAULTS TO 0, so honouring only
the configured value would leave the daemon most exposed to this -- one whose
administrator never set a timeout -- exactly as pinnable as before.  And capping
matters because an operator who sets "timeout = 86400" for slow links is asking
for patience during a TRANSFER, not for a stranger to hold a pre-auth slot for a
day.  The pre-module phase has no legitimate reason to take even a minute.

The bound is retired the moment the module is known, which is what lets the
configured value still govern the transfer.  That retirement is load-bearing:
the per-module test only ever LOWERS the timeout (`lp_timeout(module_id) <
io_timeout`), so leaving the handshake bound in place would silently clamp a
module that asked for more -- "timeout = 300" would get 60.  It is cleared
before that test runs, and only when io_timeout is still the value we armed,
since the client's own --timeout is parsed in between and must win on its own
terms.  Verified: with no global timeout and "timeout = 120" in the module, a
connection idles past 75s rather than being dropped at 60.

Applied only for a real socket daemon (am_daemon > 0): an rsh-run daemon has no
listener to exhaust.

Verified end to end with max connections = 2 and timeout = 5: with two stalled
unauthenticated connections holding both slots, a legitimate client is refused
during the timeout window and served once it elapses.  Before this change it was
refused both times.

Reported by Chamal De Silva.  Not a regression -- 3.2.7 behaves the same way.

An idle timeout alone is not enough, which the review of the first version of
this change made concrete: safe_read() consults it only when poll() TIMES OUT,
so a peer sending a byte more often than allowed_lull (timeout/2) is never
checked at all.  Measured: one byte every 20s held the handshake open for 182s
against a 60s bound, keeping its max-connections slot the whole time -- the
reported attack, merely with the attacker typing.

Non-positive configured values are treated as "use the built-in bound":
"timeout" is parsed with atoi(), so "timeout = -1" would otherwise reach
set_io_timeout() (which reads it as no timeout) and alarm() (which would take it
as a huge unsigned count), disabling the very bound it looks like it configures.

The client's own --timeout is no longer inferred by comparing values, which could
not distinguish it from an identical armed value: io_timeout is zeroed before
parse_arguments(), so anything non-zero afterwards came from the client.

So the bound is absolute and lives in the READ PATH, next to the idle timeout
it complements: safe_read() caps each poll() at whatever is left of it and
gives up when it expires, so it is re-checked on every iteration and a peer
that keeps typing cannot outrun it.

It is deliberately NOT alarm()/SIGALRM.  Three earlier attempts used one and
each regressed something: fork() clears pending alarms, so the "post-xfer exec"
parent -- which waits for the ENTIRE transfer -- kept the deadline and _exit()ed
mid-transfer, skipping the hook and releasing the max-connections fcntl lock
while the transfer child ran on; "pre-xfer exec" and the name converter are
operator scripts that may legitimately outlast any handshake bound; and the
cancellation sat inside an exec-environment compile guard, so a build without
setenv/putenv kept it armed through the transfer.  A deadline consulted only
where the daemon is already blocked reading a peer has none of those hazards.

It is also kept entirely separate from io_timeout, which is an idle timeout the
module or client may set.  Mixing them clamped a module asking for more than the
bound ("timeout = 300" became 60) and leaked the handshake value into the
transfer.  Verified: module 300 stays 300, and a client --timeout=7 still wins.

Armed for each peer-driven phase and cleared between them: at the start of the
handshake, tightened by the module's own timeout once the module is known and
its slot claimed, cleared across the hook/fork setup, re-armed before
"@RSYNCD: OK" so it spans BOTH read_args() calls including secluded args, and
cleared before the transfer.

That argument-read coverage is the part that matters most.  auth_server()
returns immediately when a module sets no "auth users", so on an ANONYMOUS
module nothing is authenticated: without a bound there, a peer could claim the
slot, take the OK, and trickle an unterminated argument line forever.  Measured:
still open after 150s before, closed at 60s after.
2026-08-02 13:26:41 +10:00
Andrew Tridgell cf15b1bb24 gitignore: cover the newer test helper binaries
.gitignore lists the older helpers (tls, getgroups, wildtest, trimslash,
t_unsafe, getfsdev) but not the ones the security work added, so a `git add -A`
in a built tree stages ~4 MB of ELF -- which is exactly how nine of them ended
up committed on this branch before being removed again.
2026-08-02 08:13:25 +10:00
Andrew Tridgell ba3e9d681b testsuite: don't report a bogus escape when the scratch path has a space
The three operator-path-traversal daemon tests failed with "escaped: a '..'
traversal reached the excluded subtree" when the build path contained a space.
That reads like a confinement failure and is not one.

rsyncd.conf's "exclude" is a SPACE-SEPARATED list of patterns, so
"exclude = /ws test/.../secret/" is two patterns, neither of which is the
directory meant to be protected.  Nothing was excluded, so the traversal
reached a subtree that was never actually off limits.

Confirmed by running the same case with a "filter" rule, which the parser
deliberately does not split at an internal space: it passes, so the traversal
protection itself holds.

Left on "exclude" rather than switched to "filter" -- these tests exist to
cover the exclude path -- and skipped with the reason when the scratch path
makes that config inexpressible.

Worth knowing outside the testsuite: an operator whose module paths contain a
space gets no warning that "exclude" silently matched nothing.
2026-08-02 08:13:25 +10:00
Andrew Tridgell 84832f0ff7 rsync-ssl, testsuite: quote paths interpolated into shell text
Third layer of the space-in-build-path work, and the first part that is not
test-only.

rsync-ssl expanded the helper program paths unquoted -- "exec
$RSYNC_SSL_OPENSSL s_client ...", likewise for gnutls and stunnel -- so an
openssl installed under a path containing a space is split and never runs.
That affects anyone with such a path, not just the testsuite.  Quoted; the
neighbouring $caopt/$certopt/... stay unquoted because they are option lists
that rely on word splitting.  Its own re-exec passes --rsh="$0 --HELPER",
which rsync then tokenises, so $0 is single-quoted for rsync's parser.

On the test side, the same shape in generated shell scripts: redirect targets
("printf ... > {capture}") and daemon hook commands, which rsync runs through a
shell, both interpolated a path with no quoting.

In a directory with a space: 235 pass, 18 fail, from 0 able to run.
Unchanged in a normal path: 257 passed, 0 failed.
2026-08-02 08:13:25 +10:00
Andrew Tridgell 0789709aeb testsuite: quote the rsync command everywhere a shell re-parses it
Second layer of the space-in-build-path work.  Quoting the Makefile got the
runner started; these are the places that then hand the binary's path to
something that splits on whitespace.

  - RSYNC_CONNECT_PROG is run by a shell.  This was the big one: an unquoted
    daemon command turned every daemon-mode test into
    "sh: 1: /path/to/ws: Permission denied".
  - RSYNC_RSH / --rsh is tokenised by rsync itself (do_cmd() in main.c, which
    honours ' and "), so support/lsh.sh needs quoting when srcdir has a space.
  - --rsync-path is a command line run by the REMOTE shell, so rsync passes it
    through unsplit and lsh.sh's eval re-parses it.
  - The generated rsync-shim scripts interpolate RSYNC into "#!/bin/sh\nexec
    ...", where it is shell syntax rather than an argv entry.

rsync_path_arg() and rsh_cmd() build those strings by splitting the command and
re-joining with shlex, so a plain path with a space comes back quoted while a
wrapper command ("valgrind ... /build/rsync") stays several words.

split_rsync_cmd() also has to cope with RSYNC once a test has appended options
to it -- chown-fake and friends do -- where the string is no longer a filename.
It now takes the longest leading run that names an existing file as the program
and splits only what follows.

In a directory with a space: 231 pass, 22 fail, from 0 able to run before the
first commit.  Unchanged in a normal path: 257 passed, 0 failed.
2026-08-02 08:13:25 +10:00
Andrew Tridgell b1d2c32b1d build/testsuite: survive a build path that contains a space
`make check` died immediately when the build directory had a space in it:

    ./runtests.py --rsync-bin=`pwd`/rsync -j 8
    rsync_bin /Volumes/Untitled is not a file

Reported by Roland Kletzing building in "/Volumes/Untitled 2"; it reproduces
anywhere, and is not macOS-specific.

Makefile.in interpolated an unquoted `pwd` into --rsync-bin at five sites, so
the shell word-split it.  Quote those, and --tooldir at the installcheck site,
which had the same bug and was not in the report.  Quote "$(srcdir)/runtests.py"
too: the script's own path word-splits just as readily.

That alone only gets as far as starting the runner.  rsync_argv() then did
shlex.split(RSYNC), which turns "/ws test/rsync" into two nonexistent programs.
RSYNC may legitimately be a wrapper command line ("valgrind ... /build/rsync"),
so it cannot simply stop splitting; split_rsync_cmd() checks whether the string
names an existing file first -- a path that exists is one word by definition --
and only falls back to shlex for a real command line.  Nine tests that called
shlex.split(RSYNC)/(RSYNC_PEER) directly go through it as well.

Deliberately a function called at use time rather than a pre-split constant:
chown-fake, devices-fake, chown, devices and partial_nowrite append
' --fake-super' or ' --super' to rsyncfns.RSYNC part-way through, and a cached
split hands back the pre-mutation command.  Caching it is what broke those two
tests while I was writing this.

The suite is still not space-clean -- in a directory with a space 157 pass and
97 fail, against 0 able to run before.  The rest is a separate problem: mostly
transfers whose --rsync-path is re-parsed by a remote shell, which needs
quoting at a different layer.  No change in a normal path: 257 passed, 0 failed.
2026-08-02 08:13:25 +10:00
Andrew Tridgell a0952930a1 build: require <poll.h>, not just poll()
io.c and socket.c include <poll.h> unconditionally, but configure only
required the function.  A system that exposes poll() through some other
header would pass configure and then fail to compile -- the AC_CHECK_HEADERS
result for poll.h was collected and never used.

Require the header too, with its own message.  Verified both ways: a normal
configure still succeeds, and forcing ac_cv_header_poll_h=no now stops with
"rsync requires <poll.h>" rather than failing later in the build.
2026-08-01 20:02:02 +10:00
Andrew Tridgell 8e0bd08a62 testsuite: CC is a command, not a filename
highfd-hang probes FD_SETSIZE by compiling a snippet, and passed $CC to
subprocess as a single argv[0].  CC='ccache gcc' then looks for a program
literally named "ccache gcc" and the test dies with FileNotFoundError
instead of probing -- and ccache is wired into PATH on the CI fleet, so
this was reachable rather than theoretical.

Split it with shlex, and treat an unusable CC as "cannot probe" (skip)
rather than an error: the fallback to cc/gcc already handles a missing CC,
and a broken one should behave the same way.
2026-08-01 20:02:02 +10:00
Andrew Tridgell d5cce08503 io: describe the timeout guards in terms of poll(), not select()
The MSG_IO_TIMEOUT cap and set_io_timeout()'s negative/overflow guards
were written when these loops used select(), and their comments explain
the danger as a tight select()-EINVAL spin on a negative tv_sec.

Under poll() the failure mode inverts: the timeout is a millisecond count
where a negative value means "wait forever", so a wrapped allowed_lull
hangs the process instead of spinning it.  The guards are still needed and
unchanged -- only their stated reason was wrong, and a rationale that no
longer matches the code is what gets a guard removed later.

poll_timeout_ms() clamps the value too, so the guards are now belt and
braces; noted so neither looks redundant on its own.
2026-08-01 20:02:02 +10:00
Stuart Inglis bf3a11cf24 io/socket: address review of the poll() conversion
Follow-up to the FD_SETSIZE fix, covering the points raised in review.

Negative/overflowing I/O timeouts.  set_io_timeout() could produce a negative
select_timeout (a peer-supplied MSG_IO_TIMEOUT value was applied unchecked),
and every wait now passes select_timeout * 1000 to poll(), where a negative
millisecond count means "wait forever" -- so a hostile or buggy peer could
stall the other side and bypass keepalives entirely.  select() used to reject
that with EINVAL, which kept the loop and check_timeout() running.  Clamp a
negative argument to 0, compute allowed_lull without overflowing near INT_MAX
(secs / 2 + secs % 2), ignore a non-positive MSG_IO_TIMEOUT value, and funnel
all three waits through poll_timeout_ms(), which keeps the count positive and
bounded.

The daemon accept loop had the same fd_set overflow.  start_accept_loop() still
stored listening sockets in an fd_set, so a daemon started with enough
descriptors already open got listener fds >= FD_SETSIZE and hit the same
undefined behaviour at startup -- verified: with the old code a transfer
through such a daemon yields nothing, with this change it succeeds.  Converted
it to poll() as well.

Readiness testing.  Treating any non-zero revents as ordinary readiness was
wrong: poll() reports POLLERR/POLLHUP/POLLNVAL unrequested, and an invalid fd
shows up as POLLNVAL on a successful poll() rather than -1/EBADF, which left
the EBADF branches dead and let an invalid ff_forward_fd reach
forward_filesfrom_data() (where EBADF reads as EOF).  Use role-specific masks
(POLL_RD_BITS / POLL_WR_BITS), handle POLLNVAL explicitly in all three loops,
and request POLLPRI so select()'s old exception set is not silently dropped.

A bidirectional fd is no longer entered twice.  A direct daemon connection uses
one fd for both directions; it now occupies a single pollfd row with OR-ed
events instead of two rows carrying different masks, which also avoids the
Cygwin < 3.3.6 duplicate-entry readiness bug.

poll() is now a declared requirement: configure.ac checks for poll.h and
poll(), failing with a clear message rather than leaving it implicit.

The test no longer hardcodes FD_SETSIZE (1024 on glibc but 65536 on 64-bit
Solaris, where it would have opened too few fds and passed vacuously); it asks
the C library for the real value via a small compiled probe and skips if that
is unavailable.  Its description now also covers the fortified-libc case, where
the pre-fix result is an abort rather than a hang.

(cherry picked from commit 7ef165dd45)
2026-08-01 20:02:02 +10:00
Stuart Inglis 44fdf0bc43 io: use poll() instead of select() to avoid an FD_SETSIZE hang (issue #231)
rsync's I/O loops (safe_read, safe_write, and the main perform_io
multiplexer) waited for readiness with select() and fd_set bitmaps. An
fd_set can only represent descriptors below FD_SETSIZE (1024 with glibc).

When rsync is started with many descriptors already open -- e.g. inherited
from a parent process that leaked fds, a high "ulimit -n", or a busy daemon
-- its own socket and pipe fds get allocated at or above 1024. FD_SET() and
FD_ISSET() then index past the end of the fixed-size fd_set, which is
undefined behavior: select() reports the fd as ready, but FD_ISSET() reads
the out-of-bounds bit as 0, so the read or write never happens and rsync
spins at 100% CPU forever with no progress. This is the long-standing
"rsync hangs at 100% CPU on large systems" report, and it matches the
MemorySanitizer use-of-uninitialized-value seen in perform_io.

Convert the three loops to poll(), which identifies descriptors by value in
a small array and has no FD_SETSIZE ceiling, so a high-numbered fd works
fine. rsync only ever waits on a handful of fds (at most three in
perform_io: in_fd, out_fd, and the files-from forward fd), so poll() is as
fast as -- or faster than -- select() here; the select()-vs-poll() cost gap
only appears when watching thousands of descriptors, which rsync never
does. The remaining select(0, ...) call is a pure timed sleep with no fds
and is unaffected.

The conversion is behavior-preserving: the same max_fd bookkeeping decides
when there is nothing to wait on, the per-fd readiness checks map to the
matching pollfd revents, and the timeout is the same (now expressed in
milliseconds).

testsuite/highfd-hang_test.py reproduces the hang deterministically by
opening enough inheritable dummy fds to push rsync's descriptors past
FD_SETSIZE before an ordinary transfer; it hangs (caught by a timeout) on
the select() code and passes instantly with poll().

(cherry picked from commit 4a751a2ceb)
2026-08-01 20:02:02 +10:00
Andrew Tridgell 1078876a31 testsuite: cover sparse holes in matched inplace blocks
(cherry picked from commit cacb9bddbd)
2026-08-01 20:02:02 +10:00
Stuart Inglis f7c67d4a1d testsuite: drop the strace-based --sparse write-count test
The test needed strace, so it skipped on every platform without it (macOS, the
BSDs, Solaris, and the AlmaLinux container).  runtests.py compares the skip set
against RSYNC_EXPECT_SKIPPED and treats any unexpected skip as a failure, so it
turned the macOS and AlmaLinux 8 jobs red and would have needed an entry in
each platform's expected-skip list -- an entry that would itself go stale the
moment strace became available.

It also earned its keep poorly: it guarded a syscall-count property rather than
correctness, and it was not what caught the --inplace --sparse hole regression
in this series (review and differential fuzzing did).  Correctness of the sparse
paths is already covered by the sparse and preallocate tests; the write-count
improvement is recorded, with measurements, in the commit that made it.

(cherry picked from commit b7639c8c71)
2026-08-01 20:02:02 +10:00
Stuart Inglis bb804f80f6 fileio: keep punching interior holes on the matched (--inplace) path
Review caught a release-blocking regression in the previous commit: with
--inplace --sparse, interior zero runs inside *matching* blocks were left
allocated.

The scan I added applied only to the write path.  The use_seek branch --
reached via skip_matched() when an in-place update finds identical data --
still trimmed just the leading and trailing zeros and lseek()'d over the whole
middle.  Before the change, write_file() fed that data through
SPARSE_WRITE_SIZE slices, so an all-zero slice in the middle of a large
matching block became a deferred hole like any other; afterwards those blocks
stayed fully allocated.  Reproduced with the reported case: an 8 MiB file whose
every 32 KiB block is 4 KiB data / 24 KiB zeros / 4 KiB data, copied onto an
identical destination with --inplace --sparse --no-whole-file
--block-size=32768, occupied 2048 KiB before this series, 8192 KiB after the
previous commit, and 2048 KiB again with this one.  Content was byte-identical
throughout; only the on-disk sparseness regressed.

Rather than duplicate the scan in the use_seek branch, drop that branch and run
both cases through the one loop, with the sole difference factored into
emit_sparse_span(): a span that is not becoming a hole is written normally, or
merely seeked past when the bytes on disk already match.  The hole itself is
flushed by the existing flush_sparse_hole(), which already picks do_punch_hole()
over do_lseek() while inside the preallocated extent -- and for an in-place
transfer the receiver sets preallocated_len to the basis size, so a matched
interior hole is genuinely deallocated rather than skipped over.

Verified by differential fuzzing against the pre-series binary: 37 file shapes
(including the reported one, runs either side of the SPARSE_WRITE_SIZE
threshold, all-zero and hole-free files, and randomised mixes) across both the
plain and --inplace modes, comparing contents and allocated blocks.  Contents
match and allocation is never worse than before the series.

(cherry picked from commit 231de22f7c)
2026-08-01 20:02:02 +10:00
Stuart Inglis 6a15079cab fileio: coalesce --sparse writes instead of 1-KiB dribbles (issue #773)
write_file()'s sparse path sliced each span into SPARSE_WRITE_SIZE (1024-byte)
pieces and write_sparse() issued one write() syscall per slice.  Copying a
large *non-sparse* file with --sparse therefore cost roughly one write() per
kilobyte -- about a million write() calls for a 1 GiB file -- which on real
storage ran far slower than the same copy without --sparse (the bug report
measured 1.36 MB/s vs 391 MB/s, ~280x).  The 1024-byte chunk is also smaller
than a filesystem block, so it cannot even create finer holes than a plain
copy could.

Rewrite write_sparse() to scan the whole span itself: it looks for interior
runs of zeros that are at least SPARSE_WRITE_SIZE long -- the same hole
granularity rsync has always used -- and emits each intervening non-zero
region (which may include shorter zero runs not worth a hole) with a single
write().  do_punch_hole() advances the file offset just like the lseek() path,
so flushing a deferred hole between segments keeps the position correct.

The hole granularity is unchanged, so sparseness is identical; only the
syscall pattern changes.  Measured on a 100 MiB random (hole-free) file:
write() syscalls drop from 100,730 to 6,125 (~16x), now tracking the data's
natural chunking rather than its size in kilobytes.  Verified byte-identical
and equally sparse output for hole-free, large-hole, small-interior-hole,
all-zero, --inplace, and --preallocate cases.

testsuite/sparse-write-count_test.py copies a 16 MiB hole-free file under
strace and asserts the write() count stays far below the old size/1024
behaviour (it skips where strace is unavailable).

(cherry picked from commit 5ecab683a8)
2026-08-01 20:02:02 +10:00
Andrew Tridgell 56f6b67453 generator: decide the unconfined mknod fallback at compile time
The ENOSYS test was the wrong discriminator.  It was meant to detect "this
build compiled no fd-relative create at all", but a live mknodat() or
mkfifoat() returns ENOSYS too -- an unimplemented FUSE mknod does, and
seccomp can synthesise it -- so a runtime failure could route a create
through the unconfined path on a platform that has the secure primitive.

On Linux the fall-through lands in do_mknod_at(), which re-confines with
secure_relative_open(), so no escape was reachable there.  The real gap is
a mixed-capability build (mkfifoat() but no mknodat()): a runtime ENOSYS
from a real mkfifoat() reached the unconfined fallback even though an
fd-relative FIFO primitive existed.

Whether a primitive exists is a property of the build, so decide it there:
no_atfd_mknod_primitive() is false wherever mknodat() covers the node type,
where mkfifoat() covers a FIFO, or where --fake-super creates through
openat() -- which is always present and was previously able to fall back on
its own unrelated failures.

Also correct the SECURITY.md residual, which overstated the loss.  Plain
mknod()/mkfifo() do not follow a planted leaf symlink; they fail EEXIST,
verified directly against a symlink to a victim file.  What a no-mknodat
platform actually loses is the pinned parent, so the residual is a
parent-component race rather than a followed basename, and ordinary
fake-super placeholder creation stays confined via openat(O_NOFOLLOW).

And narrow t_symlink_secure's skip: it skipped the whole helper without
mknodat(), including do_symlink_at() assertions that do not depend on it.
Only the do_mknod_at() checks are now skipped, and the helper still skips
outright when neither applies rather than passing vacuously.
2026-08-01 19:19:42 +10:00
Andrew Tridgell fd8bc41967 testsuite: itemize's no-hardlink-symlink expectations described no rsync
The hardlink_symlinks==false branch set five values that differ from the
true branch.  Only one of them is real.

Measured on macOS 10.13 -- the one platform that takes it, where
linkat(AT_FDCWD, sym, ..., 0) is EOPNOTSUPP so a symlink cannot be
hard-linked even though ordinary hard links work -- rsync prints the
attribute field as blanks rather than 'c.t.' + dots, says "foo/sym is
uptodate" rather than "foo/sym -> ../bar/baz/rsync", emits no trailing
--copy-dest line at all, and uses .L where the branch expected cL.

Only the change-type letter genuinely differs, and only where the symlink
itself is transferred: hL when hard-linked, cL when copied.  So the other
four knobs are gone rather than corrected -- keeping them as variables
that hold the same value on both paths would preserve the suggestion that
something varies.

It went unnoticed because every platform that had run this test takes the
other branch: Linux, FreeBSD and OpenBSD all report hardlink_symlinks
true, and Cygwin skips itemize.  macOS before 13 is the first target to
reach it.

Derived by collecting every mismatching block in one run rather than
fixing them one at a time, so the values are what rsync emits rather than
a guess that makes one block pass and leaves the next wrong.
2026-08-01 19:19:42 +10:00
Andrew Tridgell 2a44bf4e9f fleettest: record mac-x86's two skip-list differences from macOS
That host is macOS 10.13 and has no mknodat(), so it differs from the
shared macOS list in both directions:

  symlink-mknod-fakesuper-symlink-race  skips there and only there --
      do_mknod_at() is the unconfined fallback on such a build, so the
      test skips itself rather than asserting a property the build does
      not have.  mac2 is macOS 26 and still runs it.
  sender-remove-source-root-anchor      the macOS list expects it to
      skip; this host runs it.

Neither can go in testsuite/skiplist/macos.txt: both Macs share that
file and they disagree.  The per-target extra/omit fields exist for
exactly this.
2026-08-01 19:19:42 +10:00
Andrew Tridgell abe4a2717e generator: fall back where the platform has no fd-relative mknod
do_mknod_atfd() returns ENOSYS on a platform that compiled no
fd-relative create at all -- older macOS has mknod() and mkfifo() but
neither mknodat() nor mkfifoat() -- and gen_entry_mknod() returned that
straight to the caller, so a FIFO or device node could not be created:

    rsync: [generator] mknod ".../afifo" failed: Function not implemented (78)

That is not the stance SECURITY.md sets out.  Where an operation can be
secured on some platforms but not others, rsync takes the race-safe path
where it exists and falls back to the historical unconfined behaviour
where it does not, "rather than refusing the operation outright".  Its
one stated exception is the nested-socket bind(), which gen_entry_mknod()
already routes away from this path.

So fall through to do_mknod_at(), which on such a platform is do_mknod()
by design.  Only on ENOSYS: any other errno is a real failure and must
not be retried through the unconfined path.

None of this was reachable before: the platform did not link at all until
the previous commit, which is why a refusal sitting where the documented
rule says fall back went unnoticed.

The race helper skips itself where mknodat() is absent.  do_mknod_at() IS
do_mknod() there -- the held-dirfd walk and the O_NOFOLLOW leaf create are
compiled out, not failing -- so its checks were asserting a property the
build deliberately does not have, and reported the accepted residual as a
module escape.

SECURITY.md gains that residual under "Known residuals": it previously
covered only the socket case, and said nothing about the whole special-
file path degrading where mknodat() is missing.

Checked by rewriting config.h the way macOS 10.13 has it (mknod and
mkfifo yes, mknodat and mkfifoat no): --specials now creates the FIFO
where it previously failed with ENOSYS, the race test skips instead of
failing, and the suite is 254/0.  Unchanged on Linux at 255/0.
2026-08-01 19:19:42 +10:00
Andrew Tridgell c458873481 syscall: guard do_mknod_atfd()'s mknodat() with HAVE_MKNODAT
It used HAVE_MKNOD.  Older Darwin has mknod() but not mknodat(), so the
call was compiled and then failed to link:

    "_mknodat", referenced from:
          _do_mknod_atfd in syscall.o
    ld: symbol(s) not found for architecture x86_64

Reported on macOS 10.13.6 x86_64, where config.h carries
/* #undef HAVE_MKNODAT */ next to #define HAVE_MKNOD 1.  The sibling
do_mknod_at() already keys off HAVE_MKNODAT and its comment names this
exact platform.

Both occurrences change together.  The second guards

    return -1;	/* mknodat()'s errno (regular/device node) */

against an ENOSYS fallback, so leaving it on HAVE_MKNOD would report
"mknodat()'s errno" on a build where the call was never compiled -- a
stale errno from whatever ran last.

Checked by rewriting config.h the way that platform has it and compiling
syscall.c: the parent leaves one unresolved mknodat reference, this
leaves none.  A FIFO still goes to mkfifoat() where that exists, and a
socket still returns EOPNOTSUPP; only the regular/device-node path
becomes ENOSYS, which is what a platform without mknodat() can offer.
2026-08-01 19:19:42 +10:00
Filipe Casal 41f411d6e9 rrsync: pin receiver option directories the peer can pivot
A checked receiver-side directory option whose leaf does not exist can
be created as attacker-controlled transfer content and then consumed by
the same transfer: --backup-dir=a, with "a" arriving in-band as a
symlink pointing out of the restricted directory.  The same shape works
against --copy-dest, where it reads an outside file and delivers it to
the client.  Passing the leaf under the parent's /proc/self/fd pin is
not enough: the peer's symlink wins the race to the name, and rsync
creates or reads through it.

The answer is to hand rsync an inode rather than a name -- but what
inode depends on what rsync does with the option, so the policy is per
option rather than per type ("type 2" means "check when receiving", not
"is a directory"):

  --backup-dir, --partial-dir   rsync creates them on demand.  rrsync
                                creates them instead, walking down from
                                the restricted dir one component at a
                                time with O_NOFOLLOW (nested names too:
                                make_bak_dir() builds a hierarchy), and
                                pins the result.  The partial dir is
                                made 0700, as rsync makes it.
  --temp-dir                    rsync requires it to exist, so a missing
                                one stays an error.
  --link-dest, --compare-dest,  rsync only reads through these, and a
  --copy-dest                   missing one is the ordinary first-run
                                case that must keep working.  rrsync
                                pins an empty directory it then unlinks:
                                the transfer behaves as with a missing
                                one, and there is no name left for the
                                peer to take over.  Not quite identical:
                                rsync prints "--link-dest arg does not
                                exist" for a genuinely missing basis and
                                the placeholder suppresses that.

If the transfer later replaces a created name, the held inode is merely
detached -- the backup fails, it does not escape.

Without /proc/self/fd there is no way to name an inode, so on those
platforms every missing type-2 option path is refused instead -- the six
above, not the type-3 paths covered at the end.
That is the same fail-closed behaviour this change originally had
everywhere; the pinning is what buys back first use where it can.

An earlier version refused every missing type-2 leaf on every platform.
That closed the pivot but broke first-use --backup-dir and --partial-dir
-- including --partial-dir=.rsync-partial, the documented resumable-
upload idiom -- and would have broken first-run --link-dest, which is
how every rotating-snapshot script starts.

The regressions run their security assertions BEFORE their controls, so
an environment where a control fails for an unrelated reason cannot mask
the escape check by aborting first -- which is exactly what happened to
one reviewer.  The alt-dest one also has to beat a race: the generator
runs ahead of the receiver, so sorting the pivot symlink first does not
guarantee it is installed before the basis lookup, and a run where the
generator won would pass vacuously.  A few thousand files in between
give the receiver the head start, and the test asserts the symlink was
really installed rather than trusting the ordering.  Measured 5/5 RED on
the parent, 3/3 GREEN here.

The regressions cover each branch of the policy: the backup-dir pivot,
the --copy-dest read escape (its own test), first-use --backup-dir,
nested first-use --backup-dir, first-use --partial-dir including its
mode, a refused missing --temp-dir, an accepted first-run --link-dest
with no placeholder left behind, and an existing basis still working.
Each requires the specific mechanism rather than just "the outside file
was left alone", which any unrelated failure would satisfy, and each
takes the refusal branch where the pin primitive is unavailable.

Behaviour worth knowing about, since rrsync now creates these rather
than rsync: they are created while the ARGUMENTS are parsed, so they
appear even under --dry-run, where rsync's own make_path() deliberately
does not mkdir; and one is left behind if the transfer then fails.  A
pinned --partial-dir is also one directory rather than one per
destination directory, and rsync will not auto-remove it -- which is not
new, rrsync already rewrote an existing relative partial dir to its pin.

A nested --partial-dir now behaves differently from plain rsync, which
documents creating "just the last directory -- not the whole path".  With
--partial-dir=a/b and "a" missing, rsync creates nothing and rrsync
creates both.  Containment is unaffected -- each component is made
beneath the fd already held, O_NOFOLLOW -- but rrsync accepts a shape
rsync would not honour, and since rsync only removes the last component
of a relative partial dir, an empty parent is left behind.

Not fixed here, and NOT claimed to be: this covers the six type-2
directory options.  --files-from, --log-file and receiver positional
paths are type 3; where the leaf and its parent are both missing, or on
a platform without /proc/self/fd, those still reach rsync unpinned, as
SECURITY.md describes.  That is the rest of the issue, not this one.

The wrapper is handed a shim rather than RSYNC directly: RSYNC is a
multi-word command whenever the runner forces --protocol=N, and rrsync
execlp()s its RSYNC as a single executable name, so this test died
before reaching the policy under test.  A fleet run caught it on the
protocol columns of three targets.
2026-08-01 12:16:40 +10:00
Andrew Tridgell 95e7f04148 runtests: let a skip spec remove a name a composed list added
The expected-skip lists are now files referenced as @FILE and composed
(common + platform + protocol), and they are expanded here, on the
target, against the tree that shipped.  That is deliberate, but it means
nobody upstream of this point can SUBTRACT: the name lives inside a file
the composer does not read.

fleettest needs exactly that.  A target may run the same build against a
different filesystem, and then tests its platform list expects to skip
genuinely run -- a scratch dir on a second volume makes backup-crossdev
-copy and chmod-temp-dir work.  With additions only, such a target can
never be green.

Accept a '-name' entry, applied after every addition so order does not
matter.  A test name never begins with '-', so the token is unambiguous.

Every removal must remove something: a name nothing added is stale, and
a repeated removal is that same no-op written twice.  Quietly shrinking
the expected set is the failure this parser exists to prevent, so both
are refused rather than left to sit in a config unnoticed.
2026-08-01 12:12:21 +10:00
Andrew Tridgell 4b4bf80f8d fleettest: never run two targets on one machine at the same time
Targets were all submitted to the pool at once, and the per-run build
directory was named for the run alone -- <builddir>-<run_id>, identical
on every target.  Both assume one target per machine.

Two targets naming the same host break that, and the fleet now has such
a pair: mac2 and mac2-hfs are one Mac, differing only in where the
tests' scratch trees live.  They pushed into the same directory and
built over each other, and BOTH reported BUILD-FAIL -- a failure that
looks exactly like the code under test not compiling.  Either target run
by itself was fine, which is the worst way for this to present.

The build directory now carries the target name too, and a machine's
targets run one after another, with a log line saying so.  Serialising
matters beyond the shared directory: two suites on one host would fight
over the fixed ports the daemon tests claim, and over every other piece
of host-global state, so separate directories alone would not be enough.
Different machines still run concurrently, which is where the
parallelism actually was.

The target name is reduced to [A-Za-z0-9._-] before it goes into a path
that cleanup later feeds to rm -rf, so a name cannot contribute a path
separator, a shell metacharacter or a leading dash.  --cleanup still
globs <builddir>-*, which the longer name matches.
2026-08-01 12:12:21 +10:00
Andrew Tridgell dc72d0409a testsuite: xattr_set must refuse the same way on every platform
make_variety_tree() sets an xattr on every file, including the ones it
deliberately creates read-only, and tolerates a refusal:

    try:
        xattr_set('variety', os.path.basename(str(p)), p)
    except OSError:
        pass

That handler works only on Linux.  There xattr_set() calls os.setxattr()
and a refusal is an OSError; every other platform shells out to a CLI
with check=True and raises CalledProcessError, which is not an OSError
and sails straight past.  So a refusal the suite tolerates on Linux can
kill variety and variety-symlink-traversal on any CLI-backed platform.
macOS, Cygwin, FreeBSD and Solaris all carry the defect; macOS is where
Roland Kletzing hit it, on test8 through test10, on the perm7 file:

    xattr: [Errno 13] Permission denied: '.../d1/d2/.../d7/perm7'
    subprocess.CalledProcessError: ... returned non-zero exit status 1

What triggers it there is a non-root run meeting the mode-0400 files the
tree deliberately creates, which their own owner cannot attach an xattr
to.  That is why the fleet, which runs as root, never saw it.  Root is
not immune to every refusal, just to that one.

Route the four CLI branches through a helper that raises XattrError, an
OSError subclass, so one handler covers every platform.  It carries an
errno only when the tool named one, and only macOS's xattr(1) does:
setfattr and setextattr just say "Permission denied", and guessing an
errno back out of localised strerror text would be worse than admitting
we do not know.  The match is anchored to that tool's own prefix on the
first line -- the rest of the line is a filename, and a file can perfectly
well be called "[Errno 5]".

devices/devices-fake had already worked around this locally by catching
both types; that catch is now dead, so drop it.

Verified by forcing the CLI branch on Linux and running variety non-root:
it fails with Roland's traceback, on the same perm7, and passes with
this.  That establishes the exception path every CLI branch takes, not
macOS's xattr(1) in particular -- his report supplies that half.
2026-08-01 12:12:21 +10:00
Andrew Tridgell 47df88d5fb fleettest: run a target's tests on another filesystem, and add two Macs
runtests.py already honours $scratchbase, but a target could not use it:
the sudo branch runs `sudo -n env PATH="$PATH" ...`, which drops whatever
env_prefix exported.  Setting it there looked like it worked and silently
ran on the default filesystem instead -- the first HFS+ run came back
green for that reason.  Give it a target field carried inside the env
string, on both the root and non-root paths, shell-quoted so a volume
name containing a space does not turn into a stray argument.  The
non-root pass also clears the relocated scratch, which a prior sudo run
leaves root-owned outside builddir.

expect_skip_omit is the mirror of expect_skip_extra: entries the
workflow expects to skip which a target actually RUNS.  Relocating the
scratch supplies conditions the workflow's host lacks -- a separate
volume makes backup-crossdev-copy and chmod-temp-dir reachable -- and
without a way to subtract, such a target can never be green.

mac2-hfs runs the same host and build as mac2 with the scratch on HFS+.
It verifies the mount rather than assuming it: a stale directory, or a
name collision attaching at "RsyncHFS 1", would otherwise leave the
tests on APFS reporting green, which is how the first version lied.
Ownership must be on as well, and is now checked rather than attempted:
a user-attached image mounts "noowners", under which every uid/gid and
permission check is meaningless, and that alone accounted for 28 of the
31 failures the first honest run produced.

mac-x86 is the x86-64 Mac -- the only target that can build the x86-64
md5 assembly, since mac2 is arm64 where configure refuses
--enable-md5-asm outright.  It needs MacPorts for autotools, python3 and
the crypto/hash libs.  --enable-roll-simd is not set and cannot be: that
probe uses GCC-style function multiversioning, which clang does not
support on Mach-O, failing identically under Apple clang 10 and clang 19.
mac-x86 currently BUILD-FAILs on the unguarded mknodat() in
do_mknod_atfd() (#161), which it reproduced on its first run.
2026-08-01 12:12:21 +10:00
Andrew Tridgell ab373ad1d0 testsuite: itemize XFAILs where the filesystem cannot hard-link a symlink
itemize picks between two expectation sets using rsync's own
"hardlink_symlinks" build capability.  That says nothing about the
filesystem underneath: on macOS the build reports true while HFS+ returns
ENOTSUP for link()ing a symlink, and the run dies with

    failed to hard-link .../foo/sym with foo/sym: Operation not supported (45)

Selecting the other expectation set does not help and would assert
something untrue: the itemisation follows the BUILD capability, so rsync
still prints "foo/sym is uptodate" and ".L foo/sym -> ..." even though
the link failed.  Neither set describes that combination.

It is an rsync gap rather than a test one.  generator.c reports the
runtime linkat() failure as FERROR_XFER and the transfer exits 23, after
which rsync creates the symlink anyway -- while a regular file in the
same position falls back to a local copy, and so does a build compiled
WITHOUT symlink-hardlink support.  Falling back on ENOTSUP would make
this pass by itself.

So XFAIL rather than skip: the failure stays visible and flips back to a
pass once rsync falls back.  XFAILing the whole test is blunter than the
one --link-dest case deserves, but the symlink expectations are threaded
through every assertion here rather than confined to one.

The probe answers only the question it is asked: a link() refused for any
other reason -- EPERM, ENOSPC, EMLINK, a quota -- propagates instead of
being reported as a capability difference and quietly reshaping the
expectations.
2026-08-01 12:12:21 +10:00
Andrew Tridgell de38616b25 testsuite: detect an operator-path escape without sub-second mtimes
operator-path-temp-dir and operator-path-partial-dir decided whether a
symlink had been followed by sampling the target directory's
st_mtime_ns, sleeping 10ms, and looking for a change.  The temp file is
renamed away, so an mtime bump was the only trace left.

On a filesystem whose timestamps have 1-second granularity -- HFS+, and
it is not alone -- a change within the same second is invisible.  The
delta is zero, the test concludes the symlink was not followed, and
reports the operator's OWN euid-owned symlink as refused when it was
followed correctly.  Both fail that way on HFS+ while passing on APFS,
and operator-path-partial-dir is one of the failures Roland Kletzing
reported on macOS.

Pin the directory's mtime to a fixed past epoch instead, read back what
the filesystem actually stored, and ask afterwards whether it still
holds -- reading back because a filesystem may clamp or round the value,
and comparing against the requested epoch would then read an unfollowed
symlink as followed.  temp-dir-symlink-injection already works this way.

This is not proof against every clock: a directory whose mtime lands
exactly on the stored sentinel would still read as unfollowed.  That
needs the host clock set to 2001 or a deliberate restore, where the old
10ms delta failed on any coarse-granularity filesystem.

Verified in both directions by running as root, where the matrix also
exercises the cross-uid cells: a followed symlink moves the mtime off
the sentinel, a refused one leaves it.
2026-08-01 12:12:21 +10:00
fcasal c933f6227d backup: preserve backup-dir while creating it 2026-08-01 10:43:58 +10:00
Andrew Tridgell dcc3c9c51a socket: refuse a meaning-changing first character, and stop refusing aliases
The %H allow-list was wrong in both directions.

Several accepted characters change an argument's MEANING rather than its
text when they lead the value, which quoting cannot prevent because the
word stays intact -- that IS the problem:

  '-' and '+' introduce options to plenty of programs; with
      RSYNC_CONNECT_PROG="prog %H", hosts "-c" and "+x" arrive as
      options, and "sh +x" is as real as "sh -x";
  '~' is tilde-expanded by the nested shell, turning ~root into /root;
  '%' is expanded by a nested fish, where %self becomes its pid.

An empty host has the same shape from the other end: it survives a direct
exec as an empty argument but disappears when a nested shell re-splits the
command, shifting everything after it.  rsync://:873/m/ and ::m/ both
produce one.  None of these can begin a real hostname, so refuse them in
first position only -- mid-word each is literal, which matters because an
IPv6 zone id carries its '%' mid-word.

The other direction: '+' and '~' were refused outright.  They execute
nothing, and RSYNC_CONNECT_PROG exists for custom transports where %H is
often an alias the program resolves itself rather than a name the
resolver sees.  Refusing them mid-word breaks that use case for no gain.

A non-ASCII host stays refused.  That is a policy choice rather than a
free one: a custom connect program never calls getaddrinfo, so a Unicode
alias would otherwise work, and this does exclude it.  A punycode A-label
is unaffected.

What this cannot do is bound what the named program makes of the value.
"host:-rf" arrives intact, and a program that splits on ':' may
reinterpret the tail; that boundary belongs to whoever writes the
command.

The test asserted only that a marker file was absent -- equally true when
rsync failed to parse its arguments, when socketpair_tcp is blocked, or
when touch was missing.  Worse, the marker path was absolute, and a URL
authority ends at the first '/', so the injected `touch` never received
an operand and the check could not fail even with the guard gone.  It now
runs with cwd set to the scratch directory and injects a bare name, so
the marker is genuinely reachable; requires the specific refusal message;
checks the exit status; and checks that ordinary hosts still arrive at
the connect program with their text intact, which is the part an
absence-only test can never show.

Fault-injected separately: dropping the guard, dropping just the
first-character check, and narrowing the set back each fail the test on
their own, in both the default and --use-tcp transports.
2026-08-01 10:00:05 +10:00
Filipe Casal 78e10e7e32 socket: reject shell-active connect hosts 2026-08-01 10:00:05 +10:00
Andrew Tridgell f704aa4aed daemon: refuse '!', '~' and braces in a hook expansion too
The refused set was built from the characters that obviously execute
something, and missed three that a SECOND shell acts on:

  '!' negates in command position.  A hook written as an access check --
      `pre-xfer exec = sh -c '%RSYNC_USER_NAME% false'` -- becomes
      `! false`, reports success, and serves the transfer.  An
      authenticated user named "!" turns a denial into an approval, which
      is precisely the case the fail-closed comment above exists for.
  '~' is tilde-expanded, so ~root becomes /root.
  '{' and '}' brace-expand in bash and zsh.

None of them execute anything on their own, which is how a set built from
the obvious metacharacters came to miss them.  That is also the standing
weakness of the approach: this is a deny-list, and the two rounds of
review it took to find '!' are the argument for eventually inverting it.

The documentation is corrected with it -- it claimed every shell-active
character was refused, which this disproves -- and now lists the set.
Each listed character is pinned by the test, which needed its module
paths to EXIST first: a missing path fails the transfer on its own, so
checking the exit status alone passed whether or not the character was
refused.  Removing any single character from the set now fails the test.
2026-08-01 09:52:27 +10:00
Andrew Tridgell 8da62816c8 testsuite: make the nested-shell test fail for the right reason
It asserted that a marker was absent and that rsync exited non-zero.  Both
are equally true when authentication failed, when the daemon never
started, or when the globbed command was missing -- so it passed for any
of those, with or without the guard.  Changing only the password to a
wrong value left it green.

Require the daemon log to carry the specific refusal, so a transfer
stopped for some other reason no longer reads as the value having been
refused.

Add a positive control.  The test now runs the same nested-shell
expansion itself first and requires it to work; without that, a pass
could equally mean the attack was inert here and rsync was never tested
against a live one.

Stop globbing onto /usr/bin/touch.  The command the expansion selects is
now one the test writes into its own directory: the build never
guaranteed a system touch, and with the old oracle a missing one produced
a pass.  Exactly one file there matches "touc?", so the expansion is
unambiguous.

The docstring described the value becoming "find arguments", which is not
what the hook does -- it selects /usr/bin/touc? and glob-expands it to a
command.  Say what actually happens.

Checked in both directions: the wrong-password mutation that used to pass
now fails, narrowing the refused set back to the pre-existing one fails,
and the unmodified test passes under both the default transport and
--use-tcp.
2026-08-01 09:52:27 +10:00
Andrew Tridgell 5bf9940de4 rsyncd.conf: document that the hook metacharacter refusal covers your own values
Refusing a shell-active %VAR% in an exec hook is a real usability cost
and it is not confined to hostile input: the check runs on every
%RSYNC_*% value, so a module whose path holds a space cannot be
interpolated into a hook at all.  `path = /srv/My Backups` with a command
mentioning %RSYNC_MODULE_PATH% refuses every transfer of that module,
with no attacker involved.

That is deliberate rather than an oversight.  rsync escapes a
substitution for the quoting context it sits in, which is right for the
one shell that runs the command and wrong for a command that starts a
second one -- `sh -c '... %RSYNC_USER_NAME% ...'` hands the inner shell a
bare value, where "touc?" is glob-expanded against /usr/bin and chooses
the command rather than being data for it.  Escaping for an unknown
number of passes is not possible, so the value is refused instead.  Nor
can the check exempt operator-supplied values: `path` may itself be
templated from a peer one (`path = /home/%RSYNC_USER_NAME%`), and once
both are inside the same string rsync cannot tell them apart.

Say so in the manual, and give the way out: the same names are exported
to the command, so $RSYNC_MODULE_PATH inside a script is unrestricted.

The test pins both halves, since a documented behaviour with no oracle
drifts.  Both are fault-injected: narrowing the refused set back makes
the interpolated half pass when the manual says it must not, and dropping
the RSYNC_MODULE_PATH export fails the workaround half.
2026-08-01 09:52:27 +10:00
Filipe Casal 00160f76b8 daemon: reject shell-active hook expansions 2026-08-01 09:52:27 +10:00
Andrew Tridgell 1eab6f43e8 testsuite: cover the skip-list encoding guard
An invalidly encoded list must exit 2 like any other unreadable one, but no
case created invalid bytes, so dropping UnicodeDecodeError from the except
would have left the test green (failing closed with a traceback instead).
2026-07-31 21:02:55 +10:00
Andrew Tridgell c1df37d954 testsuite: prove each skip-list guard independently
Follow-up review found three guards that the test observed only indirectly:
removing the one-name-per-line check, or relaxing the regular-file check back
to exists(), or dropping a separator check still produced a hard error via the
"no such test" path, so the test could not tell them apart.  Each now runs
against a stand-in suite directory containing fixtures that make the malformed
name look real ("acls sparse_test.py", a directory named adir_test.py), so the
guard under test is the only thing that can reject it.  Verified by mutation:
each guard removed in turn makes the test fail on its own case.

Also: reject a comma in a name (it is the separator of the csv this returns
and of the summary line the fleet parses back); catch UnicodeDecodeError
alongside OSError so a mis-encoded list exits 2 rather than tracebacking; and
exercise a non-trivial relative srcdir, since in-tree os.path.relpath() is
just "." and would not have caught prefix doubling.

Two comments overstated things: a truncated list does not silently weaken the
oracle -- the comparison is exact, so it surfaces as a page of unexpected
skips.  Rejecting it is about failing where the mistake is, not about the
oracle going blind.  And extras merge into the passes the workflow pins, not
into every pass.
2026-07-31 21:02:55 +10:00
Andrew Tridgell 93e0d01798 testsuite: fail closed on every malformed skip-list spec
Review of the previous commit found four ways the parser or its test fell
short of what that commit claimed:

  - An empty or comment-only list, and an empty entry within a spec (`a,,b`,
    which is what an unset shell variable expands to), both expanded quietly
    to a smaller expected set.  A shrunken expectation is a weaker oracle, so
    these are hard errors now; a wholly empty spec remains the legitimate
    "expect no skips".
  - Name validation accepted a path, so `../testsuite/acls` passed as a test
    name.  Names must be plain and resolve to a regular file.
  - skiplist-spec_test.py built @FILE paths from a relative srcdir and handed
    them back to a srcdir-relative API, which doubled the prefix -- it would
    have failed under `make installcheck` (--srcdir=../src).  It also proved
    the sort check with a name that does not exist, so deleting that check was
    masked by the stale-name check; it now uses two real tests out of order,
    and covers the cases above.  Each guard verified by mutation.
  - fleettest returned an extras-only expected set for a pass whose workflow
    has no matching step (a non-Linux target with protocols=[29]), a
    guaranteed mismatch.  An unpinned lane is now simply unpinned.

Also restores the fleettest CI path filter that the previous commit dropped:
that job must run when runtests.py or a skip list changes.
2026-07-31 21:02:55 +10:00
Andrew Tridgell 2f9bbe835c testsuite: keep the expected-skip lists in files, not in the workflow line
Every branch that added a test which skips somewhere had to edit
RSYNC_EXPECT_SKIPPED, a single ~3 KB YAML line duplicated across seven
workflow steps -- so two such branches always conflicted, and the conflict
was in the one format git cannot merge.

The lists move to testsuite/skiplist/*.txt, one name per line with the reason
as a comment, and RSYNC_EXPECT_SKIPPED takes @FILE entries which runtests.py
expands (relative to srcdir, so out-of-tree builds work).  Several compose,
which lets the 46 names common to Linux/macOS/Cygwin live in one file: adding
a require_tcp test now edits one line of common.txt instead of three lists in
three files.

Lists must be sorted, duplicate-free, and name real tests, and an unreadable
or malformed list is a hard error -- it must never degrade to "expect no
skips", which would silently disarm the oracle on that job.  fleettest passes
the spec through to the remote runtests.py, which expands it against the tree
that was staged there.

The oracle itself is unchanged.  Verified on Linux by running the full suite
in all three lanes (check, check30, check29) against the new files: same
expected sets, all green.
2026-07-31 21:02:55 +10:00
Andrew Tridgell 9022a0a5bc ci: enforce the protocol-29 skips in the check29 oracle
The check29 steps reused the plain check list, so six tests that skip only
under --protocol=29 were unaccounted for and the run failed its expected-skip
comparison.  They gate on the wire version rather than the platform: ACL and
xattr transfers need protocol 30+, and the stdio_daemon helper speaks 30.

Verified by running the full suite at both protocols on Linux: the 29 skip set
is the default set plus exactly these six.  acl-symlink-race already carried a
comment saying its protocol gate had to be represented here.
2026-07-31 21:02:55 +10:00
Filipe Casal c3abc6c095 rrsync: protect no-overwrite auxiliary paths
--ignore-existing protects the live transfer destination, but three
peer-selectable options reach other existing objects inside the
restricted directory: --log-file appends to one, --partial-dir consumes
and then renames or unlinks one, and --delay-updates does the same
through its implicit .~tmp~ directory.  Refuse all three under
-no-overwrite.

Refusal rather than confinement, because confining these paths does not
help: keeping the --log-file append inside the tree still appends to an
existing file, and for the partial directories the peer controls both
the directory and the transferred basename, so a collision is always
reachable.  A pre-exec emptiness check would be raceable.  The cost is
that a push naming --partial-dir on the remote receiver, or using
--delay-updates, is now refused for a -no-overwrite account.

Each regression drives the option through a wrapper WITHOUT
-no-overwrite as well, and requires that to be accepted.  Without that
control the tests cannot tell "refused under -no-overwrite" from
"refused always", and would still pass if the options were disabled for
every rrsync deployment -- verified by making the refusal unconditional,
which the controls then catch.

The wrapper is handed a shim rather than RSYNC directly: RSYNC is a
multi-word command whenever the runner forces --protocol=N, and rrsync
execlp()s its RSYNC as a single executable name, so every one of these
tests died before reaching the policy under test.  A fleet run caught it
on the protocol columns of three targets.

Backup mode belongs in the same set.  Publishing a backup onto a name
that already exists deletes what is there (backup.c make_backup()), and
deleting a file backs it up first (delete.c), so a --delete of an
unrelated file can land on a protected name -- overwriting a file that
--ignore-existing was holding, with rc=0 and no diagnostic.  Disable -b
and --backup-dir.  --suffix is left enabled: with both of those refused
nothing can turn backups on, so it is inert.

The short option must be disabled before short_no_arg_re is built, since
a stock client sends b inside the remote short-option bundle and the
regex is snapshotted there; the whole -no-overwrite block therefore
moves up beside the other policy gates rather than sitting after the
chdir.  Its regression drives the collision through a wrapper without
-no-overwrite, passing --ignore-existing by hand, which both proves the
refusal is conditional and shows the primitive defeating the very
protection -no-overwrite forces.

rrsync.1.md now states what -no-overwrite costs: no explicit
--partial-dir, no --delay-updates, no server-side --log-file, no backups.
2026-07-31 13:18:39 +10:00
Filipe Casal 576ce422fe rrsync: keep remote files-from out of write-only mode
Write-only mode rejects ordinary downloads, but a remote --files-from
makes the receiving child open a server-side file and send it back over
the upload protocol.  Reject server-local --files-from paths under -wo,
keeping the exact "-" sentinel that a client-local files-from upload
sends.

The control now uploads a second, unlisted source file and requires it
NOT to arrive.  With a single file present an ordinary recursive upload
looks identical, so the control passed whether or not the list selected
anything.

The wrapper is handed a shim rather than RSYNC directly: RSYNC is a
multi-word command whenever the runner forces --protocol=N, and rrsync
execlp()s its RSYNC as a single executable name, so this test died
before reaching the policy under test.  A fleet run caught it on the
protocol columns of three targets.

Known gap: this closes the argv route only.  A per-directory merge
filter delivered over the protocol still reads a file outside the
restricted directory and returns its content in an "Unknown filter rule"
error -- reproduced with a stock client against -wo.  rsync's existing
filter-file confinement does not apply because it is gated on am_daemon
and an rrsync server is not a daemon.  Tracked separately.
2026-07-31 12:22:20 +10:00
Filipe Casal 06212f112a daemon: cap peer-selected Zstandard workers
A daemon parses the peer-supplied server argv, so a client naming a large
--compress-threads on a pull makes the daemon-side sender materialize
that many Zstandard workers: 256 was measured as 257 threads in one
connection.  No custom client is needed -- a stock rsync forwards it with
-M--compress-threads=N -- and on an anonymous module no authentication
happens first.  Clamp it to 8 on a daemon; local and remote-shell
invocations keep the operator-requested value.

A push parses and clamps it too, but creates no workers there: the
option affects compression, not decompression.

The test asserts the implementation's own bound, 8 workers plus the main
thread, rather than a looser threshold that a build unable to create
workers at all would also satisfy -- so it first requires a worker pool
to be reachable and skips if it is not, then requires it to be bounded.
It needs --use-tcp and is declared in the workflows that enforce a skip
set.

Whether the platform can be counted at all is asked once, before the
answer is folded into a max(): thread_count() returns -1 where it has no
way to look, and max(0, -1) is 0, so the -1 could never reach the check
meant to catch it and an uncountable host looked instead like a sender
that died.  A fleet run had Cygwin failing for exactly that reason.

Left deliberately open: the cap is silent, is not expressible in
rsyncd.conf, and does not bound the total across connections, since
max connections defaults to unlimited.
2026-07-30 16:45:26 +10:00
Filipe Casal c529163ef0 options: refuse aliases for exact option rules
parse_one_refuse_match() marked only the first long_options row whose
long name matched the configured spelling, then broke out for a
non-wildcard rule.  --compress-threads and --zt are separate popt rows
that both write &do_compression_threads, so "refuse options =
compress-threads" disabled the canonical row and left the alias
accepted: the refused capability was still reachable under its other
name.  The same shape covers zc/compress-choice and zl/compress-level.

An exact rule names a capability, not one spelling of it, so mark every
row that does the same thing.  Comparing the raw table fields is not
enough for that: popt's `val` means different things per argInfo.  For
POPT_ARG_VAL it IS the value stored in `arg`, while elsewhere a nonzero
`val` is an action code for the parser's switch, and POPT_ARG_NONE with
a destination stores 1 whatever `val` says.  --del is
POPT_ARG_NONE/&delete_during/0 and --delete-during is
POPT_ARG_VAL/&delete_during/1: the same destination and the same
resulting value, but unequal as table entries, so "refuse options =
delete-during" was still evaded by --del and the mirror held too.

Compare what a row does instead -- the destination and the constant it
assigns, falling back to table-entry equality for rows that store a
runtime value or only dispatch an action.  Enumerating all 258 rows,
this couples exactly one pair the field comparison missed, del and
delete-during, and changes nothing else.  Opposite switches such as
--foo and --no-foo stay distinct because they assign different values.

Two regressions.  The compress-threads one drives the raw daemon
protocol -- not to preserve the spelling, which -M--zt=N would do just as
well, but because it goes on to observe the worker pool the bypass
delivers.  Its oracle is the refusal itself -- the alias connection torn
down and
"configured to refuse --zt" logged -- and deliberately not the resulting
worker count: an accepted --zt is a defeated refuse rule however few
threads it produces, and the daemon worker cap being added alongside
this holds that count to 9, so a count-based assertion passes while the
alias is still accepted.  Run that test against the cap without this
parser fix and it does exactly that; the two changes were covering for
each other.

The delete one needs neither zstd nor a socket: --remote-option puts the
option in the daemon's argv verbatim, which is the reach an ordinary
user already has, so it drives a stock client both ways round against
modules refusing each spelling, with an unrefused module as the control.

The compress-threads test needs --use-tcp, so it skips in every other
column and is declared in the workflows that enforce a skip set, which a
fleet run otherwise reports as an unexpected skip on fourteen cells.
2026-07-30 06:36:12 +10:00
Andrew Tridgell 33257bdcaa testsuite: parse the generator's request instead of pattern-matching it
malicious-server-partial-basis-symlink-overwrite waits for the client's
generator to ask for index 1 before sending its forged basis-type
response.  It waited by searching the raw stream for one hard-coded
three-byte marker: the ndx delta, then exactly
ITEM_TRANSFER|ITEM_IS_NEW.

The generator is entitled to send more than that.  When it has already
chosen an alternate basis it also sets ITEM_BASIS_TYPE_FOLLOWS and
appends a basis-type byte, which it does once it has noticed the
partial-dir file this test plants.  Whether it does varies between
environments -- consistently not on this Linux box, consistently so on
the OpenBSD VM.  I have not identified what differs; the planted file
and the victim both exist before rsync starts and the malicious sender
does not touch them, so calling it a race would be a guess.

Where the extra flag appears the marker never matched, and the test
timed out after ten seconds reporting "generator did not request file
index 1" -- which was untrue.  The captured bytes decode as the
auto-added perishable filter rule "-p .rsync-partial/", the filter-list
terminator, then ndx 1 with flags 0xa800 and the byte 0x81,
FNAMECMP_PARTIAL_DIR: the very constant this test defines.  It failed in
five of six fleet runs and reproduced on an isolated single-target run,
so it was not load-related flakiness.

Read the request properly instead: consume the filter list, then the
index, the item flags, and the optional basis-type and xname fields,
using the framing rather than hunting for a byte pattern that can also
occur inside file data.

Both outcomes are worth driving, and they prove different things:

  * without the flag the response SUBSTITUTES a partial basis the
    generator never asked for -- the unbound-basis-type bug;
  * with it the generator asked for that basis itself, so the response
    substitutes nothing and the test proves the receiver CONFINES a
    basis it did request.

The comment claimed the first unconditionally, which was wrong wherever
the second happens.  Any other basis type now fails as a fixture change.

Failing the old way was safe -- the forged response was never sent, so
no false pass was possible -- but it cost a red cell on the fleet and
hid whatever else that target had to report.
2026-07-29 19:58:40 +10:00
Andrew Tridgell 2fbb708f15 rrsync: deny device/special creation where creation happens
A restricted dir must not let a client have the spawned rsync create
devices or special files in the served tree, but -a bundles -D into the
client's short options, so refusing -D outright breaks every ordinary
`rsync -a`.  88cee089 forced --no-D instead.  That option also clears
the rdev framing, and rrsync sets it on one end only, so the file list
desynchronised: a FIFO push hung at protocol 29 and corrupted the list
at 30, and a device push failed at every protocol including 32.  Use
--drop-D, which withholds the creation without touching the wire.

Only on the receiving side.  A sender creates no received device or
special entry in the served tree, so there is nothing to deny and
--drop-D is a no-op there; forcing --no-D on a pull was the same
one-sided change in the other direction, and broke pulls the same way.
3.4.4 forced nothing at all and is the behaviour a pull now gets back.

rrsync-specials-denied asserted the option on a "--server --sender"
command line, which conflated the two directions.  It now checks that
the receiving side forces --drop-D and still forwards the client's own
-D -- without which the two ends frame the list differently again --
that the sending side forces neither, and, rather than only what is
forwarded, that a real push cannot create a FIFO while the rest of the
transfer succeeds.  An ordinary file alongside is the control, since a
push that failed outright would "deny" the FIFO too.

The device case gets its own push, because a device desynchronises at
every protocol while a FIFO only does so below 31.  It needs no mknod
privilege: rsync's fake-super "%stat" xattr is what makes a file a
device to rsync, and running the RECEIVER under --fake-super too lets it
record one without privilege -- so the case asserts that nothing of that
name appears, not merely that the transfer survived.

rrsync-pull-arg-shapes could previously assert only that pulling a FIFO
did not hang, because forcing the option on that side broke the transfer
outright.  It now requires the pull to succeed and deliver a FIFO, which
is what a pristine 3.4.4 rrsync does.
2026-07-29 19:58:40 +10:00
Andrew Tridgell b607369f5f rsync: add --drop-D, refusing device/special creation only
-D and --no-D do two jobs at once: they decide whether devices and
special files are created, and they decide whether those entries carry
their rdev fields on the wire.  send_file_entry() and recv_file_entry()
frame those fields with the same condition, but each end evaluates its
own preserve_devices/preserve_specials, so the two only agree because
both normally parse the same command line.

That makes --no-D unusable for a wrapper that controls one end of a
connection and wants to deny creation.  Give it to the receiver alone
and the client's -D sender writes rdev the receiver never reads: the
file list desynchronises from that entry on.  A FIFO or socket breaks
below protocol 31 -- a hang at 29, "File-list index 0 not in 0 - -1" at
30 -- and a device node breaks at EVERY protocol, current ones included,
because its arm of the condition has no protocol clause at all.

--drop-D separates the two jobs: it refuses the creation and leaves the
encoding alone.  The entry is skipped through the existing non-regular
fall-through, so the visible result matches --no-D, and because it
touches no wire state it can be applied to one end by itself.

It has no effect on a sending rsync, which creates nothing.
2026-07-29 19:58:40 +10:00
Andrew Tridgell 1c0bd88f0b rrsync: don't content-open a sender leaf rsync will never open
Two shapes a pristine 3.4.4 rrsync transfers, and 88cee089 broke, both
from one cause: the pin opens the argument's CONTENT, when for a sender
rsync often only needs to name or describe it.

  * an in-tree FIFO wedged rrsync before exec.  O_RDONLY on a FIFO blocks
    until a writer appears, so an authorised user naming one could
    accumulate stuck processes indefinitely.
  * an in-tree dangling symlink failed the transfer.  realpath() resolved
    it to a missing target and the ENOENT was reported as a detected
    race, though a dangling link is an ordinary archive entry that rsync
    transmits by its target string without opening anything.

So only a regular file or a directory gets its content opened; anything
else keeps the realpath()-validated name.  The sender never opens these,
it only describes them.

The leaf is still spelled beneath a pinned directory.  An
earlier form of this commit left the bare name for rsync to re-resolve,
on the reasoning that 3.4.4 passes it that way -- but that puts every
component back in play and reintroduces CVE-2026-53783 for the shape:
with an in-tree "dir/target" that is a dangling symlink, flipping "dir"
to a symlink pointing outside leaked the outside file's content in 3 of
83 raced pulls.  With the parent pinned it is 0 in 104 -- but a race only
samples the window, and zero in 104 still leaves a few per cent of
per-attempt risk unmeasured, so rrsync-sender-parent-pin closes it
deterministically instead: a stub standing in for rsync inherits the
pinned descriptor and blocks, the parent is swapped for a symlink out of
the tree while it is blocked, and only then does the stub resolve the
argument.  It reports the in-tree leaf with the pin and the attacker's
file without it, so it fails outright if the pin is removed rather than
depending on winning anything.  A control first proves the swap really
does redirect the bare name, or the assertions would prove nothing.

Pinning the
parent costs nothing here -- pin_dir() opens it O_PATH, so the special
file itself is still never opened and a FIFO still cannot block, and
whatever the leaf becomes afterwards is reached only from beneath the
held one.

Which directory that is, sender_pinned_arg() already decides, and for
every shape except one it is the immediate parent.  The exception is a
--relative argument with no client "/./": there the whole argument is
the transmitted name, so only the anchor it starts from can be pinned
and the components below it stay raceable.  That limit predates this
commit and NEWS states it; "the parent is pinned" is not true of that
one shape.

Two boundaries this must NOT cross, each found the hard way:

  * a trailing "/" or "/." argument keeps its leaf pin: rsync opens that
    one and does follow a symlink there.  Declining it made
    rrsync-sender-leaf-flip leak the outside directory's content.
  * the decision is not gated on HAVE_PROC_SELF_FD.  It is about what
    rsync does with the argument, not about whether we can pin it, so
    gating it left the dangling-symlink failure in place on the BSDs,
    macOS, Solaris and Cygwin.

The shape matrix grows fifo, dangling-symlink and symlink-to-file cases,
and now asserts what each delivered entry IS -- kind, symlink target and
content -- on every case rather than spot-checking a couple at the end.
A name-only comparison is satisfied by an empty directory called "f1",
or by the correctly-named but empty symlinks that handing the sender a
magic link produced.  It still passes against a pristine 3.4.4 rrsync.

The FIFO case asserts only that the pull does not hang.  What a special
file does on the wire is decided by the --no-D that a restricted dir
forces on the remote side alone: the sender then omits the old-protocol
rdev fields that the client's own -D receiver still reads, so protocol
29 and 30 fail regardless of this change.  Verified by running the FIFO
case under fakeroot at protocol 29 with and without the parent pin --
it hangs identically either way, so the pinned name is not the cause.
That asymmetry is a pre-existing rrsync bug and is tracked separately.
2026-07-29 19:58:40 +10:00
Andrew Tridgell 05dabb0fdb NEWS: state what the rrsync pin actually covers
The CVE-2026-53783 entry claimed rrsync "inode-pins each validated
component and exec's against the pinned fd".  It pins the path and roots
the argument there, which is not the same thing for a sender argument,
and the primitive is Linux-only -- elsewhere rrsync keeps the
realpath()-validated name, as it always did.
2026-07-29 19:58:40 +10:00
Andrew Tridgell ce0f6d5a25 rrsync: only claim the inode pin where the kernel actually provides it
The HAVE_PROC_SELF_FD probe checked that readlink of a DIRECTORY's entry
returned the right path, which is not evidence of an inode pin, and two
platforms fail that assumption in opposite directions:

  * NetBSD makes the entry a symlink for directories only -- readlink of
    a regular file's entry fails with EINVAL -- so the probe passed and
    then every pull of a file died in the post-pin check with
    "post-pin readlink failed (race?): f1 Invalid argument".  This is
    not new: the same failure reproduces on the branch base.
  * Cygwin's readlink returns the right path, but opening the magic link
    RE-RESOLVES it.  Renaming a directory out from under a held fd lets
    the magic link reach the replacement, so the pin protected nothing
    while appearing to.  rrsync-sender-leaf-flip caught this as a real
    outside-content leak, not as flakiness.

Only Linux provides the inode-bound magic link this depends on, so
require that explicitly and keep the runtime probes as a guard for
Linux-like environments where /proc is absent or restricted.  Elsewhere
rrsync falls through to the unpinned path, as it already did on the BSDs
and macOS.  proc_self_fd_pins() mirrors the rule so the race tests skip
rather than report the intended gap, and Cygwin's workflow expects both
of them to skip -- rrsync-symlink only ran there because the old probe
wrongly reported support.
2026-07-29 19:58:40 +10:00
Andrew TridgellandLeonid Bugaev 6edb7dea2a rrsync: pin a sender argument where rsync will actually resolve it
88cee089 rewrote every validated argument to /proc/self/fd/N so the
spawned rsync re-resolves it to the pinned inode.  That is right for a
receiver, which open()s its destination, but a sender never opens its
source argument: send_file_list() lstat()s it first, and lstat of a
procfs magic link is always S_IFLNK.  So the sender described the
argument as a symlink and sent no data -- silent data loss on
"rsync -a user@host:file dest/", the most ordinary command there is.
Of the argument shapes now covered, only a trailing-slash directory
survived.

Leonid Bugaev reported the regression, diagnosed the lstat-vs-magic-link
mechanism, and supplied the first regression test.

Which pin is usable depends on what rsync does with the argument:

  * a trailing "/" or "/." directory is opened, not lstat()ed, and rsync
    does follow a symlink there, so it keeps the leaf pin -- verified:
    without it a flipped leaf transfers the outside directory's content;
  * anything else pins one level up and passes the leaf by name.  rsync
    will not follow a symlink at that position (it sends the symlink
    itself), -L/-k/--copy-unsafe-links are already disabled for a
    restricted dir, and rsync's own leaf open is O_NOFOLLOW.

The directory pin resolves normally, including a symlink at its last
component, which is legitimate and which 3.4.4 accepts; the readlink
check afterwards is what proves the held inode is in-tree.  It uses
O_PATH because reaching a known name beneath a directory needs only
search permission, and a mode 0111 parent is an ordinary way to publish
a file without letting it be listed.

Under --relative the transmitted name is the whole argument rather than
its basename, so the pin moves up to where that name starts and the rest
is spelled after a /./ marker.  The client's own first marker wins if it
supplied one, including when nothing follows it; -R is parsed out of the
short-option cluster rather than sniffed for the letter, so the trailing
capability blob (-e.iLsfxC) cannot be mistaken for it.

Directory pins are keyed by (st_dev, st_ino), so a glob whose matches
share a parent inherits one descriptor rather than one per argument.

Every shape now delivers what a pristine 3.4.4 delivers, which is what
rrsync-pull-arg-shapes asserts -- it passes against 3.4.4 itself, so the
expectations are that behaviour and not this implementation's.  The
"-R --no-implied-dirs" case is gated on protocol >= 30: at protocol 29
the receiver rejects it with "invalid path from sender" and transfers
nothing, which 3.4.4 does identically.

Moving the sender's pin off the leaf invalidates rrsync-symlink's oracle,
so it is reworked here rather than left failing.  It patches rsync to a
stub that open()s its last argument, which is a faithful model for an
intermediate path component -- whatever rsync does with the final name,
it must not reach it through a flipped parent -- but not for the leaf: a
sender lstat()s its source and transmits a symlink there rather than
reading through it.  So it now flips an intermediate directory, and the
leaf is covered against the real binary by rrsync-sender-leaf-flip, which
races both a plain file argument and a trailing-slash directory and
asserts no outside CONTENT is delivered rather than requiring a symptom
from a race that may not be won on a given run.  Its trailing-slash half
is RED against a pristine 3.4.4 rrsync, which delivers outside/dir/loot.

rrsync-symlink is now sender-only.  Measured over a 5s race, the stub
reached the outside marker 28 times in 98 runs as a sender and 25 in 97
as a receiver against 3.4.4; with the pin it is 0 as a sender but still
7-9 as a receiver, on the branch base as well as here.  That residual is
the receiver's not-yet-existing-destination fallback, which predates this
work and is tracked in #139 rather than folded in.

Clearing FD_CLOEXEC goes through F_SETFD rather than os.set_inheritable(),
which prefers ioctl(FIONCLEX) and gets EBADF from an O_PATH descriptor on
older kernels -- every sender pull on Ubuntu 18.04 aborted with "Bad file
descriptor" the moment a directory pin was taken.

Co-authored-by: Leonid Bugaev <leonsbox@gmail.com>
2026-07-29 19:58:40 +10:00
Andrew Tridgell 8678d89b2c rrsync: pass the --files-from stdin sentinel through unchecked
A pull with a local --files-from does not send the list file to the
server: it sends the literal "--files-from=-" and streams the names down
the protocol connection.  88cee089 started inode-pinning every checked
option value, so rrsync tried to realpath() and open a file named "-" in
the restricted dir and killed the connection:

    post-realpath open failed (race detected): - No such file or directory

Every --files-from pull through a restricted account was broken; 3.4.4
delivers the files.  Exempt the exact string "-" only, so a list file
that really is a pathname is still validated and pinned -- which the new
test's control case checks, using the command shape rrsync actually
accepts so that it reaches the pathname check rather than dying earlier
at the syntax check.
2026-07-29 19:58:40 +10:00
Andrew Tridgell e7986502cb auth: parse "auth users" with conf_strtok so a leading comma means commas only
auth_server() tokenised on commas AND whitespace, ignoring the documented
comma-only form, so an entry containing a space was torn in two: the rule
the administrator wrote never matched, and a rule they never wrote
appeared from its tail.  For "@Group Name:deny" that means the deny is
skipped and a later :rw entry can match instead -- an authorization
bypass for a member of the denied group.

conf_strtok() already implements the documented behaviour and the
daemon's gid field already uses it (clientserver.c); this consumer was
missed when that one was fixed.

Reported by Andres Berbescu.  Refs #137.
2026-07-29 13:10:54 +10:00
Andrew Tridgell a52cb0abc9 testsuite: a leading comma in "auth users" must split on commas alone
Four modules -- two defect cases and a control apiece -- driving the
defect in both directions.

"spaced" needs no groups at all:

    auth users = ,@nosuchgroup authuser:deny, authuser:rw

  * parsed as documented -- one entry naming a group that does not
    exist, so no match, then "authuser:rw" grants access;
  * split on whitespace -- "@nosuchgroup", then "authuser:deny", which
    matches the username and refuses a transfer that should succeed.

"grpdeny" drives the direction that was actually reported, a member of a
denied group getting in:

    auth users = ,@[! []*:deny, <realuser>:rw

"[! []*" is a wildmatch class holding a space, matching any ordinary
group name -- one with no "/" in it, which wildmatch treats as a path
separator -- whose first character is neither a space nor a "[".  So the
entry contains a space without needing an NSS group named with one,
which a test cannot create.  Parsed as documented the deny fires; split
on whitespace it becomes "@[!" and "[]*:deny", both unterminated classes
that match nothing, and the later ":rw" lets the member in.

The "[" is excluded from the class for the sake of that second half: the
simpler "[! ]*" splits to "]*", which matches any name beginning with
"]", so on a host with such a user the buggy parser would deny for the
wrong reason and look correct.

That half authenticates as the invoking user rather than the
secrets-file name, because the daemon must resolve the name to a real
uid or getallgroups() finds nothing and no group rule of any spelling
could match.

Each direction needs its own control, because "refused" is the expected
outcome of grpdeny and almost anything can produce a refusal.  "plain"
proves the synthetic credential and the transfer work; "grpctl"
(auth users = @*:rw) proves the daemon resolved the real user to a uid,
enumerated its groups, accepted its secret and could store the file.
Without them, pointing grpdeny at a nonexistent module or breaking the
real user's secret both made it pass while proving nothing.

The deny itself is checked in the daemon log, not the client's output.
A client is told only "auth failed" whatever the server decided, so
"denied by rule", "no matching rule" and "password mismatch" are
indistinguishable to it -- and a parse yielding no rules at all produces
"no matching rule", which would satisfy any client-side check without
the deny having matched anything.
2026-07-29 13:10:54 +10:00
Andrew Tridgell ec6c8fd932 github: register filter-leak as an expected skip where it cannot run
filter-leak plants a backup-dir symlink owned by another uid, so it
needs root and skips without it.  Cygwin runs the suite as an ordinary
user, so it skips and the workflow says so.

Not AlmaLinux: that job is privileged, so the test runs there -- listing
it made the run fail with "expected-but-ran".  Caught by the fleet, not
by inspection, which is the argument for running it before merging a
skip-list change.
2026-07-29 11:31:57 +10:00
Andrew Tridgell 5eb99bb6b2 exclude: exempt the daemon's own filter parameters from the confinement
Confining every parse_filter_file() open to the module root also caught
"filter", "include from" and "exclude from" from rsyncd.conf.  Those name
operator-configured paths and pointing them outside the module -- at
/etc/rsync/excludes, say -- is the ordinary way to write them; rsyncd.conf(5)
puts no constraint on where the file lives.  The result was not a refused
rule but a refused connection:

    failed to open exclude file /etc/rsync/excludes:
        Too many levels of symbolic links (40)
    rsync error: error in file IO (code 11) at exclude.c(1582)

with no symlink involved anywhere -- just a regular file outside the module.

Mark the window in which the daemon loads its own parameters and skip the
confinement there.  Everything else, in particular the peer-driven dir-merge
the leak test exercises, is still confined.  Also fix the trailing whitespace
in the original hunk.
2026-07-29 11:31:57 +10:00
Omar Elsayed 4572d1743c exclude: path resolving to operator path supplied --filter file 2026-07-29 11:31:57 +10:00
Omar Elsayed 32f36fa845 filter-leak_test.py: root-owned backup filter symlink leak
This test verifies that a root-owned backup symlink does not leak out-of-tree file contents through a filter file
2026-07-29 11:31:57 +10:00
Andrew Tridgell 5c20cd157a github: expect the merged read-only-inplace tests to skip
Follow-up to 6b885e51/51618b74, which I merged without updating the
per-workflow expected-skip lists, so every fleet run since has reported
a skip mismatch on eight targets.

Both skips are legitimate:

  readonly-partial-abort-mode-regression exits 77 as root ("root
  bypasses the read-only output-file precondition"), and the fleet runs
  most targets as root -- so it only ever executes in a non-root run.

  daemon-leaf-type-race-fchmod needs Darwin and --use-tcp, so outside
  the macOS tcp cell it always skips.

Worth noting rather than burying: this means neither sec-regression test
runs in the fleet's default cells.  The read-only one is exercised only
by a non-root local run, and the leaf-type one only by macOS over TCP.
2026-07-29 10:14:48 +10:00
Andrew Tridgell 8d82b07b54 testsuite: add the macOS setgid regression, and let it find a usable group
The test only means anything when the scratch directory's group is one
the caller cannot grant, since that is what makes macOS refuse the
setgid bit.  Taking that group from the build tree is fine for a
checkout under a shared parent but not for one under a home directory --
there the group is the user's own and the test skips silently.  I only
got RED/GREEN out of it by chgrp'ing the scratch tree by hand.

So it falls back to /private/tmp, which is group wheel.  On macOS as an
ordinary user it runs and passes.

It does NOT run in our macOS CI: that workflow drives the suite with
sudo, and root can grant every group, so the condition cannot exist --
hence the entry in the macOS expected-skip list alongside the others.
Making it run there needs a separate non-root invocation, not attempted
here.  The skip message says which case it is instead of blaming the
scratch group.

The /private/tmp directory is outside SCRATCHDIR, which the harness
cleans, so it gets a mkdtemp() name and an atexit hook: a fixed name in
a sticky world-writable directory would let concurrent runs delete each
other's live fixture, and a leftover owned by another user would make
every later run skip.  Verified on macOS that a run leaves nothing
behind.
2026-07-29 10:12:24 +10:00
Filipe Casal 5f0f8f298e syscall: preserve ordinary mode when setgid is denied
macOS's fchmodat(..., AT_SYMLINK_NOFOLLOW) returns EPERM and applies
NOTHING when the requested setgid bit is ungrantable, so
"rsync -a --chmod=D2750,F0640" exits 23 and leaves the destination at
0755/0700 where 3.4.4 leaves 0750.  fchmod() on an already-open
descriptor does the right thing: it succeeds, drops the setgid bit it
cannot grant, and applies the ordinary bits.

That fd path existed but was fenced behind "#if defined __linux__".
Guard it on O_NOFOLLOW so every platform that can open a leaf without
following a symlink uses it, and leave the fchmodat/fchmodat2 fallbacks
under __linux__.  Measured on macOS with an ungrantable group:

  fchmodat(2750, NOFOLLOW)  EPERM, 0700 -> 0700   (dir and FIFO alike)
  fchmod(fd, 2750)          ok,    0700 -> 0750
  fchmodat(0750, NOFOLLOW)  ok,    0700 -> 0750

A FIFO observed by the lstat takes the pathname call instead, then one
retry without S_ISGID.  Opening a FIFO -- even O_NONBLOCK -- makes this
process a reader for as long as the descriptor lives, which wakes a
writer blocked in open(O_WRONLY) and can cost it a SIGPIPE or the bytes
it writes before we close; the third line above is why that is
avoidable.  This does not make the function FIFO-open-free: the type
comes from the lstat, so a leaf swapped to a FIFO after it is still
opened, and set_file_attrs() opens FIFOs elsewhere for ACL/xattr work.
Closing that needs the open constrained to the observed type, which is
tracked separately.

The retry is a pathname call and does not pin the inode, so a leaf
swapped for another object of the same name is chmod'd instead;
AT_SYMLINK_NOFOLLOW still keeps it off a symlink's target, and the held
parent fd still confines the ancestors, so the out-of-tree boundary is
unaffected.  Only S_ISGID is retried -- clearing S_ISUID too could
discard a bit that was grantable when only setgid caused the failure.
Linux keeps the fd-first order it has always had.

The raced-to-a-symlink refusal after openat() also accepts EMLINK and
EFTYPE.  ELOOP is not universal for O_NOFOLLOW on a symlink -- FreeBSD
documents EMLINK and NetBSD EFTYPE -- so those two silently skipped the
refusal and fell through to the (still symlink-safe) pathname call.

Verified on real macOS: the regression test FAILS on 93a67aa9 with rc=23
and modes 0755/0700, and PASSES here with 0750/0750.
2026-07-29 10:12:24 +10:00
Andrew Tridgell 93a67aa995 github: expect fake-super-backup-fifo-regression to skip on Cygwin
The fleet run for this change reported it as an unexpected skip there.
Cygwin has no real FIFO for fake-super to represent, so the test skips
by design; every other target runs it.
2026-07-28 15:14:43 +10:00
Filipe Casal 4ce54db5ef syscall: preserve fake-super backups as placeholders 2026-07-28 15:14:43 +10:00
Filipe Casal 51618b74c0 testsuite: pin the leaf type of the EACCES chmod recovery
From PR #90, whose code change is superseded by the preceding commit:
that helper already requires S_ISREG on the fd it chmods, and declines
the recovery entirely on the local/chrooted path #90 left untouched.
The test is kept because it is the only oracle for the directory-
substitution case.

Verified on macOS over TCP: FAIL on acfc94ef, PASS on the preceding
commit.  Two limits worth knowing: it needs both Darwin and --use-tcp,
so it only runs in the macOS tcp cell, and its daemon sets
"use chroot = no", so it covers the fd-based branch only.  It also
accepts a run in which the race window was never reached, so it can
report success without having exercised the check.
2026-07-28 13:50:37 +10:00
Filipe Casal 6b885e5175 receiver: restore read-only mode before in-place transfer
The EACCES recovery added by c1d7b5c6 chmods a read-only destination to
0600 so an --inplace update can proceed, and only restores the mode
after the transfer.  Any abort in between -- peer EOF, checksum failure,
a signal -- leaves the file permanently owner-writable.  3.4.4 fails the
transfer and leaves 0444, so this is new exposure in 3.5.0.

Record the existing mode, add only owner-write, open the writable
descriptor, and put the old mode back before any network data is
consumed.  The descriptor stays writable afterwards, so a complete
read-only --inplace update still works.

What this does NOT promise.  Restoration is best effort, not a
guarantee: an unprivileged fchmod() silently drops S_ISGID when the
file's group is outside the process's groups, so 02444 can come back as
0444 with both calls reporting success, and a signal inside the
chmod/open/restore window still strands the relaxed mode.  The window
goes from "the whole transfer" to a few syscalls, which is the point,
but it is not closed.

The helper also requires a regular file, which closes PR #90's finding:
O_NOFOLLOW refuses a symlink at the leaf but not a directory swapped in
after the type probe, and the recovery would otherwise fchmod that
directory from 0755 to 0600.  On the fd-based branch that check is an
fstat() of the descriptor being chmod'd, so it is genuine.  On the
local/chrooted branch it is only a type check on a stable path --
do_stat() follows a leaf symlink and every later call re-resolves the
name -- so that branch is confined by the chroot, not by this check.
Recovery is also skipped outright when the file is already
owner-writable, since adding S_IWUSR cannot be what such an EACCES is
about and each needless chmod risks a special bit.

Reworked from PR #102, which was written against a tree that already had
secure_recv_open() and deleted it: its helper resolved through
secure_relative_open(), dropping the one_inplace operator-path ownership
policy from the recovery window.  (Its initial O_CREAT open toggled
operator_path_resolve by hand and kept the policy; the Linux
protected-regular retry and the recovery did not.)  partial-protected-
regular-retry-linux catches that -- it passes on the base, fails with
the PR as submitted, and passes here.  This version keeps the recovery
on secure_recv_open(..., one_inplace) and gates it on
"use_secure_symlinks || one_inplace" like every other open in the block.
2026-07-28 13:50:37 +10:00
Andrew Tridgell acfc94ef48 testsuite: harden the LD_PRELOAD hook and stop a crash reading as a skip
Follow-ups from review of the lazy-resolution fix:

- Guard resolution against re-entry.  If dlsym() ever reaches an
  interposed function the nested wrapper would recurse; it now takes the
  raw path instead.

- Forward a mode for O_TMPFILE as well as O_CREAT.  rsync itself never
  uses it, but the hook interposes every library in the process.  The
  test is an equality one because Linux defines O_TMPFILE as
  __O_TMPFILE|O_DIRECTORY, so a plain & would also match O_DIRECTORY.

- Treat death by signal as a failure rather than a skip.  The load marker
  is written by the hook, so a crash before that point is
  indistinguishable from the hook never loading -- which is precisely how
  the AlmaLinux SIGSEGV stayed hidden.

Note the signal branch is not exercised by any current configuration:
with the raw openat(2) fallback in place the hook no longer crashes even
when resolution fails, which is why reconstructing the pre-fix behaviour
does not reproduce it.
2026-07-26 19:40:12 +10:00
Andrew Tridgell abcf37a1d3 sender: null-check the anchor before comparing it to the module root
The copy-links confinement gate null-checks module_dir but then passes
anchor to strcmp() without checking it, which the scan-build gate flags:

    sender.c:291:7: warning: Null pointer passed to 1st parameter
        expecting 'nonnull' [core.NonNullParamChecker]

Not reachable today -- the one caller passes module_dir -- but NULL is a
legitimate value for this parameter: secure_relative_open() reads it as
"relative to the cwd", which the else branch relies on.  Only this branch
would dereference it.
2026-07-26 19:40:12 +10:00
Andrew Tridgell 9694994d5c fleettest: add an AlmaLinux 8 target
RHEL-family LTS coverage in the fleet, matching the almalinux-8-build.yml
CI job that until now was the only place this family ran.  Its container
and this VM do not agree on everything, so two box-specific skips are
recorded: no separate filesystem for a cross-device temp dir, and the
old static client the source-only push omits.

fs.protected_regular is enabled on the box (persisted in
/etc/sysctl.d/90-rsync-fleettest.conf) so protected-regular exercises the
real kernel behaviour here instead of skipping.
2026-07-26 19:40:12 +10:00
Andrew Tridgell 5cf902f87f github: correct the AlmaLinux expected-skip list
sender-remove-source-root-anchor runs and passes there -- the job is
privileged and / is writable -- so listing it as an expected skip made
the whole run fail on the mismatch.  partial-protected-regular-retry-linux
is deliberately not added: with the hook fix it runs there too.
2026-07-26 19:40:12 +10:00
Andrew Tridgell bbcef46455 testsuite: resolve the LD_PRELOAD hook lazily, not in its constructor
A preloaded open() interposes for the whole process the moment the loader
maps the library -- including calls made from OTHER shared objects'
constructors.  The order constructors run between unrelated objects is
unspecified, so resolving real_open in our own constructor is a race we
do not always win.

On AlmaLinux 8 we lose it: OPENSSL_init_library() calls open() from its
constructor before ours runs, real_open is still NULL, and the process
dies in the loader:

    #0  0x0000000000000000
    #1  open () from hook.so
    #2  OPENSSL_init_library () from libcrypto.so.1.1
    #3  call_init ... dl-init.c

Every rsync run under the hook segfaulted, the load marker never
appeared, and the test reported "hook was not loaded" -- so a crash on a
supported platform surfaced only as a skip.  Not a glibc-version thing:
ubuntu-1804 (glibc 2.27, older than AlmaLinux 8's 2.28) wins the race and
passes.

Resolve on demand at the top of each wrapper instead, with a raw
openat(2) fallback for the case where even dlsym() is unusable that
early.  The test now runs, and passes, on AlmaLinux 8.
2026-07-26 19:40:12 +10:00
Andrew Tridgell 72f2ceaa88 testsuite: skip the stdio_daemon copy-links test below protocol 30
Its hand-rolled protocol client greets with version 30 and sends a
protocol-30 argument string.  When the run pins the daemon lower the two
sides cannot agree and the client just sits there until it times out, so
the test failed on every check29 target rather than reporting anything
about copy-links.

The behaviour under test is not protocol-specific: the sibling
daemon-copylinks-parent-escape drives the same sender paths with the real
rsync client and passes at protocol 29, so skipping here loses no
coverage.  Registered in the check29 expected-skip lists.
2026-07-25 17:28:54 +10:00
Andrew Tridgell c5bc4e3677 syscall: defer a literal ".." to the walk before the leaf fast paths
secure_walk_at() has two fast paths for the final component that call
openat(ds_cur(&ds), part, ...) directly instead of going through
ds_descend().  A final component of ".." therefore never met the anchor
floor, and what happened depended entirely on the caller's flags:

    ".." with O_DIRECTORY              -> ELOOP          (refused)
    ".." with O_DIRECTORY|O_NOFOLLOW   -> fd for the directory ABOVE
                                          the anchor
    ".." without O_DIRECTORY           -> parent opened, then closed,
                                          EISDIR returned

That was harmless while every literal ".." was rejected at the front
door, but secure_relative_open_at_beneath() now admits them and
documents the held-fd stack as refusing every climb above the anchor.
The sender's own call passes O_RDONLY|O_DIRECTORY and so was never
affected -- but the guarantee the new API advertises has to hold for
whatever flags the next caller picks.

Route a literal "." or ".." through ds_descend() before the leaf fast
paths.  All three flag combinations now refuse a bare ".." with ELOOP,
and t_secure_relpath covers the matrix.
2026-07-25 17:28:54 +10:00
Andrew Tridgell 1a69d2e20d testsuite: hold down both sides of the copy-links ".." loosening
The shipped regression test covers the in-module target that the fix
enables.  The fix is a loosening, though -- the resolver used to refuse
every literal ".." at the front door, which guarded the module boundary
by accident -- so the escape needs an end-to-end guard too, not just the
unit coverage in t_secure_relpath.

Cover a file and a directory symlink in each direction.  Both types are
needed because they take different paths through the sender: the
directory one already resolved ".." via the dirstack walk while the file
one hit the front-door EINVAL, which is exactly the asymmetry reported on
this PR (a "../dir" symlink copied, a "../file" one was skipped).

The oracle is what landed on disk, not the exit status: a refused escape
legitimately makes rsync exit 23.  The in-module assertions matter as
much as the leak ones -- without them "nothing leaked" would also be
satisfied by refusing everything, i.e. by the bug being fixed.
2026-07-25 17:28:54 +10:00
Filipe Casal 4d8cbbecac sender: allow confined parent-relative copy-links targets 2026-07-25 17:28:54 +10:00
Andrew Tridgell 2532e9c17b testsuite: make the basis-xname injection reliable on slow targets
Two races made this test report a vacuous result -- neither FIFO opened,
so no traversal was attempted and there was nothing to confine -- on the
slower fleet VMs.  It failed 9 runs in 12 on NetBSD.

Wait for each FIFO helper to reach its blocking open() before starting
the transfer, instead of assuming a freshly spawned process is already
there, and retry a run that comes back vacuous.  A vacuous run is a setup
failure, not a security signal: an ESCAPE still fails immediately and is
never retried, so the oracle keeps its strength.

With the stale-object fix as well, NetBSD is 15 passes in 15.
2026-07-25 15:20:21 +10:00
Andrew Tridgell 5da6051243 testsuite: never reuse a stale object when building a patched peer
build_patched_rsync() copies the configured tree, including its prebuilt
objects, then rewrites one source and runs make.  copytree() preserves
mtimes, so on a target whose clock lags the host that pushed the tree the
copied sender.o is NEWER than the freshly patched sender.c: make reuses
it and the instrumentation never makes it into the binary.  The test then
drives an unmodified peer and reports a vacuous result -- basis-xname-
traversal did exactly that on NetBSD, whose clock ran ~1h behind (gmake
warned "modification time in the future" during the build).

Drop the object for each patched unit, and the prebuilt binary too, so
neither the compile nor the link can be skipped.  The function already
carried a comment about the same hazard on Cygwin's coarse mtimes.
2026-07-25 15:20:21 +10:00
Andrew Tridgell 4694b73d75 testsuite: let a daemon test move off a port held by other software
claim_ports() fails loudly when a port is occupied, which is correct for
the 36 tests that bind the port themselves: they must not silently drift
away from the number they are about to bind.  start_test_daemon() owns
both the bind and the URL it returns, so it can move instead -- and needs
to, because a fixed test port can be permanently held by unrelated
software on a shared CI box.  An ASUS service was found sitting on 13010
on the Windows/Cygwin target, which no amount of orphan reaping frees, so
daemon-exclude-namebased failed there on every run.

Add claim_free_port(), which tries the preferred port and then a few
nearby ones, and use it at that single seam.  _probe_bindable() grows a
non-fatal mode to support it; its default behaviour is unchanged.
2026-07-25 15:20:21 +10:00
Andrew Tridgell 957ce5038d testsuite: build the O_CLOEXEC probe the way the tree was configured
The probe compiles the real authenticate.c with a hand-written include
list and ignores the CPPFLAGS configure recorded.  Where a dependency
lives outside the default search path -- openssl from brew on macOS --
that fails at <openssl/sha.h>, for reasons unrelated to O_CLOEXEC, so
the test failed permanently on the macOS fleet target.

Take CPPFLAGS (and CC, when the environment does not override it) from
the configured Makefile so the probe matches the production build.
2026-07-25 15:20:21 +10:00
Andrew Tridgell f0949ad0de sender: keep a Cygwin UNC prefix out of the "/"-anchored cleanup
The absolute-source branch strips every leading slash and resolves the
parent beneath "/".  On Cygwin clean_fname() deliberately preserves
exactly two leading slashes, because //server/share is a separate UNC
namespace -- so //server/share/f would be resolved as /server/share/f,
a different object, and the size/mtime guard would then be comparing the
wrong file before the unlink.  That is the same wrong-target removal this
branch exists to prevent.

Decline the confined open for that shape (errno 0) so the caller falls
back to the path-based cleanup, as it did before.  Exactly two slashes
matches clean_fname's own rule: three or more still collapse to one.
2026-07-25 13:38:12 +10:00
Andrew Tridgell 38bc594f87 testsuite: cover the wrong-file removal an absolute -R cleanup could cause
The sibling anchor test only covers the nested case, where re-anchoring
the cleanup at the sender's CWD merely fails with EINVAL.  For a source
that is a direct child of / the parent component is empty, so the
cwd-backed cache handed back the sender's own working directory and
--remove-source-files unlinked a same-named entry there -- the real
consequence of the defect, and silent: the requested source survived and
the exit status was 0.

Needs root and a writable /, so it skips elsewhere; registered as an
expected skip on the non-root and sealed-root platforms.

The decoy's mtime is copied at nanosecond precision on purpose: the
sender's changed-file guard compares sub-second mtime too, and a
whole-second copy makes it skip the removal for an unrelated reason,
which would leave the test passing on a vulnerable build.
2026-07-25 13:38:12 +10:00
Filipe Casal 7053485ac1 sender: anchor absolute relative-source cleanup at root 2026-07-25 13:38:12 +10:00
Andrew Tridgell 05bd16a469 daemon: refuse peer values holding shell syntax in shell hooks
Context-aware quoting is only correct for one level of shell parsing.  A
hook may re-parse the substituted word in a nested shell:

    pre-xfer exec = sh -c 'printf %s %RSYNC_USER_NAME% >out'

The level-1 quotes are removed before the inner shell sees the value, so
an authenticated peer's username still reaches it as syntax however
carefully it was escaped.  Escaping cannot fix this; refuse instead.

A %RSYNC_*% value substituted into a shell-executed hook (early exec,
name converter, pre-/post-xfer exec) is now rejected if it holds any
character that can become shell syntax in any context: quote, backtick,
dollar, backslash, semicolon, ampersand, pipe, redirection, parenthesis,
or a control character.  Word-splitting and glob characters are left
alone -- they cannot execute anything and paths legitimately contain
them.  The refusal is fail-closed and logged: a hook may be an access
check, so silently skipping it is not an option.

Also fix the quote tracker itself, which moved to SHELL_SINGLE_QUOTED on
an apostrophe even inside "...", where it is an ordinary character.  That
made a value in `printf %s "it's %RSYNC_USER_NAME%"` escape for the wrong
context.  With the refusal above this is defence in depth, and it matters
if the refused set is ever narrowed.

The two existing hook-injection tests asserted that a metacharacter value
was quoted and the transfer still succeeded; both now expect the refusal.
2026-07-25 11:48:40 +10:00
Filipe Casal 4b4c6809ed daemon: quote hook expansions for their shell context 2026-07-25 11:48:40 +10:00
Andrew Tridgell 0293df8a81 github: register the platform-gated partial-retry tests as expected skips
partial-protected-regular-retry-policy is Darwin-only and its new Linux
twin is Linux-only, so each skips on the other's platforms; the Linux one
also skips under check29, which cannot negotiate CF_INPLACE_PARTIAL_DIR.
None of that was in any RSYNC_EXPECT_SKIPPED list, which made every Linux
and Cygwin cell report a skip mismatch.
2026-07-25 10:38:42 +10:00
Andrew Tridgell b3a560061b testsuite: cover the Linux EACCES recovery arm via LD_PRELOAD
The existing partial-dir recovery test only runs under dyld interposing,
so it skips everywhere except Darwin -- and the fs.protected_regular
compatibility retry it is meant to cover sits inside "#ifdef linux",
which Darwin never compiles.  That arm therefore had no coverage on any
platform.

Add a Linux twin driven by LD_PRELOAD: hook open/openat to model the
EACCES on the O_CREAT open of the existing partial leaf and swap the
partial dir for a symlink in the recovery window, and hook fstatat (with
an __fxstatat fallback for glibc < 2.33) to model the swap as foreign-
owned so the ownership walk refuses it.  Between the two tests both
recovery arms are now covered.

The staging path needs one_inplace, i.e. the protocol-30
CF_INPLACE_PARTIAL_DIR capability, so skip below that rather than fail a
control the older protocol can never satisfy.

Order the assertions so the escape is reported before the ownership-walk
control: a vulnerable build runs no walk at all, and that must read as an
escape rather than an inconclusive result.  Apply the same ordering to
the Darwin test, whose foreign-owner marker was built but never asserted.
2026-07-25 10:38:42 +10:00
Andrew Tridgell 0bfcd3b0f2 syscall: honor operator_path_resolve in do_chmod_at/do_lchown_at
Every other mutating do_*_at() wrapper (unlink, symlink, link, mknod,
rmdir, open, mkdir, rename) resolves an operator-supplied path through
owner_walk_parent() when operator_path_resolve is set.  do_chmod_at()
and do_lchown_at() did not look at the flag at all, and both hand an
absolute name straight to the unconfined full-path do_chmod()/do_lchown().

set_file_attrs() is called with operator_path_resolve = 1 precisely so
that "a flipped temp-dir parent then can't redirect the chmod/chown"
(rsync.c).  With no held dirfd -- an absolute --temp-dir or
--partial-dir -- both fell back to these two wrappers, so that promise
did not hold.  Give them the same ownership-walk branch the others use.

S_ISLNK(mode) still takes do_chmod()'s lchmod()/setattrlist() path.

The missing branch was spotted by Omar Elsayed in review on the
partial-dir EACCES recovery PR, together with the fix approach.

Suggested-by: Omar Elsayed <omarelsayed161@gmail.com>
2026-07-25 10:38:42 +10:00
Filipe Casal a646ded755 receiver: retain partial-dir policy across EACCES recovery 2026-07-25 10:38:42 +10:00
Andrew Tridgell 470cb86bdf sender: honor the do_*() guards when removing a source file
secure_remove_source_file() called unlinkat() directly, dropping the
dry_run no-op and the read-only/list-only refusal that do_unlink()
applies on the non-fd path.  That is what let --only-write-batch (which
implies dry_run) really delete the source files once a MSG_SUCCESS
reached the sender.  Use do_unlink_atfd(), which carries both guards.
2026-07-25 09:12:57 +10:00
Codex c0e6948d0f receiver: do not acknowledge batch-only files as installed 2026-07-25 09:12:57 +10:00
Codex 8367407f98 support: retain Python 3.7 compatibility 2026-07-25 06:57:21 +10:00
Codex b471a29888 authenticate: build without O_CLOEXEC 2026-07-25 06:45:26 +10:00
Andrew Tridgell 7b16872eff syscall: silence scan-build dead-store in do_fchmodat_nofollow fallback
When neither AT_FDCWD nor AT_SYMLINK_NOFOLLOW is available, the function body is
a no-op warning that never reads mode or dfd, so the leading 'mode &= CHMOD_BITS'
became a dead store and dfd an unused parameter -- which the pinned clang-18
scan-build gate flags (deadcode.DeadStores).  Move the mask inside the
AT_SYMLINK_NOFOLLOW guard where mode is actually used, and mark dfd/mode used in
the fallback.  No behavior change on any platform that has the symlink-safe
primitive.
2026-07-24 16:01:48 +10:00
Andrew Tridgell 7aea9d5f8e github: register dot-dir delete-scope tests as expected skips
malicious-dot-dir-delete-scope and peer-legacy-implied-delete-scope both need a
real TCP socket (require_tcp), so they skip on the pipe and protocol check
passes.  Add them to RSYNC_EXPECT_SKIPPED for the check/check30/check29 steps so
the CI skip-set matches.  (The squash-merge of the dot-content-scope fix dropped
this registration.)
2026-07-24 15:22:58 +10:00
Codex db380b62ac flist: keep synthetic and legacy implied parents non-content 2026-07-24 15:14:37 +10:00
Andrew Tridgell 4ce6d097fd github: register dot-file transfer-root tests as expected skips
daemon-dot-file-force-wipe and malicious-dot-file-delete-scope both need a real
TCP socket (require_tcp), so they skip on the pipe and protocol check passes.
Add them to RSYNC_EXPECT_SKIPPED for the check/check30/check29 steps so the
fleet skip-set matches.
2026-07-23 15:39:14 +10:00
Codex 21fade1bbb flist: reject non-directory transfer-root entries 2026-07-23 15:39:14 +10:00
Andrew Tridgell ab4e81d749 github: register malicious-server-partial-basis-symlink-overwrite as an expected skip
The new test needs a real TCP socket (require_tcp), so it skips on the pipe and
protocol check passes.  Add it to RSYNC_EXPECT_SKIPPED for the check/check30/
check29 steps so the fleet skip-set matches.
2026-07-23 14:47:21 +10:00
Andrew Tridgell fd86492913 receiver: only reject unconfined partial basis when in-place partial is active
The daemon rejection for a peer-selected FNAMECMP_PARTIAL_DIR basis that the
confined open declined fired at every protocol.  In-place partial updates are
only negotiated at protocol 30+ (CF_INPLACE_PARTIAL_DIR); at protocol 29 no
partial-basis redirect is possible, and the receiver already handled such a
transfer safely by completing it with no basis.  Gate the abort on
inplace_partial so a pre-30 daemon falls back to the safe no-basis path instead
of aborting a legitimate transfer with a protocol error.

Fixes operator-path-partial-dir-daemon at protocol 29.
2026-07-23 14:47:21 +10:00
Codex cfd40f55cb receiver: confine peer-selected partial basis paths 2026-07-23 14:47:21 +10:00
Andrew Tridgell de9000eb30 testsuite: add files-from-leak module-confinement test
Differential test for the daemon files-from/backup-symlink out-of-module read.
It races a --backup-dir push against a parent-swap flipper until a root-owned
backup symlink to an out-of-module secret lands in the backup tree, then tries
--files-from=:backup/sub/<name> and fails if the secret's content is read back
as the file list.  RED before the module-root confinement, GREEN after.

Requires root plus an untrusted uid to plant the cross-uid symlink; skips
otherwise.  Registered in the Cygwin expected-skip list.

Based on a report and proof-of-concept test by seks99x.
2026-07-23 08:28:36 +10:00
Andrew Tridgell 3fe1ed512c rsync: confine the daemon files-from open to the module root
A daemon serving a writable, non-chrooted module reads a client-requested
--files-from=:LIST through open_no_attacker_symlinks(), which follows a
symlink owned by uid 0 or the euid.  The module-root confinement in that
resolver (abspath_excluded_by_module) only fires when operator_path_resolve
is set, and this open left it clear -- so a trusted-owned symlink whose
target escapes the module was followed.

An attacker can obtain such a symlink without owning it: a --backup-dir push
makes the daemon back up the old destination symlink with the daemon's own
(root) ownership, and a parent-swap race can leave that root-owned backup
symlink pointing outside the module.  A later --files-from=:backup/... then
reads out-of-module file content as the file list, bypassing the same-uid
ownership constraint that normally protects files-from.

Set operator_path_resolve around the files-from open so the ownership walk
also refuses a trusted-owned symlink that redirects the list outside the
module root.  A daemon has no rsyncd.conf "files from" of its own, so this
path is always client-requested and confining it is unconditional.  No-op off
a daemon (the module-root check only fires when am_daemon).

The same ownership-walk opener backs the daemon merge/--exclude-from reads in
exclude.c, but those also load the module's own "include from"/"exclude from"
admin files, which on a non-chrooted module may legitimately live outside the
module; confining them there needs a client-vs-admin distinction and is left
to a separate change.
2026-07-23 08:28:36 +10:00
Andrew Tridgell 3b826d6683 github: register basis-xname-traversal in the Cygwin expected-skip list
The basis-xname-traversal test builds an instrumented sender via
build_patched_rsync(), which skips on Cygwin (coarse NTFS mtimes leave the
patched unit unbuilt, and forcing the rebuild trips -fno-common relinks). Add
it to the Cygwin RSYNC_EXPECT_SKIPPED set so its clean skip there is expected
rather than a skip-mismatch.
2026-07-22 14:50:55 +10:00
Andrew Tridgell de6ed4724d rsync: sanitize the peer-supplied basis xname on the client too
read_ndx_and_attrs() sanitized the wire-supplied xname (the alternate-basis
leaf name sent with ITEM_XNAME_FOLLOWS) only when sanitize_paths was set,
which is the daemon side. A client receiver has sanitize_paths == 0, so a
malicious server could send an xname containing ".." and, joined to an
operator basedir (--link-dest / --compare-dest / --copy-dest, or the fuzzy
dir), have the client open an out-of-tree file as the delta basis -- a
client-side arbitrary-read / file-existence-oracle / FIFO-hang. The ownership
walk in secure_basis_open() does not stop this: it deliberately follows a
plain ".." to a regular file (the legitimate --link-dest=../01 sibling, #915)
and only refuses foreign-owned symlink components.

Sanitize xname unconditionally. The operator basedir may legitimately be
relative, but the leaf name that arrives over the wire never legitimately
needs ".." or a leading "/".

Reported by z3r0s.
2026-07-22 14:50:55 +10:00
Andrew Tridgell d978342145 testsuite: add basis-xname-traversal RED test
Builds a malicious daemon-sender (env-gated xname injection patched into
sender.c) and pulls with --link-dest through the production receiver. A FIFO
one level above the link-dest dir, plus a helper blocked in open(O_WRONLY),
detects whether the receiver opened the traversed "../secret" basis. RED on
an unsanitized-xname receiver, GREEN once xname is sanitized.
2026-07-22 14:50:55 +10:00
Andrew Tridgell 594ab1e194 github: register msg-io-timeout-overflow in the Cygwin expected-skip list
The test builds a -fwrapv rsync via build_patched_rsync(), which skips on
Cygwin, so mark its clean skip there as expected.
2026-07-21 15:38:37 +10:00
Andrew Tridgell f10f666306 testsuite: add msg-io-timeout-overflow test
Builds the tree under test with -fwrapv (build_patched_rsync gains an
append_cflags option, which drops the copied tree's prebuilt objects so the flag
is actually applied on the full rebuild) to make the signed overflow
deterministic, then drives set_io_timeout(INT_MAX) via --timeout and asserts the
copy completes instead of spinning in the tight select()-EINVAL loop.  RED on the
unfixed computation, GREEN once the arithmetic is overflow-safe.
2026-07-21 15:38:37 +10:00
Andrew Tridgell 93c0e0144f io: make set_io_timeout() arithmetic overflow-safe
io_timeout can reach INT_MAX -- from an operator --timeout (options.c parses it
as a plain int, unbounded and even negative) or a peer's MSG_IO_TIMEOUT (now
also capped at 86400 in read_a_msg).  Several signed computations then misbehave:

 * allowed_lull = (io_timeout + 1) / 2 overflows to a negative allowed_lull /
   select_timeout; select() then returns EINVAL on the negative tv_sec, which
   isn't EBADF, so the read loop spins at 100% CPU forever (io_timeout ~= 68
   years never fires check_timeout), plus a keepalive flood.  Compute
   ceil(io_timeout/2) in a wider type so "+ 1" cannot overflow.

 * the generator and sender derive an int loop-check limit as allowed_lull * 5
   (generator.c, sender.c), which overflows for a large allowed_lull.  Cap
   allowed_lull so that product stays in range -- invisible to real use, as
   allowed_lull is the keep-alive half-interval and INT_MAX/5 seconds is over
   13 years.

 * a negative --timeout drove allowed_lull / select_timeout negative the same
   way; treat secs < 0 as "no timeout" up front.

The overflows are undefined behaviour, so plain -O2 gcc/clang happen to keep
select_timeout at 60, but -fwrapv / -fno-strict-overflow (common hardening) wrap
to the spin and -ftrapv aborts.

Reported by z3r0s.
2026-07-21 15:38:37 +10:00
Gogs e6044af38f io: cap MSG_IO_TIMEOUT value to prevent signed integer overflow
A malicious server can send MSG_IO_TIMEOUT with val near INT_MAX
(0x7FFFFFFF). The existing val <= 0 guard prevents timeout disabling,
but a large positive value passes through to set_io_timeout() where
(io_timeout + 1) / 2 overflows signed int, wrapping allowed_lull and
select_timeout negative. Every subsequent select() returns EINVAL
immediately, trapping the client in a tight CPU loop.

Cap the accepted timeout at 86400 seconds (24 hours), which is well
above any practical timeout and avoids the overflow in set_io_timeout().

Reported-by: z3r0s <https://github.com/z3r0s6>
2026-07-21 15:38:37 +10:00
Andrew Tridgell 8c78fe8e0b docs: NEWS.md/SECURITY.md for the post-notification hardening batch
Document the security and bug fixes integrated after the initial 3.5.0
security-fix set: peer io_error masking (io.c + flist trailer), log-file
control-character escaping (CWE-117), the --safe-links/--backup hard-link
bypass, the operator-path backup leaf sinks (do_symlink_at/do_rmdir_at), the
hash_search() chain bound (issue #217), and the clean_fname/robust_rename
resolver hardenings; plus the %%, .cvsignore "!", --chmod=a+s and
bracket-expression case-fold bug fixes.

NEWS.md gains the new SECURITY RELATED items and a BUG FIXES section;
SECURITY.md notes the peer error-flag masking, log-injection escaping and
hash_search bound in the malicious-peer section.
2026-07-20 15:24:06 +10:00
Andrew Tridgell 15141f6803 testsuite: make the partial-dir reject check protocol-aware
The outside-module victim-intact oracle holds at every protocol and
stays unconditional.  The stronger 'daemon actively rejects the forced
--partial-dir operand' behavior is protocol-30+ only: at protocol 29 the
operand is handled differently and the transfer completes normally with
the victim still untouched (verified).  So gate that check on protocol
30+, and at protocol 29 require the known-good normal outcome (rc==0,
dest replaced) rather than skipping -- keeping the branch non-vacuous.
Surfaced by the fleet's proto29 pass.
2026-07-20 14:07:05 +10:00
Andrew Tridgell 70c121d86f testsuite: skip the %C sub-case of ki58 at protocol < 30
%C only renders a hex digest for a canonical checksum.  At protocol < 30
the negotiated file checksum is a non-canonical MD4 variant, so %C (via
sum_as_hex) renders empty -- there is no digest to compare and no F_SUM
read to over-run.  Gate the over-wide %C sub-case on protocol 30+, where
checksum_for() still fails on a missing digest so a real regression is
caught.  The %% literal checks are protocol-independent and still run.
Surfaced by the fleet proto29 pass on ubuntu-2204/2404/2604.
2026-07-20 14:07:05 +10:00
Andrew Tridgell 78767976ef github: register operator-path-backup-{rmdir,symlink} in the Cygwin skip set
Both new tests skip unless run as root; the Cygwin CI job runs non-root
and enforces an exact RSYNC_EXPECT_SKIPPED set, so an unregistered skip
fails the suite.  Add them to the list (they run and pass on the
root/sudo Linux, BSD, Solaris and macOS targets).
2026-07-20 14:07:05 +10:00
Omar ElsayedandAndrew Tridgell 71b01c4b5c syscall: confine operator paths in do_symlink_at and do_rmdir_at
do_symlink_at() and do_rmdir_at() were the only operator-path syscall
wrappers still missing the operator_path_resolve branch that
do_mknod_at()/do_open_at()/do_rename_at()/do_unlink_at() already carry:
an absolute operator path (e.g. an absolute --backup-dir) took the
'*path == "/"' arm straight to the bare do_symlink()/rmdir(), whose
libc path resolution follows a parent-component symlink.  A local
attacker who owns a parent component of the backup tree could thus
redirect a backup symlink creation or a backup-dir rmdir outside the
intended tree.

Route both through owner_walk_parent() + symlinkat()/unlinkat() when
operator_path_resolve is set, mirroring the existing wrappers, so a
foreign-owned parent component is refused while the operator's own is
followed.  symlink_optout_allowed() (--insecure-links / 'insecure
links =') restores the legacy following.

Tests: operator-path-backup-symlink and operator-path-backup-rmdir
drive a live parent-component swap (native C flipper) against a local
--backup-dir push and confirm nothing escapes the backup tree (RED
before, GREEN after).

Co-authored-by: Andrew Tridgell <andrew@tridgell.net>
2026-07-20 14:07:05 +10:00
Andrew Tridgell 7105287d46 github: register source-change-size-continues in the macOS/Cygwin skip sets
The new source-change-size-continues test skips on non-Linux platforms
(it needs LD_PRELOAD + /proc/self/fd).  macOS and Cygwin are the two
non-Linux CI jobs that enforce RSYNC_EXPECT_SKIPPED, so an unregistered
skip there fails the suite (runtests.py treats an unexpected skip as a
mismatch).  Add the test to both lists.  The Linux enforcing jobs
(ubuntu, ubuntu-22.04, almalinux-8) run it and are unaffected; the
BSD/Solaris jobs do not enforce the skip set.  The mac2/cygwin fleet
targets read these same lists, so the fleet is covered too.
2026-07-20 14:07:05 +10:00
Zen Dodd d2aa9fb034 testsuite: strengthen regression oracles 2026-07-20 14:07:05 +10:00
Andrew Tridgell 527b61afb9 testsuite: correct the ki62 comment about MSG_IO_ERROR coverage
The header claimed protocol-level crafting of MSG_IO_ERROR is covered by
msg-io-* crafted-server tests; no such test exists (msg-io-timeout-zero
crafts MSG_IO_TIMEOUT).  Say what the test actually locks down.
2026-07-20 14:07:05 +10:00
Andrew Tridgell 912644c94d log: don't parse the second '%' of '%%' as a new format escape
log_formatted() renders %% as a literal '%', but log_format_has() still
rescanned the literal '%' as the start of a new escape, so a format such
as --out-format='%%i' misdetected the 'i' and turned on itemizing (and
'%%b'/'%%c'/'%%C' likewise perturbed log_before_transfer and checksum
retention).  Skip the literal so both parsers agree.

Extends the ki58 test: an attribute-only change must not be logged under
--out-format='%%i %n', while a transferred file still renders the
literal '%i'.
2026-07-20 14:07:05 +10:00
Andrew Tridgell 7761384497 backup: fail closed when a symlink target is unreadable
The --safe-links guard on the backup hard-link fast path only skipped the
backup when do_readlink() succeeded (llen > 0) and the target escaped.  A
failed readlink (e.g. the link vanished between the lstat and the
readlink) fell through to link_or_rename(), which could hard-link the
symlink into the backup area unchecked -- the same bypass the guard was
added to close.

Fail closed: skip the backup when the target can't be read.

Extend the KI-72 test with a safe (in-tree) symlink case to confirm the
guard doesn't over-block and drop legitimate safe symlinks.
2026-07-20 14:07:05 +10:00
Andrew Tridgell 9da5b5450f flist: mask peer-supplied io_error to defined bits
recv_file_list() OR's the wire-supplied end-of-list error value straight
into the local io_error at three sites (the varint-flags path, the
XMIT_IO_ERROR_ENDLIST byte path, and the protocol < 30 int flag).  Like
the MSG_IO_ERROR path fixed in io.c, a malicious peer could set arbitrary
undefined bits, which then accumulate in io_error and can be re-forwarded
to other peers.

Mask each with IOERR_VALID_MASK so only the defined IOERR_* bits survive,
matching the io.c MSG_IO_ERROR handler.
2026-07-20 14:07:05 +10:00
Leonid Bugaev ede9a1a3ba Fix man page errors: --max-alloc=0 contradiction + EXIT VALUES table
- Update --max-alloc documentation: 0 is now rejected (CVE-2026-53794),
  no longer means SIZE_MAX
- Remove nonexistent exit code 6 (never emitted by any code path)
- Add missing exit codes 15 (RERR_CRASHED), 16 (RERR_TERMINATED),
  19 (RERR_SIGNAL1)
- Fix code 20: SIGUSR1 is actually code 19, not 20
  (code 20 is SIGINT/SIGTERM/SIGHUP)
2026-07-20 14:07:05 +10:00
Andrew Tridgell 0f5986cda8 github: register ki62-io-error-mask in the expected-skip lists
ki62-io-error-mask needs --use-tcp (it SIGKILLs the daemon-side sender), so it
skips under the default pipe-mode make check.  Register it alongside the other
tcp-only daemon test (daemon-argv-limit) in every workflow that pins
RSYNC_EXPECT_SKIPPED, so its pipe-mode skip is expected rather than flagged.
2026-07-20 14:07:05 +10:00
Andrew Tridgell 7490ad2420 testsuite: harden ki58/ki62 tests for the fleet
Two of the integrated MC/DC-audit tests were environment-fragile:

- ki58: asserted the exact string '100% done percentfile', but %f expands to the
  transfer-relative path (with leading dirs) when the source is an absolute path,
  so the basename assumption failed.  Assert the literal-percent escape '100% done '
  plus the file name instead (still RED on a broken %%: it emits '100%% done').

- ki62: killed daemon.kill() -- the listener -- but rsyncd forks a child per
  connection, so the child sender kept streaming and the receiver hung.  Parse the
  transfer child's pid from the daemon log ('[pid] rsync on <mod>/') and kill that.
  killpg is not usable: the test daemon shares this test's process group.

Fixes are unchanged; only the test drivers.
2026-07-20 14:07:05 +10:00
Leonid Bugaev 3534cab477 Mask incoming MSG_IO_ERROR to defined bits only
A peer-supplied MSG_IO_ERROR value was OR'd into the local io_error
without masking, allowing a malicious peer to set arbitrary bits that
propagate to exit codes and get re-forwarded to other peers.

Fix: mask the incoming value with IOERR_VALID_MASK (IOERR_GENERAL |
IOERR_VANISHED | IOERR_DEL_LIMIT) before OR'ing.

Test: testsuite/ki62-io-error-mask_test.py
2026-07-20 14:07:05 +10:00
Leonid Bugaev cfbf411fc3 Add %% escape to --out-format and --log-file-format strings
The log_formatted() switch had no case for '%', so %% did not
produce a literal percent character.  Add case '%' to output
a single '%' character, matching the printf convention.

Test: testsuite/ki58-log-format-percent_test.py
2026-07-20 14:07:05 +10:00
Leonid Bugaev feb26929de Fix spurious abort when CVS .cvsignore contains '!' clear-list token
The CLEAR_LIST guard in parse_rule_tok checked rule->rflags for
FILTRULE_NO_PREFIXES, but NO_PREFIXES is a template-level flag
excluded from FILTRULES_FROM_CONTAINER inheritance.  The guard
was always true for CVS rules, causing RERR_SYNTAX abort instead
of clearing the list.

Fix: check template->rflags instead of rule->rflags.

Regression test: testsuite/ki73-cvs-clear-list_test.py
2026-07-20 14:07:05 +10:00
Leonid Bugaev f602f7d86d Fix bypass of --safe-links when --backup hardlinks a symlink
When CAN_HARDLINK_SYMLINK is defined (Linux, macOS), the backup
hardlink fast path at link_or_rename() succeeded for symlinks and
'goto success' skipped the safe_symlinks check.  An escaping symlink
(pointing outside the transfer tree) was silently preserved in the
backup area despite --safe-links.

Fix: check safe_symlinks BEFORE the hardlink path.  If the symlink
target escapes, skip the backup (same as non-hardlink-symlink systems).

Regression test: testsuite/ki72-safe-links-backup_test.py
2026-07-20 14:07:05 +10:00
Andrew Tridgell 81a88dc590 github: exclude the flipper tests on the OpenBSD CI VM
The vmactions OpenBSD VM has the same kernel bugs as the fleet's
OpenBSD box: a connect()-under-rename-load lost-wakeup and an FFS
rename-storm corruption that hang the symlink-race flipper tests to
the 300s timeout for non-rsync reasons (sender-remove-source-secure
just did so in the --use-tcp pass).  Exclude the same three tests the
fleet config already excludes there; the protections they exercise
are verified on the Linux and other BSD targets.
2026-07-20 14:07:05 +10:00
Andrew Tridgell da4354303e docs: fix errors in the rsync.1.md update
Corrections to the preceding man page rewrite, found in review:

- --max-alloc: 0 is rejected as invalid since 3.5.0 (CVE-2026-53794),
  it does not mean "no limit"
- -t: restore "next transfer" -- it is the subsequent run that behaves
  like --ignore-times when times aren't preserved; also note that the
  timestamp-range limit is protocol < 30, not just an old remote
- --max-delete: re-add the caution that a pre-3.0.0 CLIENT treats
  --max-delete=0 as unlimited (a new client forwards it as -1, so the
  client version is the boundary that matters)
- --stats: the deleted-files line needs negotiated protocol >= 31, not
  just a new remote rsync
- --version: the server does not ignore it; only the repeated-option
  JSON output is client-side only
- SECURITY: restore the sentence distinguishing the pre-transfer -c
  checksum from the whole-file transfer-verification checksum (which
  runs unless --checksum-choice=none); fix a singular/plural clash in
  the escape-path paragraph
- remove a leftover XXX comment marker (the paragraph it questioned is
  correct: user@ sets the rsync module user, ssh -l sets the login)
- typo/mechanical: "If it is sufficient", "is output in a JSON",
  missing space before --read-batch, unbalanced paren after
  rsyncd.conf(5), missing "the" before --whole-file, trailing
  whitespace
2026-07-20 14:07:05 +10:00
Paul Mackerras 96ed617a33 docs: Update rsync.1.md man page
Updates to make statements more definite and explicit and improve
English expression, including:

- Globally change dir to directory, arg to argument, parens to
  parentheses.

- Remove some references to behaviour of past versions, on the grounds
  that the man page should describe the current version, and only
  describe past behaviour when talking about how to get a current rsync
  client to deal with an older remote rsync version.

- In most cases where a version number is mentioned, add the month
  and year of release of that version.

- Be more explicit about when "file" means "regular file" vs. "any
  filesystem object".

- Remove some parentheses, where the parenthesized comment is important
  enough to warrant making it a first-class part of the sentence.
2026-07-20 14:06:02 +10:00
Andrew Tridgell c53d107f97 util1: drop null-tests on robust_rename's from/to args
Both callers pass non-null from/to, but the 'to &&' test taught the
clang analyzer that to may be null, and it then walked a null to
through copy_file -> unlink_and_reopen -> robust_unlink into glibc's
nonnull-annotated strlcpy, failing the scan-build gate.  The args are
required non-null, so test the first byte directly.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 4992f7e98b testsuite: make the t_safe_arg link portable to BSD and Solaris make
T_SAFE_ARG_OBJ used $(filter-out main.o,$(OBJS)), a GNU-make-only
function that BSD make and Solaris make silently expand to nothing,
so t_safe_arg linked with no rsync objects at all and failed with
undefined symbols (am_daemon, io_timeout, send_msg_int, ...).  The
fleet missed this because those VMs build with gmake; the FreeBSD,
OpenBSD and Solaris CI VMs use the native make.

Spell out the object list via a new OBJS1_NO_MAIN macro instead;
plain macro expansion works in every make.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 5022446815 github: gate PR CI on a 'run-ci' label to save CI minutes
Now that pull_request triggers fire for any base branch, limit runner
minutes by skipping PR jobs unless the PR carries the 'run-ci' label.
Applying a label needs triage access, so a fork PR can't enable the
matrix by itself.  The 'labeled' trigger type is added so applying the
label starts a run immediately; skipped jobs cost no runner minutes.
Push, schedule and manual dispatch runs are unaffected.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 39fbc02b79 github: run CI on pull requests regardless of base branch
The pull_request triggers were filtered to base branch master, so PRs
targeting staging branches (e.g. pr-rsync350-sec-fixes) got no CI at
all.  A PR is a deliberate request for review, so run the checks on
every PR; the push trigger keeps its master filter to avoid running
the whole matrix on every WIP branch push.
2026-07-20 14:05:32 +10:00
Andrew Tridgell a308186f4b testsuite: cover content changes during a transfer (shrink, vanish)
Adds a reusable harness (mutatefns.py) that mutates a source resource in the
window after the file list is built but before that resource is sent -- the
same window the growing-file regression exposed -- plus tests for a source
file that shrinks or vanishes mid-transfer.  Both are handled gracefully
today (no protocol abort, later files still transfer); these lock that in.

Note: metadata (perms/mtime/xattr/ACL) is captured at flist-scan time and
applied from the flist, so a data-phase change is a no-op -- content is the
only resource re-read at send time, so it is the meaningful surface here.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 42f7c32ce5 receiver: don't abort the transfer when a file grows during the run
The sender records each file's length when it scans the file list, then
re-stats and maps the file at its current size when it later sends the
data (sender.c: do_fstat + map_file + match_sums use st.st_size).  A file
appended to in that window -- a live log written during a nightly backup
is the common case -- is transmitted longer than its flist-recorded
length.

A hardening check added in d33e599c (offset + i > total_size and
offset + len > total_size in receive_data) turned that benign, common
condition into a fatal "received more data than file length"
RERR_PROTOCOL, tearing down the whole connection so every file after the
growing one is skipped.  Stock rsync has no such check: the receiver
writes to a temp file, so the extra bytes just extend it, and the
whole-file checksum-verify already contains a malicious sender -- the
bound bought little while breaking a routine case.

Remove both checks to restore the stock behavior.

Regression test: testsuite/growing-file_test.py (RED before, GREEN after).
2026-07-20 14:05:32 +10:00
Andrew Tridgell 94a7ccd63d testsuite: link t_safe_arg against real objects (portable, drop --gc-sections)
The KI-54 helper linked options.o compiled with -ffunction-sections and relied on
-Wl,--gc-sections to drop the option parser.  That is GNU-ld-only: the fleet
showed macOS (ld64; also its custom rule dropped the openssl include path) and the
cygwin PE linker leave parse_arguments and its deps undefined, failing the build
of every CHECK_PROGS target on those hosts.  Instead link the real rsync objects
(the same set as the rsync binary, minus main.o -- supplied renamed via
t_safe_arg_main.o) so safe_arg's deps all resolve with a plain link on every
platform.  t_safe_arg_main.o depends on $(HEADERS) so the generated proto.h is
built before it under a parallel make.  No behaviour change to the test itself.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell d443e8dc93 wildmatch: fold bracket-expression pattern chars under force_lower_case
dowild()'s literal path folds the pattern char, but the [class]/[a-z] path
compared unfolded pattern bytes against the (folded) text, so iwildmatch() was
still case-asymmetric for character classes and ranges -- e.g. a daemon
'hosts deny = [A-Z]*.EVIL.COM' failed to match a lower-case host (access-control
fail-open, same class as the literal case).  Fold p_ch for the escaped-member,
range-endpoint and plain-member comparisons; the range start (prev_ch) picks up
the folded value too.  Only active under force_lower_case (iwildmatch), so
case-sensitive wildmatch() is unchanged.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell 1777c884cf testsuite: extend iwildmatch test to bracket expressions (KI-53 follow-up)
The KI-53 fix folds only the literal-match path, so a bracket-expression
pattern ([A-Z], [ABC], [\A]) still compares unfolded pattern bytes against the
folded text -- an upper-case character class / range fails to match a lower-case
host, the same access-control fail-open class as the literal case.  Add bracket
cases (plain, range, mid-pattern, and escaped member; RED until the class/range
path folds too).

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell fbeb553b73 util1: fix clean_fname ".." collapse off-by-one
After the backward walk, s points at the first char of the prior component and
s[-1] is its leading '/', so the boundary test must read s[-1] (not *s) and t
must reset to s (not s+1).  The old off-by-one left CFN_COLLAPSE_DOT_DOT_DIRS
dead for all multi-component and absolute paths.  The peer-traversal guard
CFN_REFUSE_DOT_DOT_DIRS is checked first and is unaffected, so this is a
normalization-correctness fix, not a traversal hole.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell 07bf9af5b5 testsuite: add clean_fname ".." collapse test (KI-50)
t_clean_fname links the real util1.o and checks that clean_fname() with
CFN_COLLAPSE_DOT_DOT_DIRS collapses ".." for multi-component and absolute
paths.  RED before the fix: an off-by-one left the collapse dead for all such
paths.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell fd4c75116b util1: confine operator paths in the robust_rename EXDEV fallback
The cross-filesystem fallback copied to the dest and unlinked the source without
operator-path confinement, so an absolute --temp-dir/--partial-dir on another
filesystem was opened/unlinked via plain libc -- a raced parent symlink could
redirect the dest-write or source-unlink out of the module.  The source READ was
already confined; flip operator_path_resolve for an absolute (operator) path
around the copy_file dest-open and the do_unlink_at, leaving relative in-module
paths on the secure_relative_open arm.

This is the EXDEV-fallback backstop for the same absolute --partial-dir /
foreign-owned-parent-symlink escape that operator-path-partial-dir_test.py
already exercises at the handle_partial_dir() layer (which confines the staging
dir before this code runs).  A dedicated test for the EXDEV copy_file path itself
would need the main tmp->final rename to fail first and then hit EXDEV on a
foreign-owned raced parent -- a nested, timing-dependent trigger.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell ad64e99590 log: escape control chars written to the log file
logit() wrote the message to the log file with a raw fprintf, so an
attacker-controlled filename could inject terminal escapes that an admin later
executes when cat'ing the log (CWE-117).  Route it through filtered_fwrite(),
keeping the trailing newline raw.  Also escape C1 controls (0x80-0x9f, incl CSI)
on filtered_fwrite's use_isprint=0 path, which previously caught only C0.

Reported-by: Leonid Bugaev

The C1 escaping is gated by an escape_c1 flag set only for the log path, so
--8-bit-output / iconv terminal output still passes 8-bit bytes (incl. UTF-8)
through unchanged.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 674c175e31 testsuite: add log control-char escaping test (KI-51/52)
Transfers files whose names carry C0 (0x1b) and C1 (0x9b) control bytes via
--log-file and checks the log contains no raw control bytes (only \#NNN
escapes).  RED before the fix: logit() writes the raw filename to the log
(CWE-117), and C1 controls slip through filtered_fwrite.  Skips if the fs
rejects control-char names.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell 0d0e505902 chmod: a+s must set both setuid and setgid
parse_chmod()'s 'a' clause set the "where" bits but not topbits, so "a+s" fell
through to setuid only and dropped setgid (chmod(1) sets both).  Add
S_ISUID|S_ISGID to topbits for 'a'.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell ce0e1c3ced testsuite: add chmod a+s set-id test (KI-55)
Transfers with --chmod=a+s / u+s / g+s (needs -p to apply set-id bits) and
checks the resulting mode.  RED before the fix: a+s sets setuid only and
drops setgid.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell 22efd249ad options: fix safe_arg() uninitialized-byte leak in filename-mode quoting
The escape counter reserved a slot for every backslash, but the writer
suppresses the escaping backslash before a wildcard and -- via the
strchr(WILD_CHARS, '\0') footgun -- on a trailing backslash, so counter and
writer disagreed and left an uninitialized heap byte in the returned string that
is handed to the legacy remote shell (protect_args=0).  Make the counter mirror
the writer and guard the strchr with f[1] (which also correctly doubles a
trailing backslash).

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell a83b76aa30 testsuite: add safe_arg uninitialized-byte test (KI-54)
t_safe_arg links the real options.o (via --gc-sections so only safe_arg is
pulled in), poisons the heap, and checks that filename-mode quoting is exact.
RED before the fix: the counter/writer backslash miscount leaks an
uninitialized heap byte into the returned string.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell 7a6d45b02b wildmatch: fold the pattern char in iwildmatch (case-insensitive)
iwildmatch() folded only the text to lower case, not the pattern, so it was
asymmetric rather than case-insensitive.  An upper-case "hosts deny" token
(e.g. *.BADDOMAIN.COM) then failed to match a lower-case peer name and the
blocked host was admitted -- an access-control fail-open.  Fold the pattern
char too under force_lower_case.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell 54ee418964 testsuite: add iwildmatch case-fold test (KI-53)
t_iwildmatch links the real lib/wildmatch.o and checks that iwildmatch()
folds case on BOTH the pattern and the text.  RED before the fix: an
upper-case pattern token (a daemon 'hosts deny = *.BADDOMAIN.COM') fails to
match a lower-case host -> access-control fail-open.

Reported-by: Leonid Bugaev
2026-07-20 14:05:32 +10:00
Andrew Tridgell 2e0b572057 testsuite: make patched_rrsync robust to the rrsync RSYNC path
patched_rrsync() rewrote support/rrsync's hardcoded RSYNC path to a test stub
with a value-specific str.replace("RSYNC = '/usr/bin/rsync'", ...).  Python's
str.replace silently returns the text unchanged when the needle is absent, so on
a tree whose shipped rrsync uses a different path -- e.g. FreeBSD's net/rsync
port patches it to /usr/local/bin/rsync -- the rewrite no-oped and
rrsync-under-test kept exec'ing the real system rsync, which blocks in server
mode on stdin and hangs the rrsync tests (a testsuite timeout on the BSDs).

Match the RSYNC assignment line itself (re.subn on ^RSYNC\s*=.*$) rather than a
specific value, using a callable replacement so the path is inserted verbatim,
and fail loudly via test_fail if it is not found exactly once.  Centralize
rrsync-symlink_test.py's duplicate inline replace onto the same helper.

Reported-by: Rodrigo Osorio <rodrigo@FreeBSD.org>
2026-07-20 14:05:32 +10:00
Vladimir Marek 6fa1f15c66 acls: implement Solaris facl(2)-based ACL helpers
Solaris has facl(2), which performs ACL operations on an already-open file
descriptor.  This adds Solaris fd-based helpers for the ACL operations that
rsync needs while keeping the existing path-based fallback for callers without
a held fd.

On Solaris, setting an ACL on a directory replaces the combined access and
default ACL set.  The path-based sys_acl_set_file() already handled this by
reading the other half of the directory ACL, merging access and default entries,
and then calling acl(..., SETACL, ...).  The new sys_acl_set_fd_type() preserves
that behavior with fd-based operations: it uses fstat() to identify directories,
reads the other ACL half with sys_acl_get_fd_type(), combines the access and
default entries, marks default entries with ACL_DEFAULT, and finally writes the
combined ACL with facl(2).  Deleting a default ACL similarly fetches the access
ACL through the fd and rewrites only that access ACL with facl(2), which removes
the default ACL without re-resolving the path.

Driving the apply off the held fd also closes a symlink-race on the Solaris ACL
apply (the path-based sys_acl_*file re-resolves the path -- the CVE-2026-53799
class, unfixed on Solaris until now).  Added on integration: for a root receiver
a missing held fd on a confined receiver means the leaf was raced to a symlink
(acl_set_file follows it), so refuse the path-based set/delete rather than apply
the ACL to a redirected inode; a plain non-root receiver keeps the path-based
fallback for a legitimately un-pinnable owned leaf (e.g. a 0300 dir), matching
the operator-path op_pin rule.
2026-07-20 14:05:32 +10:00
Vladimir Marek 13a010959b lib/sysxattrs: make write_xattr more robust
Don't let close() clobber the errno from a failed write, and report a close()
failure on an otherwise-successful write.

(The short-write length, the unsigned bufpos = -1 returning success, and the
size == 0 returning failure that Vladimir also reported were already fixed
in-tree.)
2026-07-20 14:05:32 +10:00
Vladimir Marek 9502c4a1c7 testsuite: lz4-default compress-level and out-of-source-tree build fixes
Use a level-aware compressor for the compress-options --compress-level check.
The default compressor can be lz4, which has no tunable compression levels and
therefore reports level 0.  Select zlibx or zlib for this subtest so it verifies
that --compress-level is passed to a compressor that supports levels.

Read the build config when setting up the symlink-placeholder test.  The
t_symlink_secure helper is compiled against the build directory config.h; use
that same config for the Python test setup so it creates the symlink placeholder
PoC inputs when the compiled helper will exercise them.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 8a3a3e4de1 testsuite: drop the symlink-escape oracle when it runs but crashes (Solaris)
The da7c4208 oracle-exec guard only caught OSError (can't-exec / ENOEXEC, on the
BSDs and macOS).  Solaris execs the Linux x86-64 old_versions/rsync_3.2.7 binary
without ENOEXEC but it SIGSEGVs, so subprocess.run('--version') returned -11
without raising and ORACLE_BIN stayed set -- the oracle daemon launch then died
("rsyncd exited before listening on port 12910, status=-11") and failed the test.

Require the probe to exit cleanly (returncode == 0); a non-zero/signal exit, a
hang (TimeoutExpired), or a can't-exec (OSError) all degrade to the static
contract.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 07a7a19790 testsuite: make copy-xattrs-symlink-race a reliable RED oracle
The test relied on a held-dirfd cache MISS coinciding with the parent flip, so it
only caught the escape occasionally (it read as -j-load flakiness).  Bury the
flipped dir below the held-dirfd cache depth (DEEP, 70 levels > VFS_DPC_MAXDEPTH
64) so set_file_attrs() takes the fd-less metadata path for every file on every
push -- removing the cache-miss race and leaving only the parent flip landing in
the create->lsetxattr window.  Widen that window (64 KB payload) and raise the
file count (N=120) for more attempts per push.

Now reliably RED on the unfixed code (8/8 under load, the CI condition; ~5/6
standalone) and reliably GREEN with the set_file_attrs re-pin/refuse fix
(no false positives, standalone and under load).  Same inline 3-rename flipper
(the reset-each-push model the EXCHANGE c-flipper can't drive).
2026-07-20 14:05:32 +10:00
Andrew Tridgell 03535a62a5 rsync: never path-resolve a confined receiver's xattr/ACL write (copy-xattrs race)
set_file_attrs() pins the entry via the cached held dir fd (held_dfd_for) and
drives the xattr/ACL ops off that fd (fsetxattr).  But when the pin missed --
held_dfd_for() returns -1 (the path is deeper than the dirfd cache, or its dir
isn't the held one), or the leaf openat() loses a race -- held_fd stayed -1 and
set_stat_xattr()/set_xattr()/get_acl_fdat()/set_acl_fdat() fell through to the
path-based branch (sys_lsetxattr(fname,...)).  Unlike the chmod/chown/times path
wrappers (which secure-resolve), that raw lsetxattr re-resolves the parent, so a
concurrent flip of a dest parent component to a symlink->outside lands the xattr
OUTSIDE the destination tree (the intermittent copy-xattrs-symlink-race escape
that surfaces under -j load, which widens the open->setxattr window).

Re-pin through secure_relative_open() when the cached pin misses on a confined,
non-operator receiver path, so the xattr/ACL ops always use a confined fd -- NOT
a raw path lsetxattr; if the re-pin also fails (a genuinely raced parent/leaf
symlink) skip the path-based ops (xattr_refuse) rather than redirecting them.
The re-pin passes O_DIRECTORY for a directory leaf.  Apply the same re-pin/refuse
to gen_entry_copy_xattrs() (the dir xattr copy), whose dfd<0 path likewise fell
to copy_xattrs() with dest_fd==-1.  chmod/chown/times are unchanged (confined via
their *at wrappers); operator paths keep op_pin/op_refuse.
2026-07-20 14:05:32 +10:00
Andrew Tridgell c144c0931c github: drop daemon-chroot-munge-default from the AlmaLinux expect-skip
The test runs and passes on AlmaLinux 8 (chroot works and CI runs as root), so
listing it in RSYNC_EXPECT_SKIPPED tripped the strict skip-set check (expected to
skip but ran).  It only genuinely skips where chroot is unavailable -- cygwin --
whose entry is kept.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 88b4d77b18 testsuite: fall back to the static contract when the 3.2.7 oracle can't exec
daemon-symlink-escape-matrix prefers the in-tree old_versions/rsync_3.2.7 as its
legacy oracle, but that is a Linux x86-64 static binary.  On a full checkout (the
per-platform CI runners) it is present yet cannot exec on a BSD/macOS/Solaris host,
so start_test_daemon() crashed with OSError ENOEXEC and failed the test.  (The
git-archive fleet never hit this: .gitattributes export-ignores old_versions/, so
the binary is absent there and the test already took its static-contract path.)

Probe the selected oracle binary (rsync --version) and degrade to the static
contract on any OSError, the same path used when no oracle binary is present.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 32ef91adcb docs: record the recent security hardenings in NEWS.md and SECURITY.md
NEWS.md: extend the robustness-hardening section with the second-pass source
audit (hashtable/flist size-computation integer overflows, non-positive
MSG_IO_TIMEOUT reject, async-signal-safe SIGUSR2 handler) and the source-side
xattr/ACL metadata read confinement plus the fake-super cross-tree metadata-apply
fd-pin.

SECURITY.md: note in the operator-directory residual writeup that the cross-tree
metadata apply on a --temp-dir/--backup-dir leaf is fd-pinned the same way the
reads now are, including under --fake-super (previously am_root >= 0 only).
2026-07-20 14:05:32 +10:00
Andrew Tridgell 298efc7fe4 xattrs/backup: read source metadata through a held fd, not a path
The hardened receiver confined the destination side of an xattr/ACL copy (the
fsetxattr/acl_set_fd through a held O_NOFOLLOW fd) but still read the SOURCE side
by path: copy_xattrs() did get_xattr_names/get_xattr_data on the source path, and
make_backup() cached the backed-up file's ACL/xattr via get_acl()/get_xattr() by
path.  A local module writer could race the source/basis parent to a symlink
after the confined content/stat open and before that path-based metadata read,
so out-of-module xattrs/ACLs got copied onto an in-module destination or backup.

Thread a source fd through the read side, mirroring the existing dest-fd plumbing:
 - get_xattr_data(), get_xattr() and get_xattr_acl() gain an fd arg (get_xattr_names
   already had one) and use sys_fgetxattr/sys_flistxattr when fd >= 0; this also
   covers the --fake-super ACL-as-xattr read in get_rsync_acl().
 - copy_xattrs() gains a source_fd; copy_file() passes its held source fd (ifd)
   and keeps it open across the xattr copy (closing it on the fsync error path
   too); gen_entry_copy_xattrs() O_NOFOLLOW-opens the basis leaf under the
   confined resolver (with O_DIRECTORY for a directory basis) and passes it.
 - make_backup() pins the source leaf with a confined O_NOFOLLOW fd
   (backup_source_fd, like set_file_attrs's op_leaf_fd) and reads its ACL via
   get_acl_fdat() and its xattrs via get_xattr(fd); the in-place delta-backup in
   the generator pins fname the same way.  On a hardened receiver a raced/absent
   leaf skips the cache rather than reading through a flippable path.

Non-hardened receivers (fd < 0) keep the path-based behaviour unchanged.  The
basis COMPARE reads (the generator deciding a match) stay path-based: they never
copy out-of-module metadata onto a file, so they are not part of this sink.
2026-07-20 14:05:32 +10:00
Andrew Tridgell c304dd74e4 rsync: pin cross-tree fake-super metadata writes to a confined fd
set_file_attrs() pins a cross-tree operator leaf (an absolute --temp-dir /
--backup-dir / --*-dest path) with an O_NOFOLLOW fd (op_leaf_fd) and drives
chmod/chown/xattr/ACL/times off it so a flipped parent can't redirect them, but
the pin was opened only when am_root >= 0.  A daemon module with "fake super =
yes" runs with am_root < 0, so the cross-tree leaf kept op_leaf_fd/held_fd == -1
and the fake-super %stat, preserved-xattr and ACL-as-xattr writes fell back to
path-based sys_lsetxattr(): a local module writer racing the staging parent to a
symlink could redirect those metadata writes outside the module
(CVE-2026-53799 residual on the fake-super path).

Open the pin for fake-super too -- it has nothing to do with privilege: the
daemon owns the freshly-staged leaf it is about to set metadata on, so any
O_NOFOLLOW open failure is a race and is refused rather than redirected through a
re-resolvable path.  Every metadata op already routes through
held_fd/op_leaf_fd/op_refuse, so they all become fd-based; strace confirms the
cross-tree fake-super write uses fsetxattr()/fchmod(), never the l-variant.
2026-07-20 14:05:32 +10:00
Andrew Tridgell fe3ab9ca9b testsuite: symlink-escape matrix covers change_dir/basis + a real 3.2.7 oracle
The previous matrix only drove --copy-dirlinks/--keep-dirlinks, which route
entirely through send_directory -- the one daemon descent site that already
honoured the opt-out -- so it passed even while explicit-path traversal
(change_dir) and alt-dest basis (basis_link_stat) ignored "insecure links =
yes".  It also asserted a hardcoded contract rather than measuring stock 3.2.7.

Add read-plain/write-plain vectors (a plain pull/push through a symlinked
directory -> change_dir) and a compare-dest vector (--compare-dest=/symlink ->
basis_link_stat), and stand up a second daemon running old_versions/rsync_3.2.7
as a live oracle: every insecure=yes cell must follow iff 3.2.7 follows, and no
insecure=no cell may ever escape the module.  Falls back to a static contract
when the 3.2.7 binary is absent.  Red on the pre-fix build for both the escape
and the opt-out follow gap; green after.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 7b6fbf5940 daemon: make "insecure links = yes" fully restore 3.2.7 symlink following
The opt-out is meant to restore stock-3.2.7 "follow existing symlinks in the
module" behaviour, but several daemon symlink-resolution sites called the
confined resolver unconditionally and never consulted symlink_optout_allowed(),
so a module with "insecure links = yes" still refused to follow a symlinked
directory (change_dir ELOOP) or an alt-dest basis.  Gate every such site on the
opt-out -- follow plainly like 3.2.7 when set, confine otherwise:

  - change_dir() relative daemon branch (util1.c): plain chdir() under the
    opt-out instead of secure_relative_open(), so a peer can read a path through
    a symlinked directory again.
  - basis_link_stat() (generator.c): the chrooted-relative and non-chroot
    branches honour the opt-out.  The non-chroot ABSOLUTE branch (a basis rooted
    under the module by check_alt_basis_dirs, which can reach an in-module
    symlink) is, when NOT opted out, resolved through owner_walk_parent so a
    target landing outside the module root is refused -- closing a confirmed
    --compare-dest=/symlink out-of-module read oracle.  The leaf is taken
    O_NOFOLLOW under the confined parent so --copy-links can't follow a leaf
    symlink out; --fake-super folds its %stat via the held fd.  A relative
    sibling basis (--link-dest=../01) keeps the plain path (#915/#930).
  - secure_basis_open() (receiver.c): plain do_open() under the opt-out.
  - use_secure_symlinks (clientserver.c): cleared under the opt-out, so the
    receiver's protected-regular EACCES write fallback is legacy too.
  - secure_sender_parent_fd() (sender.c): declines (errno=0) under the opt-out
    so --remove-source-files re-stats the plain path.

Default modules (insecure links = no) are unchanged and stay fully confined;
the opt-out is per-module and a client cannot enable it.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 2ae254671c testsuite: guard daemon alt-dest basis confinement to the module
A daemon must clamp a peer-supplied --link-dest/--compare-dest/--copy-dest basis
dir to the served module: --link-dest=../sibling (or an absolute path) must not
let the daemon stat files outside the module (an existence/size oracle -- the
KI-48 / CVE-53795 surface).  rsync confines this lexically already (longstanding
sanitize behavior, preserved through this branch's path-confinement rework); the
test locks that in so the rework can't regress it.  The audit's "runtime-
confirmed" KI-48 was a local receiver, where --link-dest=.. is the operator's
own intended choice, not a module-boundary crossing.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 11d3090730 syscall: drop the dead name_is_dir arg from abspath_excluded_by_module
abspath_excluded_by_module() is module-ROOT confinement only by design (the
daemon name/exclude filter is name-based visibility, not a physical-path
boundary -- munge symlinks is that defense), so its name_is_dir parameter was
always discarded ((void)name_is_dir).  Remove it, and the now-redundant
absent-leaf second call in owner_walk_parent()'s leaf check that re-tested the
SAME leafabs with name_is_dir=1 for an identical result, plus the
fstatat()/isdir/absent that only fed the ignored logic.  Behavior-preserving
(the refuse decision is unchanged at every call site); drops one incidental
lstat.

(Leonid Bugaev May-2026 re-audit, KI-49 -- dead defense-in-depth arm, no escape.)
2026-07-20 14:05:32 +10:00
Andrew Tridgell 4e17fd8505 io, main: make the SIGUSR2 handler async-signal-safe
sigusr2_handler called output_summary() (rprintf/vsnprintf/fwrite/iconv/malloc)
and close_all() (fstat/shutdown/close) directly from signal context before
_exit().  SIGUSR2 is sent by the generator/parent to tell the receiver child to
print its summary and exit; if it interrupted the receiver while it was inside
malloc/stdio, the handler re-entered those and could deadlock or corrupt.

The handler now only sets a flag (got_sigusr2, a volatile sig_atomic_t); the
actual summary + shutdown moves to receive_sigusr2(), run from a safe point in
the receiver's post-transfer wait paths:
 - the perform_io() flag checks (next to got_kill_signal);
 - the safe_read()/safe_write() loops (which a --read-batch / --write-batch fd
   uses without going through perform_io);
 - the whine_about_eof() kluge loop, where the receiver waits out the race of
   the sender dying before the kill-signal arrives -- this loop polls for the
   signal, so without the flag check it slept the full 10s and then errored with
   RERR_STREAMIO instead of exiting cleanly;
 - the trailing `while (!got_sigusr2) msleep()` loop in do_recv().

(Leonid Bugaev May-2026 re-audit, KI-14.)
2026-07-20 14:05:32 +10:00
Andrew Tridgell 63f744adb1 testsuite: regression test for the hashtable integer overflow
Add t_hashtable_overflow (links the real hashtable.o, sets a realistic
--max-alloc, requests an absurd size) and a test asserting hashtable_create now
rejects it with RERR_MALLOC instead of under-allocating and crashing on the OOB
node access a regressed build would hit.  The helper defines its own
info_levels/debug_levels (as the other t_* helpers do) for the DEBUG_GTE macro
in the linked hashtable.o.
2026-07-20 14:05:32 +10:00
Andrew Tridgell 96ff1a0ce2 hashtable, flist: fix integer overflows in size computations
hashtable_create() and the grow path computed the slot-array byte count as
new_array0(char, size * node_size) in 32-bit int arithmetic; for a large
peer/data-driven size the product wrapped to a tiny value, bypassing my_alloc's
--max-alloc guard (which only saw the already-wrapped count), so the table was
under-allocated while tbl->size kept the huge size -- a later node access then
ran out of bounds (heap overflow; ASan-confirmed).  Pass size and node_size as
SEPARATE factors so my_alloc checks both before multiplying; guard each *2
doubling against int overflow BEFORE it happens; make HASH_LOAD_LIMIT divide
before multiplying; promote the HT_NODE index multiply to size_t; and test
size < 16 first so a negative req short-circuits the size-1 (INT_MIN UB).

flist_expand()'s int growth math (used+extra and *=4 / *=2 / += FLIST_LINEAR)
could overflow past INT_MAX on a very large file list; guard each operation
before it overflows and refuse rather than under-size the realloc.

(Leonid Bugaev May-2026 re-audit, KI-11/12/13.)
2026-07-20 14:05:32 +10:00
Andrew Tridgell c158d241d4 testsuite: regression test for MSG_IO_TIMEOUT(0) timeout-disable
Drives a tiny crafted rsync daemon that completes the handshake and sends
MSG_IO_TIMEOUT(0) as its first multiplex frame, then holds the socket open well
past the client's --timeout.  The fix ignores the non-positive value so the
client keeps its timeout and self-exits; a vulnerable client disables its
timeout and hangs until the test's watchdog kills it (test_fail).  Runs in any
transport mode (it connects to the local crafted server over a rsync:// URL).
RED on stock 3.4.x, GREEN on the fix.

(Leonid Bugaev May-2026 re-audit, KI-47.)
2026-07-20 14:05:32 +10:00
Andrew Tridgell 27be267ee3 testsuite: regression tests for the temp-dir injection and copy-dest read-leak
Pin the two cross-uid operator-path races fixed in "confine the remaining
cross-tree operator-path syscalls" and "copy_file: confine an absolute operator
source ...":

  temp-dir-symlink-injection  absolute --temp-dir rename pulls an attacker's
                              out-of-tree file into the destination (do_rename_at
                              absolute-side confinement)
  copy-dest-symlink-readleak  --copy-dest basis read follows a flipped foreign
                              parent symlink, leaking out-of-tree content into
                              the destination (copy_file source confinement, KI-46)

Both root+nobody gated (the cross-uid plant needs root), RED on stock 3.4.x and
under --insecure-links, GREEN on the fix; cygwin runs make check non-root so
they skip there (added to its RSYNC_EXPECT_SKIPPED).
2026-07-20 14:05:32 +10:00
Andrew Tridgell 8d3b7be875 copy_file: confine an absolute operator source via the ownership walk
copy_file() routed a RELATIVE source through secure_relative_open (parents
confined) but opened an ABSOLUTE source -- an operator basis such as an absolute
--copy-dest -- with bare do_open_nofollow, which refuses only a leaf symlink and
follows every parent.  basis_link_stat() refuses a foreign-owned basis at stat
time, but a parent flipped to a foreign symlink between that stat and this open
redirects the basis read out of tree (an out-of-tree content read-leak into the
destination; RED on 3.4.x, GREEN here).

Resolve an absolute source's parents through owner_walk_parent (foreign-owned
parent symlink refused, operator's own dirs/uid0/euid symlinks followed).
operator_path_resolve is set only across the walk -- so module-exclude is
enforced -- and restored, leaving the caller's value for the dest side; that is
why confining the source here does not re-open the copy_xattrs dest race that
wrapping the whole copy_altdest_file would (copy-xattrs-symlink-race stays green).

(Leonid Bugaev May-2026 re-audit, KI-46.)
2026-07-20 14:05:32 +10:00
Andrew Tridgell e7c017914f io: reject a non-positive MSG_IO_TIMEOUT from the peer
A peer may use MSG_IO_TIMEOUT only to ask us to adopt a SHORTER I/O timeout
(a stricter cap).  A crafted server sending val <= 0 would instead zero the
client's --timeout via set_io_timeout(0), disabling it entirely and letting the
server hang the client indefinitely.  Ignore a non-positive value.

(Leonid Bugaev May-2026 re-audit, KI-47; pre-existing since 2009.)
2026-07-20 14:05:31 +10:00
Andrew Tridgell a701236c64 confine the remaining cross-tree operator-path syscalls
The backup-dir fix pinned chmod/chown for an operator leaf; extend the same
confinement to every other sink that re-resolves a cross-tree operator path
(an absolute --temp-dir/--partial-dir/--*-dest), each confirmed by a cross-uid
race PoC that is RED on stock 3.2.7 and GREEN here:

- set_file_attrs (rsync.c): route times (do_futimens), and -- by aliasing
  held_fd to the pinned op_leaf_fd -- the xattr/ACL ops through the same
  O_NOFOLLOW leaf fd, not a re-resolvable path.  A raced/refused pin skips them
  (op_refuse) rather than redirecting.  finish_transfer now wraps the pre-rename
  set_file_attrs in operator_path_resolve so an absolute --temp-dir temp file's
  metadata is pinned too (in-tree temps keep their held dirfd, so op_pin is off).

- do_rename_at / do_link_at (syscall.c): an ABSOLUTE side was left at AT_FDCWD
  and followed a flipped parent symlink, letting a name-disclosed --temp-dir /
  predictable --partial-dir rename pull an attacker file into the destination
  (content injection).  Resolve an absolute (operator) side via the ownership
  walk -- with module-exclude enforced -- while a relative (transfer) side stays
  on secure_relative_open.  --insecure-links keeps the legacy path.

- secure_basis_open (receiver.c): an alt-dest basis read
  (--copy-dest/--compare-dest/--link-dest) on a non-daemon receiver used a bare
  do_open; route it through the ownership walk (refuses a foreign-owned basis
  symlink, still allows the "../sibling" basis of #915).  Daemons keep their
  existing confinement branch.

- configure.ac: probe futimens (do_futimens is gated on HAVE_FUTIMENS).

New fd wrappers: do_fchmod, do_fchown, do_futimens.  Documented residuals left
as-is: copy_altdest_file's basis copy (routing it re-opens copy-xattrs-symlink-
race; basis_link_stat already refuses the foreign symlink), crtimes
(do_setattrlist_crtime/do_SetFileTime, path-based on macOS/Cygwin), and
device/socket leaf metadata (pinning a device via open has side effects).
2026-07-20 14:05:31 +10:00
Andrew Tridgell 0f84b4a776 testsuite: regression test for the --backup-dir trust-laundering race
Pins the fix in "backup: confine cross-tree operator-path metadata via a pinned
fd".  A root operator runs `rsync -a -b --backup-dir=<abs> ...` while a non-root
attacker flips a backup parent component between a real dir and a foreign-owned
symlink -> outside; pre-fix, rsync's own backup-dir attribute mirroring lchowns
the planted symlink to root, laundering it into a trusted symlink the owner-walk
then follows, so the backup escapes the tree.

Root+nobody gated (cross-uid plant needs root); RED on stock 3.2.7 and under
--insecure-links, GREEN on the fix.  Uses the compiled flipper for a reliable
RED oracle.  cygwin runs make check non-root so the test skips there -- add it to
that workflow's RSYNC_EXPECT_SKIPPED; the root workflows (almalinux-8 container,
sudo ubuntu/macos) run it for real.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 334aacb448 testsuite: add an on-demand compiled TOCTOU flipper
The Python path-flipper used by the symlink-race tests wins the race window
unreliably on a journaled disk fs -- on the vulnerable binary it reproduced the
escape only ~1/3 of the time, because the interpreter loop caps the swap rate.

Add compile_c_flipper()/start_c_flipper(): a small C flipper, built on demand
against the build's config.h (CC and -I taken from TOOLDIR, then SRCDIR), that
swaps two sibling names with renameat2(RENAME_EXCHANGE) where available -- one
atomic syscall, no transient missing-name window -- and a self-healing 3-rename
fallback elsewhere.  Measured ~2x (plain rename) to ~7x (EXCHANGE) the swap rate
on disk, which turns a flaky RED oracle into a reliable one.  It self-terminates
on parent exit plus a deadline backstop (like start_path_flipper) so a killed
test can't leak an orphan, and falls back to the Python flipper where no
compiler is available.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 1624b6daac backup: confine cross-tree operator-path metadata via a pinned fd
A symlink race on a non-daemon --backup-dir let a local attacker redirect
rsync's backup writes and chmods outside the backup tree when rsync runs as
root.  make_backup() sets operator_path_resolve, but set_file_attrs() had no
held parent dir fd for an absolute backup path (held_dfd_for() returns -1), so
its chmod/chown fell through to the path-based wrappers and, for an absolute
path, to raw chmod()/lchown().  copy_valid_path() mirrors the source dir's
attrs onto each backup subdir; when an attacker flips a backup component to a
symlink in that window the raw lchown retags the planted symlink as root-owned
-- laundering it into a "trusted" (uid 0) symlink that the owner-walk then
follows, so the backup rename/chmod escapes the tree.

Pin the leaf inode of a cross-tree operator path with an O_NOFOLLOW open via
the operator owner-walk resolver and drive fchmod/fchown off that fd; a raced
symlink leaf makes the open fail and the op is refused, never redirected.  Gate
on the INTENDED type (new_mode), not the attacker-controlled on-disk type.  As
root any open failure is the race (a real owned leaf never fails); a non-root
operator, which cannot launder a uid-0 symlink, falls back to the legacy path
op on a benign EACCES.  --insecure-links opts back out.

Adds do_fchmod()/do_fchown() fd wrappers.  Residual cross-tree metadata sinks
(times, ACLs, xattrs, and --temp-dir's finish_transfer set_file_attrs) are not
covered here and are tracked for a follow-up.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 33beee7652 docs: correct vague/wrong option descriptions
Tighten man-page prose for options whose documented behaviour was
imprecise or outright wrong, matching the behaviour now pinned by the
new oracle tests and verified against the C source:

- strict modes: the real secrets-file rule is "st_mode & 06" (other
  read/write) plus root-owner-when-root, so 640 is accepted and 644
  rejected -- not the old "any user ID other than the daemon's".
- munge symlinks: rewrite the default in terms of chroot and the "/./"
  path split (disabled only for a plain chrooted module serving the
  chroot root); drop the bogus "daemon chroot" clause. Fix the helper
  reference: support/munge-symlinks is a python script, not perl.
- --no-implied-dirs: spell out that an existing in-tree dest symlink is
  followed.
- --files-from: ".." handling is collapse-then-reject-survivors.
- --copy-unsafe-links: describe the lexical unsafe-symlink rule instead
  of the old "verbose output" phrasing.
2026-07-20 14:05:31 +10:00
Andrew Tridgell e927a00b2e testsuite: pin documented option behaviour with 8 oracle tests
Add behaviour tests that nail down option semantics the man pages
describe vaguely, each verified to pass against both this branch and the
3.2.7 oracle (so they document long-standing behaviour, not regressions):

  daemon-strict-modes-matrix   secrets-file mode rule (st_mode & 06)
  daemon-chroot-munge-default  munge-symlinks default vs chroot/path /./
  safe-links-unsafe-def        --copy-unsafe-links lexical unsafe rule
  no-implied-dirs-symlink      --no-implied-dirs follows in-tree dest symlink
  files-from-path-clamp        --files-from collapse-then-reject ".."
  relative-implied-symlink     --relative sends implied dirs as real dirs
  keep-dirlinks-rule           --keep-dirlinks opening rule
  backup-dir-relative          --backup-dir resolves relative to dest

no-implied-dirs-symlink relies on the -R "/./" implied-dir marker, a
protocol-30+ feature; under protocol 29 the generator rejects the
multi-component path (same as the 3.2.7 oracle), so it passes through
without testing, matching the sibling relative-implied test.

daemon-chroot-munge-default needs root to exercise the chroot regimes
and skips otherwise; add it to RSYNC_EXPECT_SKIPPED only in the
almalinux-8 and cygwin workflows, which run make check non-root. The
macos workflow runs it as root, so the test runs there for real.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 2d10070c90 SECURITY.md: broaden the robustness-pass example list
The "Robustness against malicious peers" summary enumerated the classes of
peer-triggerable faults closed by the fuzzing/static-analysis pass. Add the two
classes that the later scanner batch introduced -- reads past a file-list
allocation (mostly bounded over-reads of an entry's extra slots) and
option-argument-driven length bounds (plus the suffix-list recursion sink) --
keeping the summary general (no per-finding detail; not every over-read
disclosed memory).
2026-07-20 14:05:31 +10:00
Andrew Tridgell cd22f195ef testsuite: lock down the in-module symlink-escape resolution matrix
daemon-symlink-escape-matrix exercises, for a writable non-chroot module, every
combination of `insecure links` {no,yes} x `munge symlinks` {no,yes} x link
origin {pre-existing, uploaded} x op {read pull, write push} x five symlink
target types (rel-within, rel-outside, rel-transits [.. above the module root
then back in], abs-outside, abs-inside).

It pins the contract: the secure default follows only an in-tree (rel-within)
link and NEVER reaches an out-of-module target (read or write); the
`insecure links = yes` opt-out restores legacy following on sender AND receiver
(so an outside target escapes, matching stock 3.2.7); and an uploaded link never
escapes regardless (munge prefixes it, munge-off sanitises it).  A secure-default
out-of-module access is a hard failure.  require_tcp + root gated; listed in the
per-platform RSYNC_EXPECT_SKIPPED pipe make-check sets.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 502fb584c4 daemon: insecure links opt-out restores legacy symlink following on the receiver
secure_relpath_active() (the gate that routes receiver-side filesystem ops --
get_dir_fd/dpc, do_*_at, open_tmpfile, make_path, link_stat -- through the
symlink-race-safe resolver) checked only am_daemon/am_chrooted/am_sender, not
the symlink_optout_allowed() opt-out.  So `insecure links = yes` (or a non-daemon
--insecure-links) restored the legacy follow only on the SENDER enumeration
(which checks the opt-out directly), while the receiver still confined writes,
mkdirs, renames, unlinks and stats through a pre-existing in-module symlink --
i.e. the admin opt-out did not actually reproduce the pre-3.4.3 behaviour it
documents (rsyncd.conf(5) "munge symlinks"/"insecure links").

Have secure_relpath_active() return 0 when symlink_optout_allowed(), so the
opt-out uniformly disables the secure resolver on both sides.  No effect on the
default (opt-out off): confinement is unchanged.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 68ff15e35c testsuite: daemon-proxy-protocol drop-leg tolerates EPIPE on send
The probe() helper sends the @RSYNCD greeting after the PROXY header, then
reads the daemon's response.  For a `want='drop'` leg (untrusted peer / a
daemon with no `proxy protocol hosts`) the daemon closes the connection,
which on some CI runners surfaces as EPIPE/ECONNRESET on our sendall() before
we ever read -- an uncaught BrokenPipeError that failed the test (seen on
AlmaLinux 8 and Ubuntu 22.04, a timing race; other runners closed read-side).
Wrap the send/recv in `except OSError` and leave `out` empty: for want='drop'
the absent greeting is the expected outcome; want='ok'/'denied' still fail
correctly on an absent greeting.
2026-07-20 14:05:31 +10:00
Andrew Tridgell fe69ae7a7d github: register new audit tests in the expected-skip lists
The four tests added with the audit fixes skip on the standard (non-ASan,
stdio-pipe) CI/fleet runs: daemon-deny-dns-failopen needs a TCP peer
(require_tcp), and the three leak reproducers need an AddressSanitizer build
(require_asan).  Add them to RSYNC_EXPECT_SKIPPED so make check / the fleet
report clean instead of flagging an expected skip as a mismatch.

(cherry picked from commit 272341682b668424e1f87fd1e8f8a5878db272c2)
2026-07-20 14:05:31 +10:00
Andrew Tridgell 5eb05f7409 flist/xattrs/generator/uidlist/clientserver: plug audit-reported leaks
Five error-path/cleanup memory leaks found by an external audit:

- flist.c send_file_name: free the ACL loaded by get_acl() when a later
  get_xattr() fails (and on the get_acl error path).
- xattrs.c copy_xattrs: free the xattr datum buffer when the setxattr fails.
- generator.c recv_generator: free real_sx at the cleanup label (the
  directory branch loaded its ACL via set_file_attrs but only the
  regular-file path freed it); zero-init real_sx so the early gotos are safe.
- uidlist.c send_one_list: free the strdup'd id-0 name after send_one_name.
- clientserver.c start_inband_exchange: free modname on the early error
  returns (it was freed only on the success path).

ASan/LSan regression tests cover the generator, uidlist and clientserver
leaks; the flist and xattrs leaks need a forced syscall failure and are
covered by the audit's standalone harnesses.

Reported-by: Leonid Bugaev <leonsbox@gmail.com>
(cherry picked from commit 078f3b99f4004510d418ee9d97d9b775dc8587bc)
2026-07-20 14:05:31 +10:00
Andrew Tridgell 054d0eb475 access: fail closed when a hosts-deny token can't be resolved
match_hostname() did a forward-DNS lookup of a config-specified hostname
token and, on gethostbyname() failure, returned "no match" -- which
allow_access() cannot distinguish from a real non-match, so a daemon with
"hosts deny = <hostname>" silently admitted the host whenever the token
could not resolve (a resolver-less chroot, or a transient DNS failure).
No attacker DNS control required.

Thread a deny flag through access_match()/match_hostname() and, on a
forward-DNS failure, treat an unresolvable DENY-list token as a match so
the connection is refused (fail closed); allow-list tokens still fail as a
non-match. Sibling of CVE-2026-43617, which fixed only the reverse path.

Reported-by: Leonid Bugaev <leonsbox@gmail.com>
(cherry picked from commit 84e469ea03a98ef31326d3d861336d7e7d582ce1)
2026-07-20 14:05:31 +10:00
Andrew Tridgell 21ddac1a67 testsuite: rsync_ls_lR tolerates non-UTF-8 bytes under Python 3.14
Python 3.14's strict UTF-8 text mode raised UnicodeDecodeError when tls
emitted a non-UTF-8 byte (a filename or symlink target with high bytes),
aborting every test that calls rsync_ls_lR via hands_setup() -- ssh-basic,
hands, delete, files-from, alt-dest, daemon-gzip-*.  Decode the tls output
with errors='backslashreplace' so a stray high byte renders as \xNN in the
listing rather than crashing the run; the result stays a clean str that
write_text()/print() can consume without re-raising.
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman 3d3150c24a testsuite: code-scanner daemon/metadata coverage tests + gcov flush before chroot
Adds six coverage tests from the code-scanner run, each closing a measured
gap in a daemon or metadata code path the suite never reached:

  - daemon-include-maxconn: rsyncd.conf &include/&merge directives +
    `max connections`/`lock file` (params.c include_config, connection.c
    claim_connection, util1.c lock_range).
  - fake-super-acl-xattr: --fake-super -A stores ACLs as user.rsync.%aacl/
    %dacl xattrs (acls.c am_root<0 IVAL/SIVAL pack, xattrs.c get/set/
    del_def_xattr_acl); Linux-only (the user.rsync.* namespace).
  - backup-crossdev-copy: make_backup() EXDEV copy-fallback for non-regular
    files (do_symlink_at/do_mknod_at/copy_file); skips without a cross-dev
    tmpfs.
  - daemon-http-proxy: RSYNC_PROXY HTTP CONNECT (socket.c
    establish_proxy_connection + base64 Proxy-Authorization + 503 branch).
  - daemon-module-options: motd file, socket options, incoming/outgoing
    chmod, dont compress, list=no, comment, --sockopts.
  - daemon-chroot: `use chroot = yes` incl. the /outer/./inner split and
    `temp dir`; probes CAP_SYS_CHROOT and skips cleanly without it.

clientserver.c flushes gcov counters just before chroot() in rsync_module()
so the per-connection child's pre-chroot lines reach disk (the build-tree
.gcda paths are unreachable post-chroot); no-op without --enable-coverage.

CI: the require_tcp-gated tests (daemon-chroot/-http-proxy/-module-options)
plus the Linux-only fake-super-acl-xattr and the cross-dev backup-crossdev-copy
are listed in the per-platform RSYNC_EXPECT_SKIPPED sets where they skip on the
pipe-transport make-check jobs.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew Tridgell fe1adcfd73 syscall: do_mknod_at must mkfifoat for an operator-path FIFO on non-Linux
The operator_path_resolve branch of do_mknod_at() called mknodat() and
returned its result directly, without the FIFO/socket fallback that the
bare-path do_mknod() and the secure-relpath branch below it both have.
mknodat() can make a FIFO only on Linux; on the BSDs/macOS/Solaris it fails
with EINVAL, so creating a special file under an operator-supplied path --
e.g. backing up a FIFO into a --backup-dir -- failed there.  Retry race-safely
with mkfifoat() on the held parent dirfd, and fail a nested socket closed
(EOPNOTSUPP) exactly as the secure-relpath path does.
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman a3de9e553d io/xattrs/rsync-ssl: EOF-sentinel guard, xattr ndx guard, gnutls CA refusal
io.c only treats a short read as the EOF sentinel when the fd is still
open; xattrs.c never stores a -1 from find_matching_xattr() and guards
ndx < 0 in set_xattr; rsync-ssl refuses the gnutls backend without
RSYNC_SSL_CA_CERT.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman 81850e8b27 options/token: bound the -v repetition, output level, and suffix token
Cap -v repetition so the argstr[64] global can't overflow, clamp a
negative --info/--debug level out of counts[], and cap a --skip-compress
suffix token at 32 bytes.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman 49cb45adcc compat: assign metadata ndx after batch flags; pick own preferred name
Move the uid/gid/acls/xattrs *_ndx assignments past check_batch_flags():
a mismatched-flag batch otherwise wrote F_XATTR(file) at offset 0 of
every file_struct, clobbering file->dirname. parse_negotiate_str() no
longer short-circuits on am_server, so each side picks its own #1 mutual
digest/checksum/compress choice rather than deferring to the peer's
order; man pages updated to match.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman 7e58170a9c generator: fix off-by-one length in read_delay_line()
A '!'-prefixed delete-delay entry computed one byte short, dropping the
final character of the name.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman aed7714325 hlink/main: guard the F_SUM checksum-slot lifetime
hlink.c must confirm S_ISREG before quick_check_ok(FT_REG,...) reads
F_SUM, and start_server() must set sender_keeps_checksum when a daemon
sender runs -c with a %C log format so make_file() allocates
SUM_EXTRA_CNT. Without these, F_SUM() reads past the pool slot and (for
%C) hex-encodes adjacent heap into the transfer log.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman 853050d25d flist/receiver: zero/empty the device & symlink extra slots
recv_file_entry (normal and XMIT_HLINKED abbrev branches) and make_file
left F_RDEV_P / the symlink-name slot uninitialized when the matching
preserve option was off, so a later read walked into adjacent pool
memory. Empty the symlink name when !preserve_links, zero F_RDEV_P when
!preserve_devices, and mirror both in the abbreviated branch. receiver.c
saves/restores the --write-devices S_IFBLK mode flip around receive_data
so dest_mode() never sees the mutated mode.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew TridgellandGreg Kroah-Hartman 0abb4ae6cb testsuite: code-scanner coverage and regression tests + gcov infra
Adds the coverage/regression tests from the code-scanner run and the
gcov plumbing they rely on:

  - scanner-argv-bounds, scanner-batch-flag-mismatch,
    scanner-delete-delay-overread, scanner-daemon-log-checksum:
    regression tests for the argv/-v/--info/--skip-compress bounds, the
    batch metadata-ndx corruption, the read_delay_line off-by-one, and
    the daemon -c/%C checksum-slot leak.
  - daemon-proxy-protocol, daemon-early-exec-nameconv, daemon-auth-group,
    daemon-standalone-detach, misc-coverage, nonroot-restrictive-perms,
    backup-acl-xattr-cache: daemon and path coverage tests.
  - rsyncfns.py: CAP_MKNOD probe in devices_supported().
  - gcov_flush() macro (rsync.h) + calls in the daemon fork/_exit paths
    (clientserver.c, socket.c); no-op without --enable-coverage. Makefile.in
    COVERAGE_EXCLUDE / gcovr / setuid .gcda refinements.
  - CI: list the new TCP/root/ACL tests in the per-platform
    RSYNC_EXPECT_SKIPPED sets.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2026-07-20 14:05:31 +10:00
Andrew Tridgell ce17dfe8f0 fleettest: per-target xfail list; tolerate crtimes on macOS
Add a per-target `xfail` field (merged with the global --xfail) so a known
platform/version-specific failure can be tolerated persistently without a
command-line flag -- the test still runs, and if it passes the entry is a no-op.

Mark crtimes xfail on mac2: older backport binaries (3.4.x/3.2.7) drive APFS
birthtime via setattrlist differently than current rsync, so the 3.5.0
testsuite's crtimes check fails there; it passes for a current binary.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 0071ba5be2 runtests: allow the variety test 600s, like hardlinks
variety is the heaviest test in the suite; on slow platforms (Cygwin) the
per-component O_NOFOLLOW resolver pushes it past the default 300s per-test
timeout.  Give it the same 600s budget the hardlinks test already gets.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 5d1b15086d syscall: give the dirstack a fixed fd array instead of a malloc'd one
The race-safe resolver's dirstack grew its fd array with realloc() inside the
recursive ds_descend() walk and freed it via a passed pointer.  clang's
unix.Malloc analyzer cannot model that ownership and reported a false "Potential
leak of ds.fds", failing the pinned-clang-18 scan-build gate.

The walk holds one open fd per path component, so its depth is already bounded
by RLIMIT_NOFILE; use a fixed inline array (DS_MAXDEPTH, mirroring DPC_MAXDEPTH)
and drop the malloc/realloc/free entirely.  ds_push() fails with ENOMEM past the
cap.  No behaviour change -- 1024 levels exceeds any reachable depth.
2026-07-20 14:05:31 +10:00
Andrew Tridgell d1d3829901 docs: man pages + README
--insecure-links and the SECURITY sections in rsync(1)/rsyncd.conf(5), the
name-based exclude/filter + munge-symlinks clarification, the rsync-ssl hostname
note, and the README (incl. the thank-you to Wayne Davison, 2004-2024).
2026-07-20 14:05:31 +10:00
Andrew Tridgell 3c674b7fdf docs: NEWS + SECURITY.md for the 3.5.0 security release
NEWS.md: the 3.5.0 security-update section.  SECURITY.md: the platform-residuals
policy, symlink-race-safe path resolution, operator-path symlink defense, the
name-based daemon exclude/filter clarification, and the known residuals.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 065ec23e4b testsuite: fleettest VM harness + version-mix expect manifests
testsuite/fleettest.py builds the branch and runs the suite across a fleet of
remote VMs (BSDs/Solaris/Ubuntu/macOS/Cygwin) over multiple transports, with a
--cleanup that reaps orphaned daemons; the expect/*.expect files are the
version-mixing manifests (current rsync vs old static peers).
2026-07-20 14:05:31 +10:00
Andrew Tridgell 161608034e ci: scan-build gate (pinned clang-18) + informational latest-clang
Gate the build on a pinned clang-18 analyzer run (deterministic checker set,
--status-bugs fails on any new report) and run the latest clang informationally.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 885a0a8ecc ci: per-platform build workflows + RSYNC_EXPECT_SKIPPED baselines
Run the security test suite (pipe + real-TCP daemon transports, proto30/29, and a
targeted non-root pass) across Ubuntu/macOS/Cygwin/AlmaLinux, with per-platform
expected-skip baselines for the tests that legitimately skip there.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 32aa900fd0 testsuite: rrsync, rsync-ssl, batch, backup and misc
rrsync restriction tests, rsync-ssl hostname/CA validation, write-batch quoting/
filter-injection, backup-incremental, delay-updates, variety and xrsync.
2026-07-20 14:05:31 +10:00
Andrew Tridgell ae014a56f3 testsuite: daemon auth / access / proxy / config hardening
auth-digest floor, namecvt empty/newline, proxy-protocol (CRLF, trusted-peer,
over-long lines), connect-prog/exec/remote-shell quoting, unix-socket atfd, and the
daemon scan cwd/dir-escape tests.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 72098206ce testsuite: daemon protocol robustness against malicious peers
The malformed-peer fuzz/crash regression tests: io flood/argv, proto cleared-flist/
ndx/hlink/selftest, match nullmap, max-alloc / size-arg / argv limits, sum-blocklen,
nested-socket specials, malicious-sender delete scope, xattr-wire-cap.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 493a8e99d6 testsuite: ACL / xattr / special-file metadata
acl-symlink-race, acls-unpinnable, xattrs-hlink, and the chown/devices fake-super
metadata tests.
2026-07-20 14:05:31 +10:00
Andrew TridgellandOmar Elsayed a55ab05c19 testsuite: daemon exclude/filter name-based behaviour
The exclude/filter is a name filter, not a symlink boundary (3.2.7-equivalent):
symlink-exclude family, daemon-exclude-namebased, the operator-path exclude /
traversal / dir-daemon cases, filter-merge and implied-trailing-backslash.

Co-authored-by: Omar Elsayed <omarelsayed161@gmail.com>
2026-07-20 14:05:31 +10:00
Andrew TridgellandOmar Elsayed 4705b6fb92 testsuite: symlink-race and operator/peer path-resolution coverage
The TOCTOU / symlink-race suite for the secure resolver and operator-supplied
paths: chdir/chmod/rename/mknod/source/dest symlink races, relative make_path and
symlinked-parent cases, the operator-path matrix (--temp/partial/backup-dir,
alt-dest basis, files-from, log-file, insecure-links), and the admin-file opens
(--password-file / daemon secrets / config / log-file / early-input symlinks),
plus the daemon module-confinement and chroot inner-module cases.

Co-authored-by: Omar Elsayed <omarelsayed161@gmail.com>
2026-07-20 14:05:31 +10:00
Andrew Tridgell 1346bc623f testsuite: shared harness, helpers and unit-test programs
The t_rename_secure / t_symlink_secure / t_acl unit harnesses (C), the rsyncfns.py
helper library (daemon fixtures, symlink matrix, tree compare, xattr/ACL drivers),
runtests.py, and the rsync_proto/xrsync/cmptree/mkvariety helper scripts that the
security tests build on.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 88cee08963 rrsync: pin path components against a TOCTOU and fail closed on anomalies
Pin each validated path component (and a receiver-side new destination's parent)
with O_RDONLY|O_NOFOLLOW and pass /proc/self/fd/N to the exec'd rsync so the child
cannot re-resolve the path; probe the /proc/self/fd magic-symlink at runtime (not
just isdir); fail closed on a readlink anomaly; and don't abort when flock() is
unavailable (Solaris).
2026-07-20 14:05:31 +10:00
Andrew Tridgell 1604890301 batch/rsync-ssl: quote replay-script args and bind the SSL cert hostname
batch.c: single-quote every --write-batch replay-script argument, quote a "--opt="
prefix unless it is a plain option token, and refuse a newline in a filter rule
written to the replay script.  rsync-ssl: bind the server certificate to the
requested hostname in stunnel mode.
2026-07-20 14:05:31 +10:00
Andrew Tridgell d199b43149 daemon: harden authentication, access, socket and config-hook handling
authenticate.c: seed gen_challenge() from /dev/urandom, add an "auth digest" floor
to refuse weak negotiated digests, and fstat the opened --password-file fd rather
than re-stat the pathname; checksum.c carries auth_digest_rank().  socket.c: reject
control bytes in the daemon host before a proxy CONNECT and bind the stunnel server
cert to the requested hostname.  clientserver/access: warn when proxy-protocol
fail-closes.  loadparm + daemon-parm: only shell-quote %RSYNC_*% for shell-executed
hooks, and add the auth-digest / proxy-protocol-hosts module parameters.
2026-07-20 14:05:31 +10:00
Andrew Tridgell 3eeedf8d08 daemon: robustness against malicious-peer protocol input
Refuse malformed/hostile wire input that could crash or corrupt the receiver:
io.c (out-of-range file index, count*blength OFF_T overflow, read_args NUL room,
deferred in_multiplexed), flist.c (sub-flist after the final flist is freed,
FLAG_HLINKED on dirs / gated on preserve_hard_links, parent_ndx bound, cleared-
slot ndx in the transfer phase), hlink.c (undeclared cross-flist gnum -> error not
assert), match.c (clamp peer flength, re-check len before want_i), log.c (drop
peer-reachable asserts and F_SUM deref), exclude.c (merge-file recursion cap,
trailing-backslash heap fix), lib/pool_alloc.c (ASan redzone for pool underflow).
2026-07-20 14:05:31 +10:00
Andrew Tridgell b62d7ed6d4 acls/xattrs: race-safe fd-based metadata application
Apply ACLs and xattrs through a held file descriptor instead of by path, closing
the symlink-race where an attacker swaps the leaf between the transfer and the
metadata set.  lib/acl.c provides fd/at POSIX-ACL primitives (the system libacl
*_at where available, else a /proc/self/fd compat that never follows on the
fallback); acls.c routes through them and stays functional (path-based) where the
OS lacks a race-safe primitive; xattrs.c routes copy_xattrs through a held fd; -VV
(usage.c) reports the runtime race-safe-ACL capability.
2026-07-20 14:05:30 +10:00
Andrew Tridgell dd3e6bfece daemon: treat exclude=/filter as a name filter, not a symlink boundary
The daemon exclude/filter chain is a name-based visibility/tamper filter, as in
stock rsync (verified against 3.2.7): a symlink whose own name is not excluded is
followed to an excluded target, and the documented symlink defense is `munge
symlinks`, not the filter.  Collapse ".." (via sanitize_path) before the daemon
dest / temp-dir / backup-dir / partial-dir / basis filter checks so a "../excluded"
path is matched by name like 3.2.7 (clean_fname's CFN_COLLAPSE_DOT_DOT_DIRS does
not collapse "a/b/c/../../../secret"), and keep a leading "/" for a "path = /"
module so an absolute filter rule still matches.  The module-ROOT confinement of
operator paths is unchanged (previous commit); only the in-module name match is
restored to its 3.2.7 behaviour.
2026-07-20 14:05:30 +10:00
Andrew Tridgell defd7110ac confine operator- and peer-supplied paths to the served module
Route every operator-supplied directory path (--temp-dir / --partial-dir /
--backup-dir / alt-dest basis) and the transfer engine's own dest/source opens
through the secure resolver + ownership walk, so a symlink owned by another uid
can no longer redirect a read, a backup, a staging open, a rename, an unlink or a
new-destination create outside the module.  Covers backup.c, generator.c (alt/
link/in-place basis), receiver.c (basis open), sender.c (remove-source / source
open / copy-links leaf), rsync.c (held-fd attr stat), clientserver.c (pid-file
parent pin), and main.c (relative-basis make-absolute, mkpath dest-arg guard).
--insecure-links / "insecure links = yes" is the local opt-out (a daemon never
honors a peer-forwarded one).
2026-07-20 14:05:30 +10:00
Andrew Tridgell 54965efca9 syscall/util1: race-safe path resolution via a held dirfd-stack resolver
The core symlink-race (TOCTOU) defense for the CVE-2026-29518 class: a portable
secure resolver that walks a path one component at a time holding an O_NOFOLLOW
dir fd per level (the dirfd-stack), plus the do_*_at() filesystem wrappers, the
held-directory fd cache, secure_relative_open[_at](), the operator-path ownership
walk (owner_walk_parent / open_no_attacker_symlinks, follow a uid0/euid symlink,
refuse a foreign one) and its module-root confinement (abspath_excluded_by_module).
util1.c routes change_dir / robust_rename / make_path / handle_partial_dir through
it; the resolver bounds its deep-path fd use against RLIMIT_NOFILE.  Also drops the
obsolete android.c openat2 path and confines delete.c via the held dirfd.
2026-07-20 14:05:30 +10:00
Andrew Tridgell adab781538 build: wire up the secure resolver, the ACL fd helper, and the test harnesses
configure.ac: detect fdopendir + a working dirfd() (a macro/inline on the BSDs,
so the default link probe mis-detects) and getrlimit/setrlimit for the resolver's
deep-path fd budget; add the race-safe-ACL build probes.
Makefile.in: build lib/acl.o; build the new t_rename_secure / t_symlink_secure /
t_acl unit harnesses; drop the obsolete android.o object.
mkgitver: version a git build by the dev version + commit (not the nearest tag),
and harden the version.h parse.
2026-07-20 14:05:30 +10:00
Andrew Tridgell 3b84610ccb manpage: clarify remote-shell daemon user@ handling
The description of user@host::module transfers over a remote shell only
documented the "ssh -l ssh-user" form, which led readers to conclude that
user@ never reaches the remote shell. In fact, for the simple
`--rsh=ssh user@host::module` form the user@ prefix is used both as the
ssh login user (ssh -l user) and as the rsync-user offered to the module;
the two are the same name. rsync only omits its own -l when the remote
shell command already specifies one, in which case user@ becomes the
rsync-user alone.

Spell out the default behaviour and why the explicit -l is needed to use
a different ssh login than the rsync-user.
2026-07-20 14:04:27 +10:00
Zen Dodd 9062840a91 ci: restore default build target 2026-07-20 10:05:43 +10:00
Zen Dodd 3c9a12011e ci: fix no-AT_FDCWD compile check 2026-07-20 10:05:43 +10:00
Zen Dodd 5cb4b8290b syscall: build without AT_SYMLINK_NOFOLLOW 2026-07-20 10:05:43 +10:00
Stuart Inglis e6956e0a30 match: bound the hash_search() chain walk (issue #217)
hash_search() walks the entire hash-table chain for the current rolling
checksum at every byte offset of the source file. Disk and VM images
contain large runs of identical blocks, so a single weak checksum
(get_checksum1) can collide thousands of times and pile every one of
those blocks onto one chain. When the sender then rolls across a region
whose weak checksum keeps landing on that chain without ever producing a
strong-checksum match, it re-walks the whole chain for every byte, giving
O(file_size * chain_length) behaviour. The result is rsync sitting at
100% CPU for hours with no apparent progress -- the long-standing "rsync
hangs on large files" reports.

Cap the number of same-weak-checksum candidates examined per offset at
MAX_CHAIN_LEN. Once the cap is hit we treat the offset as a non-match and
roll forward a byte; any block skipped this way is simply sent as literal
data, so the transferred result is always correct -- only the transfer
size is marginally affected. This is purely a sender-side search limit:
it changes no checksum, emitted byte, or protocol field, so a capped
sender interoperates with an unmodified receiver and vice versa.

On a synthetic 40000-block basis sharing one weak checksum, syncing a
60KB source dropped from ~18.4s to ~0.7s; the unbounded cost grows with
the square of the file size.

testsuite/hashsearch-chain_test.py reproduces the pathology with a tiny
basis of weak-checksum-colliding decoy blocks and asserts, via the
existing false_alarms counter (--debug=deltasum1), that the per-hash-hit
chain walk stays bounded. The assertion is exact and machine-independent
rather than timing-based.
2026-07-20 07:43:45 +10:00
Zen Dodd e2a24e8581 testsuite: C23 bool compatibility 2026-07-20 07:27:22 +10:00
AlphaGlider25 f5fa55672d Fix Solaris xattr retry handling
Use the remaining byte count for retry writes and avoid using a
size_t sentinel for write failures.
2026-06-21 14:14:40 +10:00
Andrew Tridgell 5553271274 ci: run scan-build on pinned clang-18 + latest clang (informational)
Split the scan-build workflow into two non-gating jobs, each uploading
its HTML report as an artifact:

- pinned-clang18: clang-18 / clang-tools-18 on ubuntu-24.04, so the
  checker set -- and thus the report -- is deterministic.
- informational-latest: whatever clang ubuntu-latest ships, to surface
  what newer analyzers see.

Both are informational (no --status-bugs): the tree still has known
clang-18 findings, so the run reports without blocking the build.  Once
the tree is at zero for clang-18, re-add --status-bugs to the pinned job
to turn it back into a gate.  Installs libpopt-dev so configure finds
popt under the scan-build compiler wrapper.
2026-06-16 08:55:39 +10:00
Andrew Tridgell 3f5884a3bb scan-build: close a test-helper FILE* leak
wildtest.c: close the test file before main() returns (a real, if
exit-benign, FILE* leak flagged by scan-build).
2026-06-16 08:55:39 +10:00
Andrew Tridgell 4e67f87479 scan-build: fix resource leaks on error paths
clientserver.c: close the --early-input-file FILE* on the
fstat/oversize/early-EOF error returns; it was only closed on the
success path.
getgroups.c: free the gid list before returning.
2026-06-16 08:55:39 +10:00
Andrew Tridgell 8118744f2a scan-build: drop dead assignments
Remove stores that are never read before being overwritten or going
out of scope. No behavior change except batch.c write_opt, which now
accumulates the leading-space write error into the return value
(consistent with the arg branch) instead of discarding it.

simd-checksum-x86_64.cpp, options.c, util1.c, batch.c
2026-06-16 08:55:39 +10:00
Andrew Tridgell 412cddf6be scan-build: zero-init buffers the analyzer can't prove are written
clang's static analyzer doesn't model SIVAL/SIVAL64/SIVALu or
getpeername/getsockname as initializing their target bytes, so it
reports false "garbage value" reads. Zero-init the affected buffers;
the bytes are always overwritten at runtime, so this only quiets the
analyzer.

io.c:     write_varint/write_varlong b[]
hashtable.c: hash_search buf[]
socket.c: accepted_peer/our_local
2026-06-16 08:55:39 +10:00
Greg Kroah-Hartman 399cf1aa5d generator: fix build warning
in sum_sizes_sqroot() cnt is assigned a variable but never actually
used, so remove it entirely as it's not needed anymore.
2026-06-16 05:54:17 +10:00
Andrew Tridgell fe93ffcd65 fix funding github username 2026-06-15 13:58:46 +10:00
Andrew Tridgell a27095f0e1 Create FUNDING.yml 2026-06-15 13:53:04 +10:00
Andrew Tridgell 14ab548efc runtests: write valgrind logs to a world-writable subdir
Under --valgrind some tests run rsync with reduced privileges: partial_nowrite
wraps it in "setpriv --inh-caps -all --bounding-set -all" to force EACCES, and
chdir-symlink-race's daemon drops to the module's uid.  Such a child cannot
create valgrind's --log-file in a root-owned scratchbase, so valgrind aborts at
startup and the test fails (seen only in the root + --use-tcp cell).

Put the logs in a 1777 valgrind-logs/ subdir so a privilege-dropped child can
always write them.  Scan and cleanup are unchanged; the logs just move one
directory down.
2026-06-14 08:33:03 +10:00
Andrew Tridgell 7bebcee20f generator: don't read an unstat'd sx.st when creating a device/special
this fixes a valgrind error where we could read an uninitialised sx.st
field when we don't fill the stat data.

Also drop the now-obsolete testsuite/valgrind.supp stanzas for these
reads (atomic_create/delete_item, plus the rwrite strlcpy over-read that
master already fixed) -- they are no longer needed now the reads are gone.

Thanks to report from Michael Mess <michael@michaelmess.de>
2026-06-14 08:33:03 +10:00
Andrew Tridgell e9eda5d5df wildtest: don't read past the buffer when scanning a test line
main()'s line parser stepped through the fgets() buffer with `*++s` in
three places without first checking for the terminating NUL, so a test
line whose last token runs to the end of the buffer (e.g. a final line
with no trailing newline) could advance s past the NUL and read out of
bounds.

Guard the flag-separator check and rewrite the two whitespace-skip loops
so they never step past the NUL. No behaviour change for well-formed
input: the existing wildtest.txt still passes, and the crafted overflow
input is now clean under valgrind.

Fixes #776
Reported-by: vikk777 (@vikk777)
2026-06-13 18:56:49 +10:00
Andrew Tridgell 04e4ee4ece log: copy forwarded message by length in rwrite(), not strlcpy()
The valgrind memcheck CI flagged 'Conditional jump depends on uninitialised
value(s)' in rwrite() -> strlcpy() (log.c) and the subsequent logit() fprintf.
rwrite()'s daemon/logfile branch did strlcpy(msg, buf, MIN(sizeof msg, len+1)),
but strlcpy() scans the whole source with strlen(); buf is the data buffer from
read_a_msg() (io.c) holding exactly len bytes of a forwarded MSG_* payload with
no NUL terminator, so strlen() reads past the message into uninitialised stack.

Copy exactly len (bounded) bytes with memcpy() and NUL-terminate, matching the
(buf, len) contract the rest of rwrite() already honours.  Behaviour is
unchanged for the NUL-terminated callers; the over-read is gone.

Full testsuite under valgrind (1572 logs) now reports zero unsuppressed errors.
2026-06-13 18:56:32 +10:00
Andrew Tridgell aae9534a6b ci: build test helpers before the valgrind run
`make` alone does not build the CHECK_PROGS test helpers (tls, trimslash,
t_chmod_secure, ...), so runtests.py exited immediately with "missing
test helper program(s)", produced no valgrind logs, and the scan step
failed every job with "the suite did not run". Use `make check-progs`,
which builds rsync plus the helpers and symlink fixtures without running
the suite.
2026-06-13 18:56:32 +10:00
Andrew Tridgell d0f2444aa5 testsuite: force C locale in reverse-daemon-delta byte-count parse
rsync groups the "sent/received N bytes" summary numbers using the
locale's thousands separator (e.g. de_DE uses '.'), which broke the
[\d,]+ parser and failed the test for testers in non-C locales. Run the
peer client under LC_ALL=C so the output is deterministic.

Reported-by: Michael Mess <michael@michaelmess.de>
2026-06-13 18:56:32 +10:00
Andrew Tridgell e6cb8788f8 testsuite: add gating valgrind memcheck workflow + suppressions
Add a .github/workflows/valgrind.yml that runs the full suite under
valgrind in a 2x2 matrix (user/root x pipe/tcp transport) and gates on
memory errors. It uses --leak-check=no: rsync intentionally leaves
file-list/socket/option memory unfreed at exit, so a leak check is
inherently noisy; the gate flags uninitialised reads, invalid
read/write, bad frees and uninit syscall params instead.

Add testsuite/valgrind.supp covering the known-benign reports (rwrite
strlcpy over-read on a non-NUL-terminated peer message, atomic_create/
delete_item st_mode read under fakeroot, libfakeroot msgsnd padding,
plus popt/xxhash leaks for manual --leak-check audits). runtests.py
--valgrind now loads it automatically.
2026-06-13 18:56:32 +10:00
Andrew Tridgell 11e3e2390a token: allow uncompressed literal runs larger than CHUNK_SIZE
The hardening in c44c90e9 added a check in simple_recv_token() rejecting
any uncompressed literal-run length > CHUNK_SIZE (32k). That assumption
breaks interoperability: other rsync implementations -- e.g. the acrosync
library used by the iOS "PhotoBackup" app -- use a 64k block size and
send literal runs of 65536 bytes, which 3.4.3+ now rejects with
"invalid uncompressed token length 65536".

The check was unnecessary: simple_recv_token() already reads the run
CHUNK_SIZE bytes at a time via the residue loop (n = MIN(CHUNK_SIZE,
residue)), so read_buf() never writes past the static CHUNK_SIZE buffer
regardless of the wire-supplied length. Drop the check to restore
interop; the compressed-token integer-overflow fix from c44c90e9 (the
MAX_TOKEN_INDEX / rx_token caps) is left unchanged.

Fixes #1002
Reported-by: Jack Whitham
2026-06-13 18:01:19 +10:00
Will SargandClaude Sonnet 4.6 d0c0ca2d26 testsuite: fix executability test skip on FreeBSD (EFTYPE)
FreeBSD and OpenBSD return EFTYPE (errno 79) when chmod-ing a sticky bit
onto a regular file as non-root, rather than EPERM/EACCES. Catch OSError
and check errno against the expected skip set so the test skips correctly
on those platforms instead of erroring out.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 16:47:39 +10:00
Andrew Tridgell 4ef775fa97 abdiff: A/B differential regression hunter for rsync
testsuite/abdiff.py runs the same benign transfer with two rsync binaries
(A = build under test, B = a baseline) and compares the OUTCOME -- exit code,
stderr, --stats "Literal data", the destination tree (content + full metadata),
the --itemize list, and (with --cost) peak process-group RSS. For benign input
the two must be indistinguishable; any divergence is a regression candidate.
It is a developer tool, NOT a runtests.py test (does not end in _test.py).

Capabilities:
- Scenario sweeps over options / path shapes / file types / sizes / modes /
  selection / placement / wire / transports, plus domain-knowledge pairwise +
  combo sweeps and a stochastic fuzzer/role matrix.
- Transport lanes: local, ssh split (lsh.sh), stdio-pipe daemon, a REAL TCP
  daemon (bound port + greeting/handshake/auth challenge-response), and the
  restricted rrsync wrapper (support/rrsh.sh; each binary paired with its own
  version's rrsync via --rrsync-a/--rrsync-b, since rrsync ships in the script).
- Stability gate: each binary is run N times and escalated on a candidate diff;
  nondeterministic scenarios are quarantined FLAKY, never reported as regressions.
- Parallel (-j, default 20) with a per-run findings log; --loop runs until
  --timelimit (or Ctrl-C), feeding the pool a half-random / half-systematic
  stream of new combinations. As root an "all" run also folds in the root-only
  sweeps (priv, daemonchroot).
- General coverage levers: a cost oracle (--cost, peak RSS over the whole process
  group), transport lifted as an orthogonal axis, a resume/redo sweep, and
  type-transition / nanosecond-mtime / scale (--scale N) fixtures.

Documented in testsuite/README.md.
2026-06-11 12:32:54 +10:00
Andrew Tridgell 8b042907e5 testsuite: add perftest.py to compare two rsync builds' transfer speed
A standalone dev tool (run directly, not via runtests.py) for catching
performance regressions between rsync releases.  Given two rsync binaries it
builds one deterministic test tree -- heavy-tailed file sizes, a directory
spine, symlinks, hard links and a spread of permission modes, modelled on the
gentestdata generator -- then runs the two binaries ALTERNATELY for N loops,
timing each transfer, and reports the mean and standard deviation per binary.

Each loop times a full copy into an emptied destination and an incremental
no-op against an already-synced one (rsync's scan/file-list/stat overhead,
where many regressions hide); --mode selects.  The first run of each binary is
dropped to reduce page-cache impact, the run order alternates to cancel drift,
and a B-vs-A slowdown is flagged only when it exceeds the run-to-run noise.
2026-06-11 08:23:56 +10:00
Zen Dodd b88080bd49 docs: clarify chmod copy special bits 2026-06-09 14:18:29 +10:00
Zen Dodd 30751ca864 chmod: clear special bits on copy assignment 2026-06-09 14:18:29 +10:00
Zen Dodd 0fdd7cd6a2 chmod: support permission copy modes 2026-06-09 14:18:29 +10:00
Andrew Tridgell fdc58b7cce lib: use .balign in md5 x86-64 asm to fix macOS over-alignment
The file used ".align 16" intending 16-byte alignment (GNU/ELF semantics).
On macOS the Mach-O assembler reads ".align N" as 2^N, so it requested
64KB alignment for __TEXT,__text, producing:

  ld: warning: reducing alignment of section __TEXT,__text from 0x10000
  to 0x1000 because it exceeds segment maximum alignment

The linker clamps it back, so it was harmless, but .balign 16 means
16 bytes on every target and silences the warning.
2026-06-09 12:26:24 +10:00
Andrew Tridgell 85eedb242e checksum: guard the AVX2 roll-asm path with a runtime CPUID check
When built with --enable-roll-asm, get_checksum1() called the AVX2 asm
routine get_checksum1_avx2_asm() unconditionally. Unlike the intrinsic
path (get_checksum1_avx2_64), which is function-multiversioned with a
target("default") fallback and so resolves safely on any CPU, the asm
routine is a single AVX2-only symbol with no fallback. On an x86-64 host
without AVX2 (an older CPU, or a VM that does not expose AVX2) the first
block checksum executes a VEX-encoded instruction and dies with SIGILL,
which surfaces as "connection unexpectedly closed (0 bytes received so
far)" and a code-12 protocol error.

Gate the asm call on a cached __builtin_cpu_supports("avx2") check, the
same signal the intrinsic resolver uses. When AVX2 is absent we skip it
and the SSSE3/SSE2/scalar steps (safe everywhere) do the work. Apply the
same guard in the simdtest harness so it can run on non-AVX2 hosts too.
2026-06-09 12:26:24 +10:00
Andrew Tridgell 806dff20d9 tests: add clang scan-build static-analysis CI (informational)
Run the clang static analyzer over a check-progs build, publish the HTML report
as an artifact, and print the bug count to the run summary. INFORMATIONAL only:
it does not pass --status-bugs, so it surfaces new analyzer findings without
going red on the existing (overwhelmingly false-positive) reports.

Runs on push/PR to master and via workflow_dispatch. No cron: it is
informational and its output only changes with the code (push/PR) or the clang
version, so a daily run on an unchanged tree would add noise without value.
2026-06-08 20:54:57 +10:00
Andrew Tridgell 8f63c498e9 tests: add ASan+UBSan CI gate
Add a clang AddressSanitizer + UndefinedBehaviorSanitizer workflow that builds
rsync with -fsanitize=address,undefined -fno-sanitize-recover=undefined -DNDEBUG
and runs the full test suite over both the stdio-pipe and TCP daemon transports.

UBSAN_OPTIONS=halt_on_error=1 together with -fno-sanitize-recover=undefined makes
any undefined behaviour fatal, so this job gates: the tree must stay UBSan-clean.
The remaining findings are fixed in code (hashtable/mdfour shifts, xattrs, and
log.c's file_struct, kept aligned via rounding.h); only byteorder.h's intentional
unaligned accessors are suppressed, with no_sanitize. -DNDEBUG builds as a release
does (assert() compiled out) so ASan covers the production code paths.

Runs on push/PR to master and via workflow_dispatch, plus a weekly cron to
catch breakage from a moving ubuntu-latest/clang toolchain (push/PR already
cover every code change, so daily would just re-run an unchanged tree).
2026-06-08 20:54:57 +10:00
Andrew Tridgell df2833b318 io: drop the dead/unnecessary read_varint UBSan guard
The cherry-picked #428 wrapped no_sanitize attributes on read_varint() and
read_varlong() in `#ifndef CAREFUL_ALIGNMENT`, but byteorder.h always
#defines CAREFUL_ALIGNMENT (to 0 or 1), so that guard is never true and the
attributes were dead code.

They are also unnecessary: both functions read the assembled value through
an aligned union member (union { char b[5]; int32 x; }), not an unaligned
cast, so UBSan's alignment check never fires there (verified: the ASan+UBSan
suite is clean without them).  Remove the whole block rather than fix the
guard.  (The byteorder.h annotations from #428, which are real and correctly
placed inside the !CAREFUL_ALIGNMENT branch, are kept.)
2026-06-08 20:54:57 +10:00
Sam James 7214372a8a Disable UBSAN for alignment-sensitive functions when !CAREFUL_ALIGNMENT
rsync sets CAREFUL_ALIGNMENT for architectures which do not support
unaligned access. Disable UBSAN for functions which may use unaligned
accesses when CAREFUL_ALIGNMENT is set.

Bug: https://github.com/WayneD/rsync/issues/427
Signed-off-by: Sam James <sam@gentoo.org>
(cherry picked from commit 11c1e934e8)
2026-06-08 20:54:57 +10:00
Andrew Tridgell 497357800a log: align the file_struct built in log_delete()
log_delete() builds a struct file_struct inside a char buffer offset by the
(EXTRA_LEN-granular) extra data.  The EXTRA_ROUNDING block that rounds that
offset up to the struct's alignment (exactly as flist.c does for its pool
allocations) was dead code here: log.c never included rounding.h, so
EXTRA_ROUNDING was undefined and the rounding never ran, leaving the
file_struct pointer potentially under-aligned.  That trips UBSan's alignment
check and would fault on strict-alignment arches.

Include rounding.h (and add the Makefile dependency) so the existing rounding
actually applies -- fixing the alignment at the source rather than suppressing
the sanitizer.
2026-06-08 20:54:57 +10:00
Andrew Tridgell fa084c4ae3 xattrs: fix UBSan-detected undefined behavior
Three pre-existing issues UBSan flags during the xattr tests:

  * xattr_lookup_hash(): the summed hashlittle2() values overflow the
    signed int64 accumulator (UB).  Accumulate in uint64_t and convert back
    at return -- the key is only used for hash-table equality, so the value
    is unchanged.
  * rsync_xal_get(): for an empty list (count == 0) the loop init
    `rxa += count-1` forms `items - 1` on a NULL `items` (UB).  Guard with
    `if (count)`.
  * rsync_xal_store(): `memcpy(dst, xalp->items, 0)` passes a NULL source for
    an empty list (UB).  Guard with `if (xalp->count)`.
2026-06-08 20:54:57 +10:00
Andrew Tridgell 4148419736 hashtable, mdfour: avoid signed left-shift overflow
UBSan flags two spots that shift a value into the top bits of a word via a
signed operand:

  * lib/mdfour.c copy64(): `in[i] << 24` promotes the uchar to int, so a
    byte >= 128 overflows int (UB).  Cast each byte to uint32.
  * hashtable.c NON_ZERO_64(): `(int64)(x) << 32` overflows int64 whenever
    x's high bit is set.  Shift as uint64_t (covers all four call sites).

Behavior-preserving -- only the intermediate type changes; the resulting
bit pattern is identical.
2026-06-08 20:54:57 +10:00
Andrew Tridgell 66712a90b3 rsync-web: updates for the 3.4.4 release 2026-06-08 14:45:03 +10:00
Andrew Tridgell b780749ffb release.py: accept a git worktree in require_top_of_checkout()
In a git worktree .git is a file (a gitdir pointer), not a directory,
so os.path.isdir('.git') wrongly aborted with "no .git dir" when the
release was run from a worktree. Use os.path.exists() so it works from
both a normal checkout and a linked worktree.
2026-06-08 14:44:37 +10:00
Andrew Tridgell d25c5e4b11 ci: move the daily scheduled jobs to weekly
Every platform build (the BSD/Solaris/macOS/cygwin/almalinux/ubuntu jobs),
coverage, the version-mix job and the android static build ran on a daily cron
*in addition to* push and pull_request to master. Since push/PR already cover
every code change, the cron only adds drift coverage -- catching breakage from a
moving runner image or toolchain that no commit triggers. Those images do not
change daily, so a daily run mostly re-tests an unchanged tree.

Move them all to a weekly cron (Mondays, keeping each job's existing time) to
keep that drift coverage at roughly a seventh of the Actions spend and log
noise. fleettest was already weekly. Per-change CI on push/PR is unchanged, and
workflow_dispatch still allows an on-demand run.
2026-06-08 10:25:38 +10:00
Andrew Tridgell 1ddfe17d65 fleettest: --cleanup also kills stray flippers/daemons and root-owned dirs
A run killed without a parent-death backstop can strand a TOCTOU path-flipper
(a busy `python -c` rename loop that pins a CPU) and an orphaned test rsyncd
(--no-detach --address=127.0.0.1) that squats its fixed port -- the wedge the
claim_ports() bind-probe now reports and points at --cleanup. Sweep both, best
effort, before removing the run dirs.

Each sweep counts the pattern, kills it (with a `sudo -n` retry for a process a
root-running test left), then re-counts after a settle: KILLED reports what
actually died, and a process that survives (pkill blocked, no passwordless sudo,
missing/limited pkill) is reported as SURVIVED and fails the run instead of
falsely claiming success.

Run-dir removal falls back to `sudo -n rm` so a dir whose contents a root test
owns is removed instead of failing with "Permission denied" (the failure mode
seen on the ubuntu/mac targets); only a dir that survives even sudo is failed.

The kill patterns use the pgrep self-exclusion trick ('r[e]name', 'det[a]ch')
so they match a real process's "rename"/"detach" but not the literal pattern in
the cleanup shell's own argv -- run_on() passes the whole script as the remote
argv, so without it --cleanup would signal itself. The patterns are host-global
(not scoped to one run), so --cleanup is documented to run between runs, not
during one.
2026-06-08 09:41:59 +10:00
Andrew Tridgell 6e6b4135ab testsuite: verify a claimed test port is actually bindable
claim_ports() takes a POSIX byte-range lock per port, which serializes
concurrent live test runs. But the kernel drops that lock the instant the
holding process dies, even if the run left an orphaned rsync --daemon still
bound to the port -- which happens when a run is SIGKILLed on a platform with
no parent-death backstop (rsyncfns only arms PR_SET_PDEATHSIG, Linux-only, so
the BSDs/Solaris/macOS can strand a daemon). A later run then wins the freed
lock while the socket is still squatted and dies with a cryptic "bind() failed:
Address already in use" / "did not see server greeting".

After taking each lock, actually bind the port (SO_REUSEADDR, so a port merely
in TIME_WAIT is not a false positive; only a live squatter fails) and close it
immediately. On failure stop with an actionable message naming the port and the
likely orphaned daemon. Closes the gap that masked the OpenBSD daemon-auth wedge.
2026-06-08 09:41:59 +10:00
Andrew Tridgell c2b8e4532b fleettest: require runtests.py in --testsuite-repo, not the build tree
When --testsuite-repo provides the suite, the build tree (--repo) need not
carry runtests.py -- it may be an older release whose shell testsuite predates
the Python runtests.py (e.g. a 3.4.1 backport branch built and tested with the
current suite).  Check runtests.py in TESTSUITE_REPO and only require the build
tree to be rsync source (rsync.h).
2026-06-08 06:29:49 +10:00
Andrew Tridgell 7b66c0665f fleettest: add --testsuite-repo to run another tree's suite against this build
--repo couples the built source and the test suite that exercises it.
--testsuite-repo PATH overlays runtests.py + testsuite/ from a second tree onto
the staged build tree, and sources the expected-skip workflows from it, so one
can build an older release (e.g. a 3.4.x stable branch) and run the current
comprehensive suite against that binary. Defaults to --repo, so the existing
single-tree behaviour is unchanged.
2026-06-08 06:29:49 +10:00
Andrew Tridgell 49f8dd1ca4 runtests: stop discovering obsolete *.test shell tests
The shell testsuite was removed in 1f689ec0 (rewritten in Python); only
*_test.py remain, yet collect_tests still globbed *.test and _testbase mapped
foo.test and foo_test.py to the same canonical name. Harmless on a master tree
(no .test files), but when an older tree's *.test files are present -- e.g.
fleettest --testsuite-repo building a 3.4.x release whose shell suite still
exists -- both glob to the same test name and scratch dir and race under -j,
producing spurious failures. Drop .test discovery entirely.
2026-06-08 06:29:49 +10:00
Andrew Tridgell 6fad1d7d74 testsuite,ci: mark recv-discard-nullderef CI skip and tighten its check
The regression test honestly skips when it cannot force the receiver's
output mkstemp() to fail -- as root (root bypasses DAC) and on Cygwin
(chmod 0555 does not deny the owner a write). The ubuntu, ubuntu-22.04,
almalinux and macOS jobs run `make check` as root, and Cygwin can't
enforce the unwritable directory, so the test skips on all of them.
runtests.py fails a run on any skip-set mismatch, so add the test to
those jobs' RSYNC_EXPECT_SKIPPED lists; the BSD/Solaris jobs run as root
too but enforce no expected-skip set, so they need no change.

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

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

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

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

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

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

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

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

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

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

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

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

Fixes: #880

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Regression check:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:03:58 +10:00
Andrew TridgellandClaude Opus 4.7 bc63ea82f2 ci: run the OpenBSD --use-tcp test step at -j2
The OpenBSD job runs inside a nested VM. At -j8 the --use-tcp run starts
many concurrent loopback daemons, and under that resource pressure the
daemon connection handshake occasionally loses a timing race and one test
hangs to the 300s runner timeout. It is an environment artifact, not an
rsync defect: the daemon handshake writes-then-reads with unbuffered early
I/O (no flush/mutual-wait deadlock), the indefinite wait is the documented
no-timeout daemon behaviour, and it does not reproduce off OpenBSD even with
the full suite pinned to a single CPU at -j8.

Drop just this job's --use-tcp parallelism to -j2 so the nested VM stops
over-subscribing; the pipe `make check` and every other platform are
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:44:12 +10:00
Andrew TridgellandClaude Opus 4.7 0d4fb1bc89 testsuite: cover more path/file-operation code (syscall.c, util1.c, delete.c)
Target previously-uncovered functions in the path/file-operation files the
resolver restructure touches, confirmed hit under coverage:

  preallocate   --preallocate (syscall.c do_fallocate) and sparse hole-punching
                via --preallocate --sparse and --inplace --sparse (do_punch_hole),
                on a file several levels deep.
  fuzzy-basis   --fuzzy basis selection with similar-named candidates and no
                exact match, so the generator scores them (util1.c fuzzy_distance).
  delete-deep   add a --backup --delete case so removing an extraneous
                backup-suffixed file consults delete.c is_backup_file.

preallocate probes --preallocate support up front and skips where it is
unavailable: macOS, the *BSDs and Solaris build without fallocate/posix_fallocate
(and FALLOC_FL_PUNCH_HOLE is Linux-only), and reject the option outright. It runs
on Linux and Cygwin. fuzzy-basis and delete-deep are plain local transfers with
no skips. All green on master and under --protocol=29/30.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:44:12 +10:00
Andrew TridgellandClaude Opus 4.7 52480aaac2 runtests: compare expected-skipped order-insensitively; register daemon-access-ip
The --expect-skipped check compared the skip list as an ordered string, so the
per-platform RSYNC_EXPECT_SKIPPED lists had to match runtests' collection order
(sorted filenames) exactly -- a subtle, easy-to-break ordering dependency.
Compare the skipped SET instead; which tests skipped is what matters.

Register the new require_tcp test daemon-access-ip in the per-platform
expected-skipped lists (it skips in the pipe-transport make check, like
daemon-chroot-acl and proxy-response-line-too-long).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:44:12 +10:00
Andrew TridgellandClaude Opus 4.7 702a8f61b7 testsuite: cover daemon access-control, config includes, --stop-at
Target the lowest-coverage rsync files identified from a merged (pipe + proto29/30
+ tcp) gcov report:

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

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

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

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:44:12 +10:00
Andrew TridgellandClaude Opus 4.7 f1d5a3c815 ci: declare new metadata-coverage test skips for macOS and Cygwin
acls-depth skips where ACLs/setfacl are unavailable (macOS, Cygwin) like the
existing acls tests, and sparse skips on APFS (macOS), where a seek-written
hole isn't allocated sparsely. Add them to the per-platform RSYNC_EXPECT_SKIPPED
lists so the skip-set assertion stays accurate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew TridgellandClaude Opus 4.7 3086dbc0fd ci: add an Ubuntu gcov coverage job
Builds with --enable-coverage and runs the suite under both transports
(make coverage, then make coverage-tcp). gcovr's line/branch/decision totals
are printed to the step log and also written to the GitHub step summary, so the
coverage numbers are visible directly in the CI output; the HTML reports are
uploaded as an artifact. make coverage exits with the suite's status, so a test
regression fails the job.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:31:52 +10:00
Andrew TridgellandClaude Opus 4.7 63e599b921 build: add 'make coverage-tcp' and drop deprecated gcovr --branches
coverage-tcp reuses the coverage recipe with --use-tcp (daemon tests over a real
loopback rsyncd, which also runs the require_tcp-only tests) and a separate
report directory, via COVERAGE_RUNFLAGS / COVERAGE_DIR. Verified end to end:
pipe run reports 63.9% lines, the TCP run 64.5% (it exercises more code).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:06:21 +10:00
Andrew TridgellandClaude Opus 4.7 4b862306e5 testsuite: restore non-Linux xattr/fake-super coverage
The Python rewrite had gated the xattr / fake-super tests (xattrs,
xattrs-hlink, chown-fake, devices-fake) to Linux because it used the
Linux-only os.*xattr. Restore them on macOS, FreeBSD, Cygwin and Solaris
via a per-OS xattr surface in rsyncfns.py (xattrs_supported / xattr_set /
xattr_dump):
  * Linux   -- os.*xattr
  * macOS   -- xattr
  * FreeBSD -- setextattr / lsextattr / getextattr
  * Cygwin  -- getfattr / setfattr (from the `attr` package; CPython on
               Cygwin has no os.*xattr)
  * Solaris -- runat(1), with the script on stdin and the attr name/value
               passed via the environment (the runat -c form mangles args)

Test attribute names are logical; the "user." namespace prefix is added
only on the Linux-style platforms (Linux, Cygwin). RSYNC_PREFIX/RUSR vary
per OS (macOS and Solaris use rsync.nonuser to avoid rsync's reserved
rsync.* space). The macOS and Cygwin workflows no longer skip these tests;
the FreeBSD/Solaris jobs use IGNORE skip-checking so need no change.

Verified on real Linux, macOS, FreeBSD, Cygwin and Solaris hosts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:34:52 +10:00
Andrew TridgellandClaude Opus 4.7 70948a9dc3 testsuite: post-review fixes and lock-file hardening
* chmod-option: pin umask to the suite-wide 022 baseline (mirroring the
    old rsync.fns) so rsync's --chmod `D+w` is computed and applied under
    the same umask -- fixes failures under a different ambient umask (077).
  * daemon module-list test: assert the `list = no` module does NOT leak
    into the listing (the substring check alone missed regressions).
  * claim_ports() lock file: open with O_NOFOLLOW and only fchmod a file we
    O_EXCL-created, rejecting a symlink OR hard link planted at the
    well-known /tmp path -- which, with the TCP tests running under sudo in
    CI, could otherwise chmod an arbitrary root-owned target. Require a
    pristine (regular, nlink==1) file.
  * CI: extend the Linux/Cygwin expected-skip lists for the gated tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:34:52 +10:00
Andrew TridgellandClaude Opus 4.7 951bf0a446 socket: enforce socketpair_tcp()'s anti-hijack guarantee
socketpair_tcp() fakes a connected socket pair via a loopback TCP
self-connect (socket -> bind 127.0.0.1:0 -> listen -> connect ->
accept), used by sock_exec() for RSYNC_CONNECT_PROG. Its comment has
long promised that "nobody else can attach to the socket, or if they
do that this function fails", but nothing actually verified it: the
code accept()ed whatever connection arrived first without checking it
was the one our own connect() made.

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

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

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:34:52 +10:00
Andrew TridgellandClaude Opus 4.7 bea8a3a16f testsuite: secure stdio-pipe daemon transport by default, opt-in TCP
Daemon-mode tests default to the stdio-pipe transport (RSYNC_CONNECT_PROG),
which opens no listening socket -- so `make check` never exposes a network
service. Real TCP is opt-in via `runtests.py --use-tcp`, with the daemon
bound to loopback (127.0.0.1) on a claim_ports()-reserved port; CI runs the
suite both ways.

start_test_daemon() is the single seam every daemon test uses: the secure
pipe by default, a real rsyncd on a claimed loopback port under --use-tcp.
Tests with no pipe equivalent (the fake-proxy listener and the reverse-DNS
hostname-ACL daemon test) are gated behind require_tcp().

`make check` also now runs the suite in parallel by default (CHECK_J=8);
the claim_ports() byte-range locks make that safe across concurrent runs.

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

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

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

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

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

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

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

The dead rsync.fns shell library is removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:34:52 +10:00
Andrew TridgellandClaude Opus 4.7 8839314025 ci: add static Android NDK build workflow
Cross-compiles statically-linked rsync binaries with the Android NDK for
arm64-v8a (all modern phones) and armeabi-v7a (older 32-bit devices), and
uploads them as workflow artifacts for adb push / Termux use.

The build is self-contained (optional external libraries disabled; keeps
md5/md4 and the bundled zlib) and forces a few configure cache values
that can't be probed when cross-compiling: lchmod()/lutimes() off (Bionic
doesn't declare them until API 36 though the symbols link), and
socketpair / mknod-FIFO / mknod-socket on (Android runs a Linux kernel,
so these match the native result). IPv6 is enabled explicitly.

Since the binaries are cross-compiled the test suite can't run; the job
instead asserts each binary is static and the correct architecture, and
smoke-tests `--version` under qemu-user.

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

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

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

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

Update both call sites to use the helper.  Linux/FreeBSD/macOS
still take the /dev/urandom fast path; NonStop and any other
platform missing the device get the awk fallback transparently.
Both paths verified locally with the symlink-dirlink-basis test.
2026-05-21 07:40:30 +10:00
Andrew Tridgell e1c5f0e93a t_chmod_secure: probe kernel RESOLVE_BENEATH at runtime; drop test skip
The chmod-symlink-race test was previously a no-op on Solaris,
OpenBSD, NetBSD, and Cygwin via a case 'uname -s' skip.  The skip
was too broad: of the four scenarios the helper exercises, only
the 'legitimate within-tree dir-symlink' one actually needs
RESOLVE_BENEATH-equivalent kernel support.  The other three
(attack rejection, plain relative path, top-level file) behave
identically on the per-component O_NOFOLLOW fallback and would
have caught the t_stub.c max_alloc=0 bug fixed in the previous
commit if the test had been allowed to run.

Make the helper probe the running kernel for either
openat2(RESOLVE_BENEATH) on Linux 5.6+ or openat(O_RESOLVE_BENEATH)
on FreeBSD 13+ / macOS 15+ by opening '.' under the requested
confinement.  Honour the result:

  - If RESOLVE_BENEATH-equivalent confinement is available, the
    within-tree symlink scenario must succeed (status quo).
  - If not, the per-component O_NOFOLLOW fallback rejects every
    symlink including legitimate ones; expect the within-tree
    symlink scenario to be rejected (rc != 0) and the file mode
    to remain unchanged.

The attack-rejection, plain-path and top-level scenarios are
unchanged: they expect the same outcome on both code paths.

Drop the case-based skip from chmod-symlink-race.test so the test
runs everywhere and the per-component fallback gets the CI
coverage that the SunOS/OpenBSD/NetBSD/Cygwin runners can
provide.  HPE NonStop -- which lacks RESOLVE_BENEATH but isn't in
the existing skip list -- is also covered by this change.
2026-05-21 07:40:30 +10:00
Andrew Tridgell cfdc27c613 t_stub.c: raise max_alloc default so test helpers can allocate
The t_stub.c shim defined max_alloc = 0 as a placeholder to satisfy
the link against util2.o.  This was harmless when the test helpers
made no allocations, but the secure_relative_open() implementation
in 3.4.0+ calls my_strdup() in its per-component O_NOFOLLOW
fallback (syscall.c around line 1857), and the 3.4.3 do_*_at()
hardening series added more such calls.  With max_alloc=0, every
allocation in that path trips the 'exceeded --max-alloc=0' check in
util2.c's my_alloc(), and t_chmod_secure (which exercises
do_chmod_at via secure_relative_open) fails on the very first
my_strdup.

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

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

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

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

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

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

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

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

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

A follow-up commit repoints HTML_SRC in packaging/release.py at
the new in-tree location.
2026-05-20 15:36:44 +10:00
Andrew Tridgell 9d014670df INSTALL.md: point Ubuntu users at the ppa:rsyncproject/rsync PPA
Most Ubuntu users landing on INSTALL.md want to install rsync, not
build it.  Add a short section near the top that offers the
Launchpad PPA as the one-line path for the four currently supported
series (jammy 22.04 LTS, noble 24.04 LTS, questing 25.10,
resolute 26.04 LTS), and clarify that the rest of the file is about
building from source.
2026-05-20 15:36:44 +10:00
Andrew Tridgell 647a00a278 start on 3.4.4 2026-05-20 11:50:33 +10:00
Andrew Tridgell 2c7777aaa6 Preparing for release of 3.4.3 [buildall] 2026-05-20 10:07:26 +10:00
Andrew TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.7 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 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 TridgellandClaude Opus 4.6 aa142f08ef fix uninitialized mul_one in AVX2 checksum and add SIMD checksum test
The AVX2 get_checksum1_avx2_64() read mul_one before initializing it,
which is undefined behavior. Replace the cmpeq/abs trick with
_mm256_set1_epi8(1) to match the SSSE3 and SSE2 versions.

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

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

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

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

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

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

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

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

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

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

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

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

Result:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

While at it, silence again a warning that is being triggered by
Apple's clang 15.
2024-11-14 07:15:14 +11:00
Wayne Davison 6601510425 Mention more NEWS. 2024-11-09 11:05:16 -08:00
Wayne Davison f7ac7ffd16 Some minor option/prompt tweaks. 2024-11-05 17:50:16 -08:00
Wayne Davison 4320c25fcc More helper script improvements. 2024-11-05 13:44:17 -08:00
Wayne Davison 4490fb8660 Add some info for making a release. 2024-11-05 13:03:04 -08:00
Wayne Davison 475ca7d43c Add helper script for updating samba files. 2024-11-05 12:42:42 -08:00
Wayne Davison 7c3c54b132 Don't force zsh use. 2024-11-05 11:20:28 -08:00
Wayne Davison bcf0738f98 Indentation tweak. 2024-11-05 11:20:17 -08:00
Wayne Davison 8749ec6436 Update to newer artifact version. 2024-11-05 11:14:46 -08:00
Wayne Davison 42e2b56c4e Another cast when multiplying integers. 2024-11-05 11:01:03 -08:00
Wayne Davison 0902b52f66 Some checksum buffer fixes.
- Put sum2_array into sum_struct to hold an array of sum2 checksums
  that are each xfer_sum_len bytes.
- Remove sum2 buf from sum_buf.
- Add macro sum2_at() to access each sum2 array element.
- Throw an error if a sums header has an s2length larger than
  xfer_sum_len.
2024-10-29 23:06:34 -07:00
vincent sgherzi 9615a2492b added apple silicon path details 2024-05-29 11:19:19 +10:00
Wayne Davison 4592aa770d More tweaks for Actions.
- When a .github/workflows/*.yml file changes, skip running unaffected
  builds.
- We need git to be installed for git-version.h generation.
2024-04-10 13:24:09 -07:00
Wayne Davison 8bc363cc9f Separate the builds and make Cygwin always run. 2024-04-10 13:02:34 -07:00
Wayne Davison a9a3155756 Work around pkg install issue.
The xxhash, lz4, and zstd libraries aren't getting installed on FreeBSD.
[buildall]
2024-04-10 12:45:26 -07:00
Wayne Davison fcc79836b8 Get fetch-depth:0 right. 2024-04-10 12:30:05 -07:00
Wayne Davison 804411b7fd Get rid of gensend target & cached git version.
- Change the developer flow to not require updating the git-version repo
  that the builds used to download a git-version.h file. The Actions now
  do a full repo fetch so that the .h file can be generated via the git
  history.
- Get rid of the gensend Makefile target that was used for the above.
- Get rid of the pre-push git hook file that called "Make gensend".
- Change the FreeBSD build to save an artifact with its built binaries.

[buildall]
2024-04-10 12:23:58 -07:00
Wayne Davison 0b1b2a3ff4 Get the "dev" suffix right. 2024-04-10 11:53:17 -07:00
Wayne Davison 50bdf9685d Remove duplicate paragraph. 2024-04-10 11:51:59 -07:00
Charalampos Mitrodimas 3f2a38b011 CI: added Solaris build
Signed-off-by: Charalampos Mitrodimas <charmitro@posteo.net>
2024-04-09 07:34:26 +10:00
Wayne Davison 5510255f12 Tweak maintainer messaging. 2024-04-08 13:16:12 -07:00
Wayne Davison 56a039b04a Changes for 3.3.1dev. 2024-04-08 13:15:16 -07:00
Andrew Tridgell 7bc3be2b9e CI: fixed rules for when to trigger 2024-04-08 15:50:47 +10:00
Andrew Tridgell 411c4789df support: added install_deps_ubuntu.sh
convenient way to bootstrap quickly
2024-04-08 15:32:16 +10:00
Andrew Tridgell 231b239f30 check for stpcpy
needed for popt on macos
2024-04-08 15:31:36 +10:00
Andrew Tridgell 4c8683c875 update to popt 1.19 2024-04-08 15:31:36 +10:00
Rose 85c906f964 Silence unused var warning
recv_ida_entries still needs to be called regardless, so we cannot take that out. Let's just quiet the compiler instead.
2024-04-07 09:28:03 +10:00
Christian Hesse 35f5a21a16 hint that a proxy can handle plain and ssl stream at the same time 2024-04-07 09:25:46 +10:00
Andrew Tridgell 99673f937f CI: added FreeBSD build 2024-04-07 08:07:50 +10:00
Andrew Tridgell 9505ac5945 removed old cirrus CI 2024-04-07 08:07:50 +10:00
Ivan Babrou 0dd25d4752 configure.ac: fix failing IPv6 check due to missing return type
Fixing this warning escalated to an error, resuting in no IPv6 support:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* Extract new function unlink_and_reopen from copy_file

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

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

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

Resolves #208

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

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

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

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

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

 - Added the `--max-alloc=SIZE` option to be able to override the memory
   allocator's sanity-check limit.  It defaults to 1G (as before).
   Fixes bugzilla bug 12769.
2020-06-25 20:54:21 -07:00
Christian Hesse 39a083b16b Add missing semicolon in man page
All nginx configuration directives end in semicolon.
2020-06-24 17:54:47 -07:00
Wayne Davison 202b7b18af Tweak alloc args to size_t w/proper realloc order. 2020-06-24 17:52:36 -07:00
Wayne Davison 20934382e3 Use normal C comment style. 2020-06-23 21:45:32 -07:00
Wayne Davison 1bdf68b905 Prepare for future release of XXH3 & XXH128. 2020-06-23 21:01:25 -07:00
Wayne Davison 89827e49bc Another NEWS update. 2020-06-23 19:32:44 -07:00
Wayne Davison f157ff3b3a Avoid negotiating a "none" choice by default
The client does not pass "none" as a negotiation choice unless it's from
the user's environment list.  The server still passes the "none" value
to the client unless its environment var excludes it.
2020-06-23 19:20:01 -07:00
Wayne Davison d15cfef935 Setup for 3.2.2dev. 2020-06-23 19:02:59 -07:00
Wayne Davison 28f9c960d5 The server side can enforce its negotiation limits 2020-06-23 18:53:23 -07:00
Wayne Davison 323c42d51e Improve how the env restricts negotiated strings
- The env on the server side now affects the negotiated strings
  that are sent to the client.
- A too-old remote rsync gets a default negotiated string value
  so that an env restriction now handles old clients the same way
  as new ones.
2020-06-23 17:33:16 -07:00
Wayne Davison d1fdf9ff8d Avoid -U if --atimes is disabled. 2020-06-23 15:53:47 -07:00
Wayne Davison e93f40d8b4 Apple needs a leading underscore. 2020-06-23 15:47:27 -07:00
Hiroshi Takekawa 4df1b1d4fe Makefile.in: Use srcdir for installing rsync-ssl
When building out of source tree, we can't find rsync-ssl in the current
directory and installation fails.  Fix it by using the srcdir variable for the
path to rsync-ssl.

Signed-off-by: Hiroshi Takekawa <sian.ht@gmail.com>
2020-06-23 09:02:33 -07:00
Wayne Davison 1af58f6b77 Improve the info about compression. 2020-06-22 20:37:52 -07:00
Wayne Davison a8fc8fc2d2 Preparing for release of 3.2.1 2020-06-22 19:31:16 -07:00
Wayne Davison b8b7f1f3d0 Fix a typo. 2020-06-22 19:30:07 -07:00
Wayne Davison 622a116917 A few more man page tweaks. 2020-06-22 19:21:32 -07:00
Wayne Davison b51b0b3236 Get the NEWS heading idiom right. 2020-06-22 14:46:14 -07:00
Wayne Davison 8cb1c99563 A few more https changes. 2020-06-22 14:21:15 -07:00
Wayne Davison 597a751466 Update links to https. 2020-06-22 14:16:21 -07:00
Wayne Davison 5a9e4ae5e7 Improve the options info a bit more. 2020-06-22 14:12:27 -07:00
Wayne Davison 3094552311 Add --zl=N opt & improve its docs. 2020-06-22 13:41:42 -07:00
Samuel Henrique e4c9ff5873 Make --backup be set when --backup-dir is used 2020-06-22 13:05:17 -07:00
Samuel Henrique 9b13bcf185 Add Documentation field to systemd unit 2020-06-22 13:01:02 -07:00
Wayne Davison 8f6d6bcb08 Tweak valid_ipaddr() check. 2020-06-22 11:17:56 -07:00
Wayne Davison 300fd3055a Even more NEWS changes. 2020-06-22 09:57:29 -07:00
Wayne Davison f6df3708c2 A few more NEWS changes; change release script. 2020-06-22 09:09:51 -07:00
Wayne Davison 785cb938ec Even more NEWS improvements. 2020-06-21 23:30:00 -07:00
Wayne Davison 246d117df0 A bit more NEWS tweaking. 2020-06-21 23:07:16 -07:00
Wayne Davison 3f776f582b More talk of buggy clang++. 2020-06-21 22:53:33 -07:00
Wayne Davison d8d2d71663 Get the g++ version to see if it is really clang. 2020-06-21 22:34:32 -07:00
Wayne Davison 6a9adabfbb Mention the early-input on stdin. 2020-06-21 20:25:51 -07:00
Wayne Davison 2564f25114 Put the date in the heading of a pre release too. 2020-06-21 19:46:28 -07:00
Wayne Davison 7fb08531e0 Preparing for release of 3.2.1pre1 2020-06-21 19:24:26 -07:00
Wayne Davison 9dba0bb7fb Improve the simd note. 2020-06-21 18:52:28 -07:00
Wayne Davison 87bca719c3 Merge OLDNEWS.md into NEWS.md 2020-06-21 18:42:53 -07:00
Wayne Davison f3439944ea The proto files don't need perl, so change fetch rule. 2020-06-21 16:46:50 -07:00
Wayne Davison a7c1690d62 One more >= tweak. 2020-06-21 15:30:34 -07:00
Wayne Davison 662fedd74b Get the early-input reading code right. 2020-06-21 15:23:13 -07:00
Wayne Davison 128139c66a Leave 3.2.0 news in the NEWS file for 3.2.1. 2020-06-21 15:20:43 -07:00
Wayne Davison 2b439c1fc8 Disable atimes on macOS. 2020-06-21 15:16:56 -07:00
Wayne Davison e16b22751a Add --early-input=FILE option. 2020-06-21 14:32:00 -07:00
Wayne Davison 7587e20cf4 Output a helpful msg about configure only if the command fails. 2020-06-21 12:55:24 -07:00
Wayne Davison 2e1b46db39 Close STDIN for early exec script. 2020-06-21 11:17:09 -07:00
Wayne Davison f4e6fe54c9 More NEWS changes. 2020-06-21 09:18:52 -07:00
Wayne Davison f86ceb5539 Give more_testing() a default target. 2020-06-21 09:06:59 -07:00
Wayne Davison dfa34b4792 Some more docs/news changes.
- Mention the -VV behavior.
- Mention how the protect-args default is presented in -V list.
2020-06-21 08:58:45 -07:00
Wayne Davison e9e9fd0cca Use an ssse3 target instead of an inline declaration. 2020-06-21 08:28:49 -07:00
Wayne Davison 7e95ba8787 Add -fno-slp-vectorize to clang++. 2020-06-21 08:05:19 -07:00
Wayne Davison 66fd34ed84 Mention atimes & protected-args in capabilities. 2020-06-20 23:28:19 -07:00
Wayne Davison f8c6f9f4f3 Tweak the NEWS. 2020-06-20 23:15:22 -07:00
Wayne Davison e6cfebb578 We only need one capability marked with a "*". 2020-06-20 22:09:08 -07:00
Wayne Davison 5d2379d93f Mention "asm" instead of "ASM". 2020-06-20 21:59:51 -07:00
Wayne Davison 6884ccbd2f Mention openssl-crypto in -VV list. 2020-06-20 21:44:46 -07:00
Wayne Davison bad97961dc Elide -g from CXXFLAGS before the c++ test. 2020-06-20 21:26:21 -07:00
Wayne Davison b0ab07cdac Some README and man page tweaks. 2020-06-20 20:31:52 -07:00
Wayne Davison 68c4583693 Change repo to be 3.2.1dev. 2020-06-20 20:17:13 -07:00
Wayne Davison 6b237b0fe9 Require -VV to see SIMD & ASM in version output 2020-06-20 19:57:11 -07:00
Wayne Davison b37a136314 Get rid of -g option in CXXFLAGS (at least for now). 2020-06-20 19:23:54 -07:00
Wayne Davison c9c8c64506 Remove leftover case match. 2020-06-20 19:00:57 -07:00
Wayne Davison c5d502dc5f When fetching gen files, make sure aclocal.m4 is older than configure files. 2020-06-20 18:46:16 -07:00
Wayne Davison 1629b803cb More asm improvements
- Only use the asm code if we're on x86_64.
- More changes to decouple asm from simd.
- Check if the -Wa,--noexecstack option works.
- Support --disable-asm configure option.
2020-06-20 18:40:47 -07:00
Wayne Davison 29c7a4558a Include more SIMD test code to weed out older compilers. 2020-06-20 17:48:56 -07:00
Wayne Davison b7dc2ca25c Decouple the MD5 asm code from the simd enabling. 2020-06-20 17:01:25 -07:00
Wayne Davison f525f2c818 Remove asm type & size. 2020-06-20 16:54:24 -07:00
Wayne Davison 1b5819efbd Use AC_RUN_IFELSE() to make sure we can run the cpp test program. 2020-06-20 14:47:55 -07:00
Wayne Davison a56a0bc7d6 Mention how to turn off simd near the cpp compile. 2020-06-20 11:53:22 -07:00
Wayne Davison bd7bd5ff0c Simplify some escaping. 2020-06-20 11:35:56 -07:00
Wayne Davison f9aece899f Change SIMD test to use a compile check. 2020-06-20 11:13:06 -07:00
Wayne Davison 63508f1518 Handle hard-linking the top-level $VER-NEWS.html file on a final release. 2020-06-20 09:57:35 -07:00
Wayne Davison 9ac22062af The nightly dir is gone now. 2020-06-20 09:43:25 -07:00
Wayne Davison 73faaab26d Pass --noexecstack to assembler. 2020-06-20 09:23:56 -07:00
Wayne Davison 9467c1f9b9 Fix conditional directives in the asm file
- Switch .s -> .S to enable the preprocessor.
- Move some defines from mdigest.h to md-defines.h.
- Tweak the asm file to use md-defines.h.
- Add a couple missing .h dependencies in the Makefile.
2020-06-20 09:06:18 -07:00
Wayne Davison 04653dabc8 Exclude the asm code when it's not being used. 2020-06-20 08:05:53 -07:00
Wayne Davison 19617f7b4a Fix compiling in a separate dir. 2020-06-20 08:05:53 -07:00
Caleb Xu b218de2702 lib/md5-asm-x86_64.s: fix build with Apple Clang
The Mach-O x86-64 model doesn't seem to support ".type" and
".size" directives in assembly. Add ifdefs that should allow for
the file to build without issues in Apple Clang.
2020-06-19 23:38:15 -07:00
Wayne Davison d4764934c3 A slightly modified g++/clang++ check. 2020-06-19 23:29:31 -07:00
Wayne Davison c225330aaf Preparing for release of 3.2.0 2020-06-19 14:11:01 -07:00
Wayne Davison 3c56896d21 Simplify a variable. 2020-06-19 11:07:02 -07:00
Wayne Davison deb8353d2c Yes, we know we're discarding a return value. 2020-06-19 10:56:32 -07:00
Wayne Davison 73053f26bc Simple change to recv_token(). 2020-06-19 09:55:48 -07:00
Holger HoffstätteandWayne Davison 0c13e1b3f8 Prevent unnecessary xattr warning by reordering header inclusion. (#22)
xattr headers have been provided by glibc (at least on Linux/glibc)
for many years now. Reorder the inclusion of xattr headers to
attempt compatibility/legacy after the common case.
This prevents the warning without changing compatibility to
non-glibc systems.

* Add dependency on lib/sysxattrs.h header in Makefile

Co-authored-by: Wayne Davison <wayne@opencoder.net>
2020-06-19 08:22:54 -07:00
Wayne Davison 9da38f2f99 A few minor man page tweaks. 2020-06-19 00:26:43 -07:00
Wayne Davison a93ffb1ae9 More non-breaking space/dash improvements
- In html, use css more for non-breakability.
- In nroff, mark more dashes as non-breaking in code->bold sections,
  and get rid of backslashed dashes in preformatted blocks.
2020-06-18 23:55:51 -07:00
Wayne Davison e08f600378 Use -&#8288; instead of &#8209;
Using a non-breaking zero-width char after a dash makes the browser
avoiding breaking on that dash and also makes it match a dash in a
search.  This is better than a non-breaking dash char, which does not
match a dash in a search.
2020-06-18 22:58:11 -07:00
Wayne Davison e406845542 Comment must be indented to avoid ending the list item. 2020-06-18 21:57:34 -07:00
Wayne Davison a93eb4cf38 Handle a missing c++ too. 2020-06-18 17:02:46 -07:00
Wayne Davison 7fd24bef0f Make SIMD enabled by default again (for x86_64) 2020-06-18 16:28:28 -07:00
Wayne Davison 1a9a184145 Check extra rounding using an int64. 2020-06-18 15:45:39 -07:00
Wayne Davison 4965ccf283 We need to use nawk or gawk on Solaris, not their weird awk. 2020-06-18 14:53:55 -07:00
Wayne Davison c6f89cbf9c Complain if we can't enable simd on non-x86_64. 2020-06-18 14:27:00 -07:00
Wayne Davison 2921779c1f Fix clang check. 2020-06-18 13:46:01 -07:00
Wayne Davison cbed522ef4 Get rid of useless -e with sed. 2020-06-18 13:31:50 -07:00
Chainfire 4f539ccf21 x86-64 SIMD build fixes (#20)
* x86-64 SIMD build fixes

configure.ac was modified to detect g++ >=5 and clang++ >=7. Additionally
some script malfunctions on FreeBSD were corrected.

The get_checksum1() code has been modified to fix clang and g++ 10
compilation.

This version of the code and configure.ac has been tested on:

Ubuntu 16 - gcc 7.3.0, clang 6.0.0
Debian 10 - gcc 5.4.0, 6.4.0, 7.2.0, 8.4.0, 9.2.1, 10.0.1, clang 5.0.2,
6.0.1, 7.0.1, 8.0.0, 9.0.0, 10.0.0
ArchLinux 20200605 - gcc 10.1.0, clang 10.0.0
FreeBSD 12.1 - gcc 9.3.0, clang 8.0.1

It is unknown if it will work on gcc 5.0-5.3, but the script currently
allows it.
2020-06-18 13:20:44 -07:00
Wayne Davison b5e539fc5a Use documentation to extract 2 more .h lists
- Change default_cvsignore char[] into a define.
- Make the DEFAULT_DONT_COMPRESS and DEFAULT_CVSIGNORE defines get set
  based on their info in rsync.1.md.
- Add a few more don't-compress suffixes from Simon Matter.
2020-06-18 11:20:57 -07:00
Wayne Davison 88c18ef648 Make the g++ check more lenient. 2020-06-18 09:31:47 -07:00
Wayne Davison 7dc9431f60 A few minor man page improvements. 2020-06-17 11:25:38 -07:00
Wayne Davison 07a3e1f939 Enhance compatibility with older python3 versions. 2020-06-17 10:52:02 -07:00
Wayne Davison 93223719c9 A couple more NEWS tweaks. 2020-06-17 10:30:32 -07:00
Wayne Davison 0b2d5fe494 Preparing for release of 3.2.0pre3 2020-06-17 10:12:09 -07:00
Wayne Davison d3c7cfad22 Be a little more explicit with override info. 2020-06-17 09:31:48 -07:00
Christian Hesse 9ec777faf8 add a systemd socket unit for rsync 2020-06-17 09:19:12 -07:00
Christian Hesse 69f445fd09 update rsync systemd unit, add more security features 2020-06-17 09:19:12 -07:00
Wayne Davison 643b9d0183 Change SIMD back to disabled unless requested. 2020-06-16 23:00:01 -07:00
Wayne Davison 2c681b874e Some fixes after compiling on cygwin. 2020-06-16 22:58:24 -07:00
Wayne Davison e44e79cedb Update config.guess & config.sub. 2020-06-16 21:24:23 -07:00
Wayne Davison beaf19c3e7 Have --disable-md2man affect the Makefile. 2020-06-16 19:05:05 -07:00
Wayne Davison 0b2a394cbc Fix /usr/bin/env with script args. 2020-06-16 18:32:16 -07:00
Wayne Davison 27e88dec04 Use /usr/bin/env for increased portability. 2020-06-16 18:27:48 -07:00
Wayne Davison 929f136b3b A few more NEWS tweaks. 2020-06-16 15:48:23 -07:00
benrubson 6a22f4fee1 enh(configure) Promote OpenSSL crypto lib support 2020-06-16 15:05:36 -07:00
Wayne Davison d90990d6ac A few more trivial tweaks. 2020-06-16 14:42:41 -07:00
Wayne Davison 111225a996 Fix md2man --test on a fresh checkout. 2020-06-16 14:03:16 -07:00
Wayne Davison 7dfcbf7df6 Add g++ failure info; add mention of SSL rsyncd examples. 2020-06-16 12:10:56 -07:00
Wayne Davison 38ecf188d9 Only complain about lack of g++ on linux for now. 2020-06-16 11:35:54 -07:00
Wayne Davison 29be5eddde Add configure check for md2man functioning; split long error lines. 2020-06-16 11:13:45 -07:00
Wayne Davison 54b1ddc45d Change configure to make new features more likely to get included in a build. 2020-06-16 09:59:00 -07:00
Wayne Davison 8cd9aa326c Fix bug in CXXFLAGS tweak. 2020-06-16 09:14:28 -07:00
Wayne Davison cd50745e1c Remove the new $< use from the Makefile. 2020-06-16 08:46:44 -07:00
Wayne Davison ae94e3db4b Tweak my email. 2020-06-16 07:55:42 -07:00
Wayne Davison 6efaa74dd3 More spelling fixes from Fossies
https://fossies.org/linux/test/rsync-master.tar.gz/codespell.html
2020-06-16 07:46:28 -07:00
Wayne Davison 5496eda5d1 Turn help-from-md into an awk script. 2020-06-15 18:32:00 -07:00
Wayne Davison 353dec1102 Avoid -e option to sed for BSD. 2020-06-15 15:08:42 -07:00
Wayne Davison d80da9e674 A few small tweaks. 2020-06-15 15:04:08 -07:00
Wayne Davison 6f0c56304f Preparing for release of 3.2.0pre2 2020-06-15 11:53:19 -07:00
Wayne Davison 2452ad3663 Fixed setting of rsync_lastver var. 2020-06-15 11:52:54 -07:00
Wayne Davison 1fa38546a0 Document how to setup rsyncd behind a TLS proxy. 2020-06-15 11:36:24 -07:00
Wayne Davison 249e28c75a Rename "haproxy header" to "proxy protocol". 2020-06-15 11:33:23 -07:00
Wayne Davison 6273153c5f Add preliminary gnutls support. 2020-06-15 11:19:36 -07:00
Wayne Davison 628dcceb8d Choose openssl before stunnel. 2020-06-15 10:41:08 -07:00
Wayne Davison 00ec415a69 Tweak the stunnel4 Verify config; tweak the rsync-ssl docs/usage. 2020-06-15 09:36:13 -07:00
Wayne Davison ec8035cef9 A minor NEWS tweak. 2020-06-15 09:21:26 -07:00
Wayne Davison 775f64f4b8 Add a warning header to the generated help-*.h files. 2020-06-14 18:49:38 -07:00
Wayne Davison 660274bfb7 A few more md -> html improvements 2020-06-14 18:28:30 -07:00
Wayne Davison 59cf9ff797 More NEWS improvements. 2020-06-14 18:00:18 -07:00
Wayne Davison ff272503b0 Output who_am_i() info in all rsyserr() messages. 2020-06-14 15:54:42 -07:00
Wayne Davison 43a939e3f2 Improve some md files. 2020-06-14 15:29:45 -07:00
Wayne Davison b65b6db304 Add handling of non-breaking space & double-dash. 2020-06-14 15:29:45 -07:00
Wayne Davison 7b1f8f57c3 Update rrsync & its opt-culling script. 2020-06-13 22:11:37 -07:00
Wayne Davison c32012d199 Need to indent a code block in the README. 2020-06-13 21:31:26 -07:00
Wayne Davison 9ba6ce1b67 More release improvements. 2020-06-13 21:21:26 -07:00
Wayne Davison ca9e247762 Mention renamed .md files. 2020-06-13 20:42:33 -07:00
Wayne Davison f27a630e46 Don't use c++ comments. 2020-06-13 20:12:15 -07:00
Wayne Davison 243a9d9be0 A few more release script fixes. 2020-06-13 20:11:06 -07:00
Wayne Davison c528f8d5c8 Preparing for release of 3.2.0pre1 2020-06-13 19:16:26 -07:00
Wayne Davison 1d1c0f14e1 Make -4 & -6 also able to affect an ssh remote shell. 2020-06-13 19:15:02 -07:00
Wayne Davison e63ff70eae Some indentation fixes. 2020-06-13 19:15:02 -07:00
Wayne Davison 8a70f1420b Some fixes for the release script & other helpers. 2020-06-13 19:15:00 -07:00
Wayne Davison cdf58a7aba Change alt_dest_name() to alt_dest_opt(). 2020-06-13 12:04:13 -07:00
Wayne Davison 1d6c9676f9 Change 3 alt-dest vars to just one + some defines. 2020-06-13 11:47:08 -07:00
Wayne Davison 3d29fa99ec Tweak a couple var names. 2020-06-13 11:47:04 -07:00
Wayne Davison d167935874 Change a function name. 2020-06-13 03:03:33 -07:00
Wayne Davison d326961290 Fix overzealous setting of mtime & tweak time comparisons
- Stop setting the mtime on a file we didn't transfer (or didn't verify
  the checksum) when the time diff is within the modify window.
- Stop computing a time difference (-1|0|1) when all we care about is
  time equality.
2020-06-13 02:41:30 -07:00
Wayne Davison 7dec4029ee Convert a couple files to UTF-8; more Copyright years. 2020-06-13 02:41:04 -07:00
Wayne Davison ab0189c813 Make use of poptDupArgv(). 2020-06-12 23:28:27 -07:00
Wayne Davison bb484a799e The unalias argv array needs room for a trailing NULL. 2020-06-12 23:19:14 -07:00
Wayne Davison ad9f1571ce Add hashtable to delete_in_dir() to fix -x deletions 2020-06-12 17:42:56 -07:00
Wayne Davison f800557824 Tweak the hashtable routines to be a little clearer and easier. 2020-06-12 17:42:41 -07:00
Wayne Davison 13f81f4aa7 Tweak a usage message. 2020-06-12 10:20:33 -07:00
Wayne Davison d3ae752c53 Fix running prepare-source from a separate build dir. 2020-06-12 09:29:42 -07:00
Wayne Davison e3437244b5 Improve how the help lines are harvested from the md file. 2020-06-12 08:50:51 -07:00
Wayne Davison 1efeb59166 Enable SIMD by default (if g++ is around). 2020-06-12 08:29:36 -07:00
Wayne Davison d4fc18f375 Use the refused-option code to disable options that aren't compiled into the source. 2020-06-11 22:53:29 -07:00
Wayne Davison 58680edb12 Improve checkcsum/compress info that may differ between packaged versions. 2020-06-11 22:03:10 -07:00
Wayne Davison 34141954c7 Add packaging notes to NEWS. 2020-06-11 20:35:18 -07:00
Wayne Davison cba00be622 Translate man page's option list into .h files for options.h to use. 2020-06-11 20:34:23 -07:00
Wayne Davison de78dd685b Simplify the install of rsync-ssl by unifying 2 scripts. 2020-06-11 20:26:56 -07:00
Wayne Davison 88abb50229 Promote newer compressors to the start of the list. 2020-06-11 20:26:56 -07:00
Wayne Davison 6d6b8595df Remove generated doc files via make clean. 2020-06-11 20:26:45 -07:00
Wayne Davison 66bd4774a8 Allow maintainer to build with /usr/local prefix but document /usr. 2020-06-11 19:15:08 -07:00
Wayne Davison c117fa4bf9 Create a get_device_size() helper function. 2020-06-11 16:09:36 -07:00
Wayne Davison b040825b86 Improve the haproxy header docs. 2020-06-11 15:23:35 -07:00
Wayne Davison 3c793ef153 Use /dev/shm instead of requiring /dev/shm/tmp. 2020-06-11 14:33:25 -07:00
Wayne Davison cff0764b7f Add haproxy header parameter to rsync daemon 2020-06-11 14:22:25 -07:00
Wayne Davison a3377921eb Add early exec daemon parameter.
Inspired by Ciprian Dorin Craciun's `bootstrap exec` patch.
2020-06-10 21:38:37 -07:00
Wayne Davison a61ffbafe5 Make sure the tmpdir2 dir is writable. 2020-06-10 13:59:02 -07:00
Wayne Davison 190b474610 Mention how to run a single test. 2020-06-10 13:59:02 -07:00
Wayne Davison 85e90b0f80 Update copyright year in runtests.sh too. 2020-06-10 13:59:02 -07:00
Wayne Davison 516ca6a442 Add support for /run/shm/tmp dir so the CI action doesn't skip a test. 2020-06-10 13:17:41 -07:00
Wayne Davison fe993ca94d Have the CI actions run make check29 & check30. 2020-06-10 12:02:40 -07:00
Wayne Davison f8683063fb Fix a couple batchfile issues. 2020-06-10 11:23:14 -07:00
Wayne Davison 7c83eb6e63 Improve gensend rule & list of PHONY targets. 2020-06-10 09:44:23 -07:00
Wayne Davison 58e8ecf48f Improvements for release process; a gensend hook. 2020-06-10 09:31:01 -07:00
Wayne Davison c5e44330a5 Fix double-gen of manpages. 2020-06-10 00:39:30 -07:00
Wayne Davison ae82762c31 Fix the output with -D; a few minor tweaks. 2020-06-10 00:26:29 -07:00
Wayne Davison 2ac7401b44 Mention the github rsync home. 2020-06-09 18:08:29 -07:00
Wayne Davison 44af79223e The samba rsync server now requires ssl. 2020-06-09 17:28:07 -07:00
Wayne Davison 03fc62ad2f More man processing improvements
- Support the commonmark library in addition to cmarkgfm.
- Remove github-flavor from the markup.
- A few more html style improvements.
2020-06-09 17:02:41 -07:00
Wayne Davison 68c865c9e6 A few more man page script improvements. 2020-06-09 09:17:37 -07:00
Wayne Davison 6dc94e39a7 Output the files at the end; fix a missing double-quote. 2020-06-09 00:58:07 -07:00
Wayne Davison 8146b04ffb Fix the html title. 2020-06-08 22:52:58 -07:00
Wayne Davison 5b19cf7875 A couple man page tweaks. 2020-06-08 22:48:14 -07:00
Wayne Davison 53fae55652 Change man page src format from yodl to markdown.
This removes the yodl dependency, which is sometimes hard to track down.
Instead, this uses a python3 script that leverages the cmarkgfm library
to turn the source file into html.  Then, the script parses the html in
order to turn the tag stream into a nroff stream using a simple state
machine. While it's doing that it also implements one added format rule
that turns an ordinal list that starts at 0 into a description list
(since markdown doesn't have an easy description list idiom).
2020-06-08 21:03:42 -07:00
Wayne Davison bd66a92e7c Tweak the new heading 2020-06-07 19:46:22 -07:00
Sebastian Andrzej Siewior 165ef61de3 Add OpenSSL license exception also to COPYING
Add the OpenSSL license exception also to the COPYING file which
contains the license related information.

Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
2020-06-07 19:46:22 -07:00
Wayne Davison 7dbbde8c5e Use ZSTD_CLEVEL_DEFAULT define. 2020-06-07 19:30:24 -07:00
Wayne Davison 888f4f9503 Put the rsync-ssl-rsh helper script into a lib dir. 2020-06-07 19:25:18 -07:00
Wayne Davison 2c6f0581ac A couple minor fixes. 2020-06-04 22:54:38 -07:00
Wayne Davison 916faecb83 Only sender can output non-final stats on error
The receiving side's stats are split between 2 processes until the very end.
2020-06-04 21:40:43 -07:00
Wayne Davison 5d7b71b7a7 Make use of O_NOFOLLOW if it is defined. 2020-06-04 19:47:59 -07:00
Wayne Davison 0dde65a26b Mention more NEWS items. 2020-06-04 19:08:04 -07:00
Wayne Davison b177311aee Use a lock to not fail on a left-over pid file. 2020-06-04 19:08:03 -07:00
Wayne Davison 778f0dff9b Use more switch statements. 2020-06-04 16:17:12 -07:00
Wayne Davison 342579aa6f Make -V the short opt for --version. 2020-06-04 15:52:38 -07:00
Wayne Davison 01b9bbb0f9 Avoid a deadlock due to huge amounts of verbose messages.
Allow the receiver to increase their iobuf.msg xbuf if it fills up. This
ensures that the receiver will never block trying to output a message,
and thus it will always drain the data from the sender and keep the
whole thing from clogging up.
2020-06-04 14:20:51 -07:00
Wayne Davison 852a0b29c3 Tweak --copy-as docs a bit more. 2020-06-04 13:07:50 -07:00
Wayne Davison 55290c8584 Add hostname "lh" as a --no-cd localhost. 2020-06-04 12:58:02 -07:00
Wayne Davison e633091d23 Fix change_dir() leaving appended slash in curr_dir on failure. 2020-06-04 12:35:24 -07:00
Wayne Davison 4c9fdb9f74 Handle --skip-compress right for new compressors
Some compressors can't completely turn off, so minimize the level
when a file is being "skipped".
2020-06-02 18:06:09 -07:00
Wayne Davison e0d30a22d7 Mention that rsync --version outputs checksum & compress lists. 2020-06-02 17:21:51 -07:00
Wayne Davison 42ec4e3090 Update ccpp.yml
Switch to ubuntu-20.04 and add a couple dev libs.
2020-06-02 17:20:22 -07:00
Wayne Davison 3735002751 Some improvements to the release mechanism
- Some manpage changes to make them more consistent and to add a section
  that the release script expects in rsync-ssl.
- Fixed some issues in release-rsync pertaining to various file changes,
  such as the .md file changes.
- Change the gpg handling to stop prompting for a passphrase since gpg
  now makes use of gpg-agent (and the old gpg script is apparently not
  passing through fd 2 that git needs to get status).
2020-06-02 16:54:07 -07:00
Wayne Davison d47a80c05e Move the CSUM defines. 2020-06-01 18:49:15 -07:00
Wayne Davison 9dd9952138 A few style tweaks. 2020-06-01 18:38:06 -07:00
Jorrit Jongma 71c4ae2336 Move OpenSSL-related MD4/5 defines and imports to lib/mdigest.h
Works just as well, prevents having to repeat them across files
2020-06-01 17:57:38 -07:00
Wayne Davison c0268d9217 Some improvements for --msgs2stderr and --daemon.
- Set am_daemon to -1 (from 1) when the daemon is run via rsh.
- Only disable --msgs2stderr for a normal (socket) daemon.
- Forward a -q to the server if --msgs2stderr was also specified.
- Added --no-msgs2stderr option to allow it to be overridden.
2020-05-31 16:02:46 -07:00
Wayne Davison da448cdc99 Mention the latest NEWS items. 2020-05-30 05:54:09 -07:00
Wayne Davison d619a87aa5 Avoid noop_io_until_death() if --msgs2stderr was specified. 2020-05-30 05:53:59 -07:00
benrubson a931301bef Search for double-fuzzy files only when needed 2020-05-29 23:13:15 -07:00
Wayne Davison 265b0bc9bb Silence a strncpy() warning. 2020-05-29 17:35:56 -07:00
Wayne Davison 13f249a826 Silence some g++ warnings. 2020-05-29 17:24:15 -07:00
Wayne Davison c66e08acb3 Give configure's snprintf() test a guaranteed short string at the start. 2020-05-29 17:24:05 -07:00
Wayne Davison f5446552f3 Silence gcc7.1 warnings about snprintf(). 2020-05-29 14:18:08 -07:00
Wayne Davison 364d302bca Fix regex issue due to python 3.8 bug. 2020-05-28 20:48:24 -07:00
Wayne Davison 60e71c1b8b A few minor xxhash changes. 2020-05-28 20:40:23 -07:00
Wayne Davison 342921eb97 Merge pull request #5 from benrubson/daemonstats
Have daemon log data sent/received even when exiting with an error.
2020-05-28 13:42:47 -07:00
Wayne Davison df0ed76a76 Add stub for canonical_checksum(). 2020-05-28 12:46:46 -07:00
Wayne Davison d7521f5428 The xxh* checksums don't need to be reversed on output. 2020-05-28 12:33:36 -07:00
Wayne Davison c7f10de442 Switch to using LZ4_compress_default(). 2020-05-28 11:40:52 -07:00
Wayne Davison f60bd811e9 Use MSG_FLUSH in a couple more spots. 2020-05-28 00:41:39 -07:00
Wayne Davison cd0637a953 Merge pull request #2 from benrubson/flush
Help final error messages get to the sender. Is particularly helpful when talking with an older rsync client. Adds a new flush type of MSG_FLUSH.
2020-05-27 23:35:25 -07:00
Wayne Davison 8809f65b13 A couple minor tweaks. 2020-05-26 22:36:56 -07:00
benrubson c906619620 Log data sent/received even if error 2020-05-26 19:53:25 +02:00
Wayne Davison 5710d2fe2e Simplify the capabilities array. 2020-05-26 08:05:24 -07:00
benrubson 32fe5fbc11 Correctly send last error to sender 2020-05-26 16:24:30 +02:00
Wayne Davison bcb0a24a8f Convert NEWS & OLDNEWS into .md files. 2020-05-26 02:24:33 -07:00
Wayne Davison 96ed96dabd Fix the parsing of the --version capabilities. 2020-05-25 23:42:51 -07:00
Wayne Davison 8ad2ca9ae2 Install yodl for the CI builds. 2020-05-25 23:42:51 -07:00
Wayne Davison 23a37ecac4 Get indent right. 2020-05-25 23:33:11 -07:00
Wayne Davison 47bae3abf6 Improve output of capabilities in --version list.
It now wraps better as the "no " prefixes change, and it makes it easier
to maintain patches that add items into the capabilities list.
2020-05-25 23:24:46 -07:00
Wayne Davison dff9dd56a0 Remove xxhash from capabilities list.
It's now listed in the "Checksum list:" output.
2020-05-25 21:31:40 -07:00
Wayne Davison 7182508a75 Fix a couple deb names. 2020-05-25 21:21:58 -07:00
Wayne Davison cb0fe9e195 Improve CI setup. 2020-05-25 21:17:51 -07:00
Wayne Davison 87f2984df0 Improve how negotiated info affects batch files. 2020-05-25 19:19:59 -07:00
Wayne Davison 24ce3e9d54 Tweak the --zz option to --zc (aka --compress-choice). 2020-05-25 16:57:47 -07:00
Wayne Davison 98cddfaf7a Rename a couple files to .md 2020-05-25 15:05:06 -07:00
Wayne Davison d1fcb8ce9d Add some extra indent. 2020-05-25 14:59:51 -07:00
Wayne Davison b4ace35304 Create ccpp.yml 2020-05-25 14:43:25 -07:00
Wayne Davison 888ce058d8 Two sparse fixes from Yuxuan Shui.
- Make "len" parameter of do_punch_hole an OFF_T.
- Clear sparse_past_write in sparse_end(), otherwise when write_sparse()
  is called for the next file, do_punch_hole() will be called with a pos
  that's not actually the current position in file, causing it to fail.
2020-05-25 14:01:52 -07:00
Wayne Davison c394e86184 Include lz4 compression support.
Based on a patch that was emailed to me without a valid return address.
2020-05-25 13:45:56 -07:00
Wayne Davison 4aaadc2f29 Include zstd compression support.
Based on a patch by Sebastian A. Siewior. Fixes bug #14338.
2020-05-25 13:44:48 -07:00
Wayne Davison abef92c037 Fix handling of a compressor that has no off_level. 2020-05-25 13:02:56 -07:00
Wayne Davison 87019d7721 Output the default checksum & compress lists in the --version output. 2020-05-25 13:02:12 -07:00
Wayne Davison fc265c5a92 A couple minor configure.ac tweaks. 2020-05-25 11:50:44 -07:00
Wayne Davison d999efe6e5 Make compression-level handling generic. 2020-05-25 11:18:51 -07:00
Wayne Davison 97e8c55ee8 Some minor tweaks & tidying up. 2020-05-24 22:50:51 -07:00
Wayne Davison 739fa96737 Change odd-ball map_ptr() to use remainder like the others. 2020-05-24 20:38:48 -07:00
Jorrit Jongma d474f2986e Improve performance of file_checksum()
Previously files were hashed in blocks of CSUM_CHUNK (64) bytes. This
causes significant overhead. The CSUM_CHUNK define cannot be changed as
md5.c depends on it, but there is no obvious reason to use it in
file_checksum(). By using CHUNK_SIZE (32 kB) instead, in some test
cases throughput more than doubles.
2020-05-24 20:33:33 -07:00
Wayne Davison a863c62cd1 More NEWS updates. 2020-05-24 20:19:15 -07:00
Wayne Davison 5ac353d845 Prefer zlibx compression consistently instead of having 2 possible default preference orders. 2020-05-24 19:52:08 -07:00
Wayne Davison faecd066a6 Don't auto-foward debug options to the server side anymore. 2020-05-24 19:37:15 -07:00
Wayne Davison 6efc43cc0a Fix -z choice with older rsyncs. 2020-05-24 19:16:05 -07:00
Wayne Davison 4496e0e8e7 A few more compression tweaks. 2020-05-24 18:43:03 -07:00
Wayne Davison 64d5ea39c0 More compress changes
- Add the zlibx (external-code compatible) compression name.
- Re-enable zlib support with the external library so it can be
  tried as a fallback if zlibx isn't available.
- Add --compress-choice=STR (aka -zz=STR) option.
- Make --cc=STR an alias for --checksum-choice=STR.
- Hook up the new compression negotiation logic.
2020-05-24 17:24:42 -07:00
Wayne Davison 4af8403aa2 Fix negotiation of none & improve NSTR debug msgs. 2020-05-24 13:49:06 -07:00
Wayne Davison 2f84a6bd73 Add support for negotiated checksum names. 2020-05-24 13:22:19 -07:00
Wayne Davison eda15d52a8 Make xxh64 the "main_name" for the current xxhash. 2020-05-24 02:10:05 -07:00
Wayne Davison 741d5f10c6 Fix some warnings. 2020-05-24 02:04:14 -07:00
Wayne Davison 4f92fd8ddd Some more checksum improvements
- Add/improve --debug=CSUM2 messages.
- Add an "xxh64" alias for "xxhash" name because we should be
  getting a few more xxhash variations in the future.
- Tweak the matching code to handle entries that have multiple
  names.
- Tweak some of the vars/defines.
2020-05-24 01:00:53 -07:00
Wayne Davison 7f2359a5cc Improve some early debug-message newlines.
Avoid a newline issue during the output of --DEBUG=CSUM info from
both the server and the client -- we need to output the full message
with its newline as much as possible.
2020-05-23 21:51:28 -07:00
Wayne Davison 6e942e5898 Avoid re-evaluating the args of SIVAL* w/CAREFUL_ALIGNMENT. 2020-05-23 21:43:53 -07:00
Wayne Davison 1c9bb168bb Unify the checksum context memory, since we only use one at a time. 2020-05-23 18:52:03 -07:00
Wayne Davison 799de21af6 Fixed the use of openssl MD4 for transfer checksums. 2020-05-23 16:22:36 -07:00
Wayne Davison 1cb1edeb68 Optional openssl support for MD4 pre-transfer checksums (but, sadly, not transfer checksums). 2020-05-23 12:30:58 -07:00
Wayne Davison 15c1162b24 Add optional use of the openssl crypto lib for MD5. 2020-05-23 10:06:59 -07:00
Wayne Davison a7175ee029 Mention a few more news items. 2020-05-22 23:26:25 -07:00
Wayne Davison 68516f91be Add "input" handling for cmd_txt_*() pkglib.py. 2020-05-22 22:49:02 -07:00
Jorrit Jongma 531ffa8104 Optimized assembler version of md5_process() for x86-64
Originally created by Marc Bevand and placed in the public domain.
Enable/disabled via the same --enable-simd configure switch as
the rolling checksum optimizations.
2020-05-22 22:37:21 -07:00
Wayne Davison d7212df0f1 A little more safety in negotiate_checksum(). 2020-05-22 19:29:05 -07:00
Wayne Davison a28bc3ebf6 Promoting xxhash support. 2020-05-22 17:59:12 -07:00
Wayne Davison 55bb4dab7a Some checksum improvements
- Improve csum negotation logic.
- Define the csum names in a single structure.
- Add --debug=CSUM.
2020-05-22 17:59:12 -07:00
Jorrit Jongma 5fa4209ca0 AVX2 optimized version of get_checksum1() for x86-64
Additionally restructures build switches and defines from SSE2 to SIMD,
to allow potential reuse should patches become available with SIMD
instructions for other processor architectures.

(Some minor tweaks of Jorrit's patch to avoid requiring GNU make and to
avoid C++ comments in .c files.)
2020-05-22 11:31:31 -07:00
Wayne Davison 4f6c8c6652 Checksum negotiation & more bits for compat_flags
- Add checksum negotiation to the protocol so that we can easily add new
  checksum algorithms and each will be used when both sides support it.
- Increase the size of the compat_flags value in the protocol from a
  byte to an int.
2020-05-22 09:52:14 -07:00
Wayne Davison a7303a3d3d Fix a bug in the writing of the batch.sh file
Fix the code that writes the options and the default destination path
into the batch.sh file to be able to handle options being specified
after source/dest args.
2020-05-22 08:27:07 -07:00
Jorrit Jongma 70c6b408dc SSE2/SSSE3 optimized version of get_checksum1() for x86-64
Requires compilation using GCC C++ front end, build scripts have been
modified accordingly. C++ is only used when the optimization is enabled
(g++ as compiler, x86-64 build target, --enable-sse2 is passed to
configure).

(Wayne made a few tweaks, including making it disabled by default.)
2020-05-21 14:41:55 -07:00
Wayne Davison be7af36c51 Tweak the accept/refuse strings a bit. 2020-05-18 00:06:06 -07:00
Wayne Davison 3b36bde953 Add back a lost "*" and document the refusing of log-file* opts. 2020-05-17 23:28:35 -07:00
Wayne Davison c3986d4c5a More manpage improvements for "refuse options". 2020-05-17 22:19:25 -07:00
Wayne Davison b3a1a0ca9d Add the ability to negate matches for the daemon's "refuse options". 2020-05-17 21:29:11 -07:00
Wayne Davison e448d31d63 Need to flush early errors before we exit. 2020-05-17 21:20:15 -07:00
Wayne Davison 37de48979e Some pkglib improvements. 2020-05-17 20:32:43 -07:00
Wayne Davison 08f955e17b A couple more manpage fixes. 2020-05-13 00:20:03 -07:00
Wayne Davison 3435ae9bd0 A bit more manpage tweaking. 2020-05-13 00:11:57 -07:00
Wayne Davison 7a9295778c Change r'\1%s\2' to r'\g<1>%s\2'. 2020-05-06 17:23:34 -07:00
Wayne Davison f7746d0000 A couple extra function checks for future features. 2020-04-29 22:14:49 -07:00
Wayne Davison 96a6ea0f26 Convert another packaging script to python3. 2020-04-29 21:25:17 -07:00
Wayne Davison 6242786158 A few superficial tweaks. 2020-04-29 19:41:56 -07:00
Wayne Davison b430ceec7a Use a varint to send the file-list flags
If both sides support the "V" compatibility flag, we send the file-list
flags as a varint instead of a 1-or-2 byte value.  This upgrades the
number of reserved flag bits from 1 to 17 with very few extra bytes in
typical file-list data.
2020-04-29 18:22:52 -07:00
Wayne Davison 3a7bf54ad5 A resumed partial-dir file is transferred in-place.
Fixed bug #13071.
2020-04-29 17:02:14 -07:00
Wayne Davison c1cb307b4b Fix a couple issues with the atime file-list value. 2020-04-26 18:42:37 -07:00
Wayne Davison af6118d98b Allow a missing parent dir when --delete-missing-args was specified. 2020-04-26 18:10:40 -07:00
Wayne Davison ea3337a210 Add extensions to the default no-compress list.
Fixes bug #13749.
2020-04-26 17:07:58 -07:00
Wayne Davison 035a13f7e4 Add a few new opts to rrsync. 2020-04-26 14:54:45 -07:00
Wayne Davison f14adfd75e Some var cleanup; move test-util vars into t_stub.c. 2020-04-26 14:54:43 -07:00
Wayne Davison d218be3482 Update a few more copyright years. 2020-04-25 23:34:16 -07:00
Wayne Davison f4c077e85e Change pending version to 3.2.0 (currently 3.2.0dev). 2020-04-25 23:30:42 -07:00
Wayne Davison 1c7785ab1e Change do_setattrlist_times() to use an stp arg. 2020-04-25 21:39:11 -07:00
Wayne Davison 87257f869c Change --set-notime to --open-noatime. 2020-04-23 14:32:26 -07:00
Wayne Davison b936741032 Added --atimes and --set-noatime options. 2020-04-23 13:24:15 -07:00
Wayne Davison 2b2a3c87b6 Mention more changes in the NEWS. 2020-04-23 13:18:07 -07:00
Wayne Davison 6e962ac51e Eliminate .in for rsync-ssl. 2020-04-22 14:53:06 -07:00
Wayne Davison 991ab811cb Turn nightly-rsync into a python script. 2020-04-22 12:43:25 -07:00
Wayne Davison 3249824264 Some more rsync-ssl improvements:
- Make the rsync-ssl default behavior more user friendly.
- Install rsync-ssl & rsync-ssl-rsh in the regular install rules.
- Add a manpage for rsync-ssl (which is also installed).
- Get rid of the rsync-ssl-client package in our spec file.
2020-04-22 12:40:14 -07:00
Wayne Davison 1c465dc33a Change the name of the rsh-ssl-rsync script. 2020-04-22 10:52:01 -07:00
Wayne Davison 2a87d78f69 Change the rsync-ssl helper script
The new rsh-ssl-rsync helper script (replacing stunnel-rsync) supports
openssl in addition to stunnel.  The RSYNC_SSL_TYPE environment variable
can be set to specify which type of connection to use, and the first arg
to rsync-ssl can be --type=stunnel or --type=openssl to override the env
var or the default of "stunnel".  The helper script now looks for
stunnel4 or stunnel on the PATH at runtime instead of having configure
look for it at compile time.
2020-04-19 14:00:33 -07:00
Wayne Davison 3ba4db7030 Two more spelling fixes and some year updates. 2020-04-16 09:31:02 -07:00
Wayne Davison d29702134a Spelling fixes from a Fossies run done by Jens. 2020-04-15 17:42:23 -07:00
Wayne Davison 1c82a1e1e5 A few file-data improvements. 2020-04-12 15:51:20 -07:00
Wayne Davison 2d0c7adba9 Change some packaging tools into python3 and make a few improvements. 2020-04-12 00:13:35 -07:00
Wayne Davison 0b4b31a960 Add a (pending) release line for 3.1.4. 2020-04-11 17:59:22 -07:00
Wayne Davison 8a21e5a8c6 Use the --quiet option. 2020-04-09 18:42:31 -07:00
Wayne Davison fa9c8d04d4 Put the year-tweak script into packaging dir. 2020-04-09 15:17:09 -07:00
Wayne Davison c5fabfb068 Set Copyright years and make them easier to update
I replaced git-set-file-times with an improved version that I wrote
recently (in python3). A new script uses it to figure out the
last-modified year for each *.[ch] file and updates its copyright.
It also puts the latest year into the latest-year.h file for the
output of --version.
2020-04-09 15:11:37 -07:00
Wayne Davison e2aee6c4af Switch RSYNC_PORT to -1 in check_for_hostspec(). 2020-04-07 19:21:37 -07:00
Wayne Davison 2598ca668b Fix the default skip-compress list.
The default value of the skip-compress list actually comes from the
daemon's default lp_dont_compress() value, but a while back the vars
stopped getting default values in a non-daemon run. I added a call to
reset_daemon_vars() so that the "Vars" values get set from "Defaults".
2020-04-07 18:15:09 -07:00
Wayne Davison cd7ad50bc8 Tweak the grep to look for sys/sysmacros.h. 2020-04-07 15:32:06 -07:00
Wayne Davison 5e4a1441cb Avoid the include warnings for major(). 2020-04-07 15:16:19 -07:00
Wayne Davison 9dea2ae87c Make use of the new RSYNC_PORT env var. 2020-04-07 13:36:01 -07:00
Ethan Sommer via rsync 795268bb7c Replace mkproto.pl with mkproto.awk
This replaces the build dependency on perl with one on awk which is
already used throughout the build system and is much more ubiquitous
than perl.
2020-04-07 13:08:05 -07:00
Wayne Davison 70cbc66b7f Set RSYNC_PORT in the env for a daemon-over-rsh connection.
Fixes bug #14163.
2020-04-05 19:34:27 -07:00
Wayne Davison b63276e70f A quick fix for some perl patch-helper scripts. 2020-04-05 17:18:32 -07:00
Wayne Davison b0c03e2be9 Another tweak for a change in git status. 2020-04-05 17:12:29 -07:00
Wayne Davison 8475e0e492 Tweak some indentation. 2020-04-05 17:03:15 -07:00
Wayne Davison 7f06cc7ed0 Don't throw an error if a potential fuzzy dir isn't a dir
Add a flag for calling get_dirlist() and for send_directory() that
indicates that the dirname is allowed to not be a directory.  Based
on a patch by Ben Rubson.  Fixes bug #13445.
2020-04-05 16:41:15 -07:00
Wayne Davison 10d40508e6 Use "exit 1" in atomic-rsync for error exit.
Fixes bug #15469.
2020-04-05 16:23:07 -07:00
Wayne Davison daf8f7a669 Some configure improvements for strict C99 compilers (based on a patch by Florian Weimer). 2020-04-05 16:19:54 -07:00
Wayne Davison 15fa9ab06d Add progress output via SIGINFO and SIGVTALRM
On BSD-ish systems you can type Ctrl+T to see the current file and
the progress output (in --info=progress2 format).  On hosts w/o
SIGINFO, use something like "killall -VTALRM rsync" or a more
targetted "kill -VTALRM PID ..." call (as needed).
2020-04-05 15:07:31 -07:00
Wayne Davison 7e70e4842b No need to forward --write-devices to a remote sender. 2020-04-05 12:01:48 -07:00
Wayne Davison 9e9d33a2db Added the --write-devices option.
This is a fleshed out version of the old one in the patches repo with
documentation & proper handling of the implied --inplace option for a
daemon's option-rufusing considerations. I ommitted the -w short option
as I would hate for someone to turn this on accidentally.
2020-04-05 11:56:28 -07:00
Wayne Davison b32aa4797d Make exit_cleanup() use _exit() if called from a signal handler.
Fixes bug #13982.
2020-04-05 10:26:40 -07:00
Wayne Davison 826ddc5403 Enhance the validation of --block-size for older protocols.
Fixes bug #13974.
2020-04-05 10:05:25 -07:00
Wayne Davison 3bd4e1e8cd Make the --copy-links caveat a little clearer. 2020-04-05 09:43:59 -07:00
Wayne Davison 51e23e0ab7 Use nanosleep if it is available.
Fixes bug #14328.
2020-04-05 09:22:00 -07:00
Wayne Davison 08650cb14c Add a --copy-as=USER[:GROUP] option
This can be used by a root-run rsync to try to make reading or writing
files safer in a situation where you can't run the whole rsync command
as a non-root user.
2020-03-29 13:18:20 -07:00
Wayne Davison 24c28cd715 Match the latest git "clean" text. 2019-03-19 09:35:59 -07:00
Wayne Davison c0c6a97c35 Try to fix the iconv crash in bug 11338.
Applying Michal Ruprich's suggested patch for the rwrite() function that
should hopefully help with a bug that I couldn't reproduce.
2019-03-16 11:51:49 -07:00
Wayne Davison d47d379216 Fix bug in try_dests_reg that Florian Zumbiehl pointed out.
If the alternate-destination code was scanning multiple alt dirs and it
found the right size/mtime/checksum info but not the right xattrs, it
would keep scanning the other dirs for a better xattr match, but it
would omit the unchanged-file check that needs to happen first.
2019-03-16 11:12:53 -07:00
Wayne Davison eb1b138ec2 Clarify the cut-off point for --copy-safe-links. 2019-03-16 10:55:50 -07:00
Wayne Davison 13f596433b Some doc tweaks suggested by Clément Pit-Claudel. 2019-03-16 10:10:14 -07:00
Wayne Davison 3fe4469bfa Fix zlib CVE-2016-9843. 2019-03-16 09:56:11 -07:00
Wayne Davison 8eb50bce43 Fix zlib CVE-2016-9842. 2019-03-16 09:56:11 -07:00
Wayne Davison fc10fafa25 Fix zlib CVE-2016-9841. 2019-03-16 09:56:11 -07:00
Wayne Davison efcbec3df5 Fix zlib CVE-2016-9840. 2019-03-16 09:56:11 -07:00
Wayne Davison 3e2e4b5a33 Tweak the copyright year. 2019-03-16 09:15:49 -07:00
Wayne Davison 79332c0d66 Fix --remove-source-files sanity check w/--copy-links the right way.
Fixes bug #10494.
2019-03-16 09:09:09 -07:00
Wayne Davison bdfc296faf Handle a run from down inside the checkout tree. 2019-03-15 12:20:55 -07:00
Wayne Davison 2ad1c4e800 Improve write-only --sender check & handle 2 new options. 2019-01-15 11:18:36 -08:00
Wayne Davison 0da7ba57b5 Update option culling to handle latest changes. 2019-01-15 11:16:50 -08:00
Wayne Davison b3d12c5a3d Use a separate pass-by-value pointer for clarity. 2019-01-15 10:46:29 -08:00
Wayne Davison bc7402aa3a Avoid warning about leaked mem (didn't affect rsync's pool use). 2019-01-15 10:46:29 -08:00
Wayne Davison f233dffbd6 Avoid leaving a file open on error return. 2019-01-15 10:38:00 -08:00
Wayne Davison c2da3809f7 Fix --prealloc to keep file-size 0 when possible. 2019-01-15 08:59:35 -08:00
Wayne Davison 48346c878f Reject --log-file when read-only. 2019-01-09 13:35:21 -08:00
Wayne Davison a0274c08b5 Improve check for ".." and guard against dash args. 2019-01-09 13:35:21 -08:00
Wayne Davison f627e27749 Save each expanded daemon-config string on first use to
avoid a new alloc on every use (one that was not freed).
2019-01-09 13:35:21 -08:00
Wayne Davison 0b6cae6792 No need to strdup each new section since we stopped using free(). 2019-01-08 20:30:58 -08:00
Wayne Davison e5610f1877 Avoid a yodl macro warning. 2019-01-08 16:39:48 -08:00
Wayne Davison c376170644 Make sure that some memory zeroing always happens. 2019-01-08 14:46:41 -08:00
Wayne Davison 48163179eb Avoid a yodl macro warning. 2019-01-08 13:38:19 -08:00
Wayne Davison b4c1b27e03 Fix 2 spelling errors pointed out by bug 13734. 2019-01-08 13:34:32 -08:00
Wayne Davison c90b87e021 Avoid a failed test if dirs report 1 hlink (e.g. WSL weirdness). 2019-01-04 21:43:50 -08:00
Wayne Davison ad17b21889 Silence fall-through warnings. 2019-01-04 15:06:30 -08:00
Wayne Davison a366868535 Avoid a potential out-of-bounds read in daemon mode if argc is 0. 2018-12-15 16:59:18 -08:00
Wayne Davison f55d35c5a0 Try to be clearer that --append-verify isn't a general-purpose-copy option. 2018-11-20 14:17:32 -08:00
Wayne Davison 6af8e11450 Don't force cygwin to solaris ACLs anymore. 2018-11-20 14:11:42 -08:00
Wayne Davison 1a288c06d9 Document how a leading comma changes the gid parsing. 2018-11-20 13:44:09 -08:00
Wayne Davison 4aeb093206 Fix itemizing of wrong dir name on some --iconv transfers.
Fixes bug #13492.
2018-11-20 13:21:32 -08:00
Wayne Davison 1eb7a7061a Need to mark xattr rules in get_rule_prefix().
This fixes the bug of xattr filters getting sent as a normal filter rule
(since the 'x' was dropped in the prefix).
2018-06-14 15:22:53 -07:00
Wayne Davison eec6ab7615 Avoid a compiler error/warning about shifting a negative value.
Fixes bug #13268.
2018-03-25 19:11:41 -07:00
Wayne Davison 5df9847f06 Allow some pre-/post-xfer exec shell restrictions.
Support both RSYNC_SHELL & RSYNC_NO_XFER_EXEC environment variables.
2018-03-25 11:02:50 -07:00
Wayne Davison fb7a162f53 Prepare the repository for more development. 2018-03-25 10:04:29 -07:00
Wayne Davison d73762eea3 Preparing for release of 3.1.3 2018-01-28 15:24:27 -08:00
Wayne Davison d58405a353 Mention nanoseconds change. 2018-01-15 11:25:04 -08:00
Wayne Davison 0f8e9e2d86 Don't force nanoseconds if a file wasn't transferred or checksummed. 2018-01-15 10:58:31 -08:00
Wayne Davison c4a3f55be3 Preparing for release of 3.1.3pre1 2018-01-14 21:34:42 -08:00
Wayne Davison 473108ae6e Tweak copyright date. 2018-01-14 19:55:07 -08:00
Wayne Davison e401959b89 Mention more changes. 2018-01-14 19:51:25 -08:00
Jeriko One 7706303828 Ignore --protect-args when already sent by client
In parse_arguments when --protect-args is encountered the function exits
early. The caller is expected to check protect_args, and recall
parse_arguments setting protect_args to 2. This patch prevents the
client from resetting protect_args during the second pass of
parse_arguments. This prevents parse_arguments returning early the
second time before it's able to sanitize the arguments it received.
2018-01-09 17:51:30 -08:00
Wayne Davison f5e8a17e09 Fix issue with earlier path-check (fixes "make check")
and make a BOOL more explicit.
2017-12-03 16:27:11 -08:00
Jeriko One 5509597dec Check daemon filter against fnamecmp in recv_files(). 2017-12-03 16:13:06 -08:00
Jeriko One 70aeb5fddd Sanitize xname in read_ndx_and_attrs. 2017-12-03 16:13:05 -08:00
Jeriko One 3e06d40029 Check fname in recv_files sooner. 2017-12-03 16:12:28 -08:00
Wayne Davison 416e719bea More archaic-checksum improvements. This makes the len vars clearer
and ensures that only the flist code gets the 2-byte digest len.
2017-11-07 14:01:13 -08:00
Wayne Davison 9f5dc9309d Use the right sum len. 2017-11-07 13:32:10 -08:00
Wayne Davison b984e9dbd4 Replace startdit|enddit with description for newer yodl.
Fixes bug 13115.
2017-11-05 12:29:14 -08:00
Wayne Davison c60d9fcab1 Add missing closing paren that Paul Slootman pointed out. 2017-11-05 11:56:52 -08:00
Wayne Davison 47a63d90e7 Enforce trailing \0 when receiving xattr name values.
Fixes bug 13112.
2017-11-05 11:46:09 -08:00
Wayne Davison bc112b0e7f Use full MD4 len for archaic protocol auth. 2017-10-30 13:34:18 -07:00
Wayne Davison 8a82feeb7c Don't overflow an allocated dest buf when input path is empty.
Fixes bug 13105.
2017-10-29 15:53:28 -07:00
Wayne Davison 0350f95e7b Add an extra argc validation in do_server_sender().
Fixes bug 13104.
2017-10-29 15:52:56 -07:00
Wayne Davison 9a480deec4 Only allow a modern checksum method for passwords. 2017-10-24 20:44:37 -07:00
Wayne Davison c252546cee Don't forget to tweak sum_update(). 2017-10-24 20:44:20 -07:00
Wayne Davison 7b8a4ecd6f Handle archaic checksums properly. 2017-10-24 15:42:43 -07:00
Wayne Davison 17b849c97a Set our_uid & our_gid values when changed by the daemon.
Fixes bug 10719.
2017-10-09 17:13:00 -07:00
Wayne Davison 276a9836bd Mention --link-dest limit. 2017-10-08 09:30:18 -07:00
Wayne Davison c140b2e749 Mention refusing delete for write-only. 2017-10-08 09:18:10 -07:00
Wayne Davison b547302943 Mention -O is forced, not just implied. 2017-10-08 08:52:33 -07:00
Wayne Davison 136c6c7734 Fix double-fuzzy + link-dest issue.
Fixes bug 11866.
2017-10-08 08:39:37 -07:00
Wayne Davison 3fb1634f21 Fix possible buffer overrun for some large name_len values.
Fixes bug 12568.
2017-10-07 17:54:05 -07:00
Wayne Davison 881addc9e1 Add "daemon chroot|uid|gid" parameters.
This allows the daemon to run chrooted as any uid+gid you like
(prior to the transfer possibly changing the chroot and/or the
uid+gid further). Based on the patch in #12817.
2017-09-04 14:20:16 -07:00
Wayne Davison b7799aaefe Add nanosecond mtime support for Mac OS X.
Slightly tweaked the patch contributed by Heikki Lindholm.
2017-08-31 08:22:14 -07:00
Wayne Davison ce854cf021 Add "syslog tag" to rsync daemon config. 2017-04-29 13:49:14 -07:00
Wayne Davison 9e7b8ab7cf Don't allow --daemon or --server alias via popt.
Fixes bug 12576.
2017-02-20 11:09:16 -08:00
Wayne Davison 87bc224011 Add a way to specify xattr name filtering. 2017-01-22 16:01:45 -08:00
Wayne Davison a4e8b552d6 Join some lines. 2017-01-22 15:55:54 -08:00
Wayne Davison 62652202c4 Get rid of some superfluous double-quotes in error messages. 2017-01-22 15:42:36 -08:00
Wayne Davison 001adf5096 Fix extern of preallocated_len w/o SUPPORT_PREALLOCATION. 2016-10-31 09:06:50 -07:00
Wayne Davison ff66fd4bb6 More fixes for --progress quirks.
This patch avoids inconsistent evaluation of options in the
show_filelist_p() function by turning it into a var.  We
also avoid setting "output_needs_newline" if --quiet was
specified.
2016-10-29 15:26:10 -07:00
Wayne Davison e02b89d0d3 We need a LF after filelist-progress with a CR.
Fixes bug 12367.
2016-10-29 14:33:44 -07:00
Wayne Davison d1a1fec134 Use S_BLKSIZE when multiplying st_blocks. 2016-10-15 11:33:07 -07:00
Wayne Davison f3873b3d88 Support --sparse combined with --preallocate or --inplace.
The new code tries to punch holes in the destination file using newer
Linux fallocate features. It also supports a --whole-file + --sparse +
--inplace copy on any filesystem by truncating the destination file.
2016-10-10 11:53:03 -07:00
Stefan Metzmacher 6e3b2102bc xattrs: maintain a hashtable in order to speed up find_matching_xattr()
As a testcase I've used one directory on gpfs with 1000000 files,
each with an xattr called 'name$i' having a value of 'value$i'.
So we also have 1000000 unique xattrs. The source and dest directories
are already in sync before. So the rsync command is basically a noop,
just verifying that everything is already in sync.

The results before this patchset are:

  [gpfs]# time rsync -a -P -X -q source-xattr/ dest-with-xattr/

  real    8m46.191s
  user    6m29.016s
  sys     0m24.883s

  [gpfs]# time rsync -a -P -q source-xattr/ dest-without-xattr/

  real    1m58.462s
  user    0m0.957s
  sys     0m11.801s

With the patchset I got:

  [gpfs]# time /gpfs/rsync.install/bin/rsync -a -P -X -q source-xattr/ dest-with-xattr/

  real    2m4.150s
  user    0m1.917s
  sys     0m17.077s

  [gpfs]# time /gpfs/rsync.install/bin/rsync -a -P -q source-xattr/ dest-without-xattr/
  real    1m59.534s
  user    0m0.924s
  sys     0m11.599s

It means the time in userspace dropped from 6m29.016s down to 0m1.917s!
Without -X we get ~ 0m0.9s with or without the patch.

Part of a patchset for bug 5324.
2016-08-14 14:37:49 -07:00
Stefan Metzmacher cc29b94d0f hashtable: add hashlittle() from lookup3.c, by Bob Jenkins
Part of a patchset for bug 5324.
2016-08-14 14:20:19 -07:00
Stefan Metzmacher 6eb71beaff xattrs: introduce a rsync_xa_list struct as layer between two nested item_lists
We have the global 'item_list rsync_xal_l', this maintains an array
of rsync_xa_list structure, one per file.

Each rsync_xa_list structure maintains an array of rsync_xa structure,
while each represent a single xattr with name and value.

Part of a patchset for bug 5324.
2016-08-14 14:20:16 -07:00
Stefan Metzmacher 39d7e3ec25 xattrs: let rsync_xal_store() return ndx.
Part of a patchset for bug 5324.
2016-08-14 13:54:34 -07:00
Stefan Metzmacher ac97bc14f6 xattrs: add const to empty_xattr
Part of a patchset for bug 5324.
2016-08-14 13:54:34 -07:00
Greg Whiteley 31e93c3228 Makefile: rounding.h generation requires proto.h via rsync.h
Bug 12029 - Makefile missing dep gives parallel race for rounding.h

Signed-off-by: Greg Whiteley <greg.whiteley@gmail.com>
2016-07-20 08:34:26 -07:00
Wayne Davison a720d81d0a Fix "could not find xattr #1" errors.
The abbreviated-xattr code can get requests that are not in the same
order as the xattr list, so we need to support wrap-around scanning
of the available xattrs. Fixes bug 6590.
2016-06-26 12:06:09 -07:00
Wayne Davison 359758d611 Fix path check when prior_dir_file is NULL. 2016-06-04 11:53:33 -07:00
Wayne Davison 1f83b51d71 Improve the top-level section on include/exclude traversal.
This is my edit of some suggestions by Karl O. Pinc.
2016-05-07 15:55:26 -07:00
Wayne Davison a5a7d3a297 Add --checksum-choice option to choose the checksum algorithms. 2016-05-01 17:06:54 -07:00
Wayne Davison 4fc78878e0 Tweak indentation only. 2016-05-01 16:29:34 -07:00
Wayne Davison b973bffa94 If a backup fails (e.g. full disk) rsync should fail.
Fixes bug 11668.
2016-04-17 16:31:57 -07:00
Wayne Davison d1c80404fe Output "UNKNOWN" if starttime or endtime is -1.
Fixes bug 11382.
2016-04-17 16:11:04 -07:00
Wayne Davison 9a12959ab6 Support only splitting users/groups on commas.
Fixes bug 11817.
2016-04-17 15:56:11 -07:00
Wayne Davison 070c810e2d Tweak non-fatal output when man pages cannot be created. 2016-04-17 11:43:46 -07:00
Wayne Davison 71ec4609eb Fix use of obsolete compile macro.
Fixes bug 11813.
2016-04-17 11:20:38 -07:00
Wayne Davison 0f7db203fb Only output about new backup dirs when requested.
Fixes bug 11812.
2016-04-17 11:06:58 -07:00
Wayne Davison 4e25abc9a9 Fix/improve the sort functions.
Fixes bug 11704.
2016-01-31 14:40:51 -08:00
Wayne Davison 839dbff2aa Add support for comparing nanoseconds on the receiver.
This patch adds the ability to specify --modify-window=-1 (aka -@-1) to
ask rsync to compare files with the full nanosecond timestamps.  The
default is still -@0 for the moment, which ignores nanoseconds in time
comparisons.  Changing the default to -1 would cause a copy from ext4 to
ext3 to constantly compare as different, or a copy there and back again
to do a full copy as it zeroed all the nanosecond times.  Such a change
might be too much of a functional difference for things like backup
solutions to handle without a warning period.  The current plan is to
support nanosecond comparisons for those that want them, and possibly
change the default window value from 0 to -1 at some point in the
future.
2016-01-24 11:16:10 -08:00
Wayne Davison f6f5ea4173 Prepare the repository for more development. 2016-01-24 11:12:32 -08:00
Wayne Davison 9a37d9cb1e Fix a tweak that should have been untweaked. 2015-12-24 13:09:47 -08:00
Wayne Davison 16b49716d5 Preparing for release of 3.1.2 2015-12-21 12:00:49 -08:00
Wayne Davison 58faa1e8b9 Improve the "use chroot" & "numeric ids" info a bit more. 2015-12-21 11:56:33 -08:00
Wayne Davison 9250e9ac23 Improve the comment a bit more. 2015-12-21 10:54:02 -08:00
Wayne Davison 3623d94fe7 Fix rule for out-of-tree builds.
Fixes bug 11635.
2015-12-18 16:09:58 -08:00
Wayne Davison cbc42b9c16 Don't allow an empty flag name to --info & --debug. 2015-12-18 14:46:28 -08:00
Wayne Davison 6ff5824c25 Document expand_item_list's args & make sure incr==0 works OK. 2015-12-18 14:41:22 -08:00
Wayne Davison 32de6b7cb4 Fix return of stat info from try_dests_reg().
The try_dests_reg() function could sometimes tweak the stat struct's
info when it should have been left unchanged.  This fixes bug 11545
(where an ACL check of a file that was mistakenly thought to be a
directory failed).
2015-12-05 11:10:24 -08:00
Wayne Davison bb853b3205 Add -wo option for write-only rrsync mode. 2015-09-13 16:23:54 -07:00
Wayne Davison cce44865c1 Fixed logging of %b & %c when using --log-file.
The %b and %c escapes were outputting cumulative values when logged via
--log-file only (the bug didn't affect daemon transfer logging or the
output of the client's --out-format info).  Also unified the %b & %c
switch case to make it easier to maintain.  Fixes bug 11496.
2015-09-07 10:07:17 -07:00
Wayne Davison 1983198097 Add support for netbsd in xattrs case.
Closes bug-suggestion 11484.
2015-09-02 12:20:52 -07:00
Wayne Davison 2a7355fb56 Change daemon's gid list to use an "item_list". 2015-08-24 11:54:00 -07:00
Wayne Davison 3da1dc4d18 Add configure option to set max daemon gid list.
Fixes bug 11456.
2015-08-24 10:09:57 -07:00
Wayne Davison abfb41e63e Avoid creating even the top-level backup dir until needed.
Fixes bug 11423.
2015-08-23 21:58:18 -07:00
Wayne Davison 9d1cd2437c Improve make_path() error return for non-dir element. 2015-08-23 21:55:16 -07:00
Wayne Davison f8d2ecd223 Preparing for release of 3.1.2pre1 2015-08-08 12:47:35 -07:00
Wayne Davison 453914e35b Update the copyright year. 2015-08-08 12:47:03 -07:00
Wayne Davison 3f26e38f86 Mention latest fixes. 2015-08-08 12:42:13 -07:00
Wayne Davison 81d1ca0683 Don't create so many empty backup dirs.
Fixes bug 10724.
2015-08-08 12:19:42 -07:00
Wayne Davison 289ccbd02f Allow samba.org hostname to be overridden. 2015-08-02 11:15:17 -07:00
Wayne Davison 85d3877be9 Improve mergedir filter handling internals.
Fixes bug 10995.
2015-07-13 10:56:13 -07:00
Wayne Davison e203245d76 Make sure chk subdir can't diverge in time from its src subdir. 2015-07-12 14:00:45 -07:00
Wayne Davison dfbcc4f7ec Avoid --remove-sent-file issue for non-regular files. 2015-07-12 13:26:01 -07:00
Wayne Davison 0bcb8b639a Mention local-only effect of --msgs2stderr. 2015-07-12 13:10:05 -07:00
Wayne Davison 77f750f167 Mention how we handle a module named "global". 2015-07-12 10:38:32 -07:00
Wayne Davison 23afe20780 Brant Gurganus's autoconf updates.
This improves some obsolete autoconf macros and increases the minimum
autoconf version from 2.60 to 2.69.  Fixes bug 11369.
2015-07-07 10:37:12 -07:00
Wayne Davison e12a6c087c Add parent-dir validation for --no-inc-recurse too. 2015-07-04 16:25:23 -07:00
Wayne Davison 6feb7d37df Change "fail" to "test_fail".
Fixes bug 11322.
2015-06-10 15:20:01 -07:00
Wayne Davison 81ff413bb0 Make the checksum_seed a bit harder to predict. 2015-05-11 14:32:45 -07:00
Wayne Davison eac858085e Add compat flag to allow proper seed checksum order.
Fixes the equivalent of librsync's CVE-2014-8242 issue.
2015-05-11 12:36:20 -07:00
Wayne Davison 2ac35b4507 Pass -I option to aclocal. 2015-05-01 15:17:41 -07:00
Tiziano Müller 3bc319766d Use AS_IF instead of plain if/then/fi 2015-05-01 14:26:21 -07:00
Tiziano Müller a689fb1f5f Ignore .deps directories. 2015-05-01 14:26:21 -07:00
Tiziano Müller b560a96b2c Check for perl and assign it to a var since it is needed for generating the protocol header. 2015-05-01 14:26:21 -07:00
Tiziano Müller 51e3c3da85 Remove dead targets from build system 2015-05-01 14:26:21 -07:00
Wayne Davison 461086bbe7 Handle configure's new version style. 2015-05-01 14:26:21 -07:00
Tiziano Müller 00694610a6 Specify package name and version in call to AC_INIT 2015-05-01 14:26:21 -07:00
Tiziano Müller 8e3a6db842 Properly quote arguments for AC_LIBOBJ. 2015-05-01 14:26:21 -07:00
Wayne Davison 8354cad53e Must define LIBOBJDIR in the Makefile. 2015-05-01 14:26:05 -07:00
Tiziano Müller 317a0e8ca5 Use AC_CONFIG_LIBOBJ_DIR and AC_REPLACE_FUNCS to adhere to autoconf standards 2015-04-26 17:54:08 -07:00
Tiziano Müller 066ad8c719 Modularize m4 macros
Split acinclude.m4 into one file per function in m4/
2015-04-26 17:54:06 -07:00
Tiziano Müller ec4f644d2f Properly quote m4 macro 2015-04-26 16:50:41 -07:00
Wayne Davison 4bf342c60f Update generated-files logic. 2015-04-26 16:50:41 -07:00
Tiziano Müller a2f733c664 Rename aclocal.m4 to acinclude.m4 and add make target
It is common practice to split up m4 files for easier maintenance and
generate the required aclocal.m4 using `aclocal` instead.
2015-04-26 16:50:16 -07:00
Stefan Behrens 3ea74eb388 rsync: fix of-by-one in check of snprintf() result.
Fixes bug 11229.
2015-04-22 10:31:04 -07:00
Wayne Davison 962f8b9004 Complain if an inc-recursive path is not right for its dir.
This ensures that a malicious sender can't use a just-sent
symlink as a trasnfer path.
2014-12-31 13:48:42 -08:00
Wayne Davison ae189e18de Mention that --append can be dangerous. 2014-12-31 13:10:46 -08:00
Wayne Davison 5b34561cf7 Call set_modtime even if only NSEC is different. 2014-12-31 13:10:37 -08:00
Wayne Davison 5546dab329 Use usleep() for msleep() if it is available. 2014-11-27 12:02:56 -08:00
Wayne Davison 6128f56694 Add a missing closing paren. 2014-10-10 14:15:11 -07:00
Wayne Davison 743f5a30d5 Prepare the repository for more development. 2014-09-06 11:00:52 -07:00
Wayne Davison a955e93316 Mention DRY RUN in --debug=exit output. 2014-09-06 10:47:13 -07:00
Wayne Davison aca7dd3bff Adding the long options that BackupPC likes to use. 2014-09-04 13:44:50 -07:00
Wayne Davison bc55aa04df Set GIT_MERGE_AUTOEDIT=no in the environment. 2014-07-31 15:59:32 -07:00
Wayne Davison f438d5abe0 Match latest git's repo branch message. 2014-07-31 14:52:30 -07:00
Wayne Davison 6fe798392b Match latest git's repo status messages. 2014-07-31 14:47:09 -07:00
Wayne Davison 6ceb9ea012 Remove superfluous ${INSTALL_STRIP} uses. 2014-07-31 14:39:09 -07:00
Wayne Davison 6900d35cce Fix a typo. 2014-07-31 14:38:16 -07:00
Wayne Davison 7cb0de6326 Preparing for release of 3.1.1 2014-06-22 09:50:03 -07:00
Wayne Davison 61e74afc42 Add a clarification about shell wildcard expansion. 2014-06-22 09:41:17 -07:00
Wayne Davison 0466e46b9f Make sure the link() destination file doesn't exist. 2014-06-22 09:04:24 -07:00
Wayne Davison aa4c6db043 Make sure cmp_time() doesn't mess up due to a time_t overflow.
Fixes bug 10643.
2014-06-15 17:53:34 -07:00
Wayne Davison edb0d9c792 Updated NEWS & tweaked a comment. 2014-06-14 09:55:37 -07:00
Wayne Davison 6ffd8f2169 Put zlib and popt -I options early in the CFLAGS. 2014-06-14 09:48:56 -07:00
Wayne Davison ba43e88527 Fix hard-link bugs when receiver isn't capable.
If the receiving side cannot hard-link symlinks and/or special files
(including devices) then we now properly handle incoming hard-linked
items (creating separate identical items).
2014-06-13 16:05:08 -07:00
Wayne Davison ff08acd4f2 Added a flag to disable xattr hlink optimization.
I added a compatibility flag for protocol 31 that will let both sides
know if they should be using the xattr optimization that attempted to
avoid sending xattr info for hardlinked files.  Since this optimization
was causing some issues, this compatibility flag will ensure that both
sides know if they should be trying to use the optimization or not.
2014-06-08 10:42:14 -07:00
Wayne Davison 03bb593f81 I missed this tweak in the undo of a prior xattr optimization. 2014-06-08 10:21:27 -07:00
Wayne Davison 4c8eb5f951 Preparing for release of 3.1.1pre2 2014-05-26 15:42:03 -07:00
Wayne Davison f491d17352 Mention all the latest changes in the NEWS. 2014-05-26 14:08:31 -07:00
Wayne Davison 288e64a79f Avoid an xattr-finding glitch on the receiver.
Fixes bug 9594.
2014-05-26 14:08:31 -07:00
Wayne Davison 0872dff60c Add new-compress option to rrsync. 2014-05-26 12:13:01 -07:00
Wayne Davison 3ce7a65c11 Make --omit-dir-times avoid early-create directories. 2014-05-25 16:43:14 -07:00
Wayne Davison de8ec0b309 Exit with a partial-transfer error for a sender-remove failure. 2014-05-25 16:00:13 -07:00
Wayne Davison 677c6e14cc Check for attr lib. 2014-05-05 09:25:13 -07:00
Wayne Davison 7665ba5b36 Fix usermap/groupmap parsing of MIN-MAX IDs. 2014-04-30 12:34:15 -07:00
Wayne Davison adc600cbe2 Check F_IS_ACTIVE() in a few more spots.
The code needs to ignore flist entries that were marked inactive (by
either duplicate removal or empty-dir pruning) in a few more spots.
2014-04-20 14:41:54 -07:00
Wayne Davison 43d6d0c5ba Change args to file_checksum() to prepare for future changes. 2014-04-19 16:26:35 -07:00
Wayne Davison 22a3ac0b55 Add new-style compression that skips matching data.
Adding new-style compression that only compresses the literal data that
is sent over the wire and not also matching file data that was not sent.
This new-style compression is compatible with external zlib instances,
and will eventually become the default (once enough time has passed that
all servers support the --new-compress and --old-compress options).

NOTE: if you build rsync with an external zlib (i.e. if you specified
configure --with-included-zlib=no) you will ONLY get support for the
--new-compress option!  A client will treat -z as uncompressed (with a
warning) and a server will exit with an error (unless -zz was used).
2014-04-19 12:18:19 -07:00
Wayne Davison 1524c2e5c7 Expand the backslash description a bit more in excludes. 2014-04-19 10:16:00 -07:00
Wayne Davison 0dedfbce2c Avoid infinite wait reading secrets file. 2014-04-13 13:51:36 -07:00
Wayne Davison 4cad402ea8 Receiver now rejects invalid filenames in filelist.
If the receiver gets a filename with a leading slash (w/o --relative)
and/or a filename with an embedded ".." dir in the path, it dies with
an error (rather than continuing). Those invalid paths should never
happen in reality, so just reject someone trying to pull a fast one.
2014-04-13 10:36:59 -07:00
Wayne Davison 306d112730 Mention how "max verbosity" affects info & debug opts. 2014-03-07 23:47:40 -08:00
Wayne Davison 371242e4e8 Have receiver strip bogus leading slashes on filenames.
If the receiver is running without --relative, it shouldn't be receiving
any filenames with a leading slash.  To ensure that the sender doesn't
try to pull a fast one on us, we now make flist_sort_and_clean() strip a
leading slash even if --relative isn't specified.
2014-03-02 16:47:01 -08:00
Wayne Davison e1bfdf67f3 Avoid the use of an extra leading dot when using --temp-dir. 2014-02-26 14:00:10 -08:00
Wayne Davison 3fe686b577 Explicitly mention that dirs aren't affected by --update. 2014-02-24 11:30:07 -08:00
Wayne Davison 783611707b Include a systemd file that some distros might want. 2014-02-24 10:19:14 -08:00
Wayne Davison cd909fde87 Fix --info=progress2 info as a file is transferred.
Applying Anish Shankar's patch to fix speed and stats of files as they
are transferred.  Fixes bug 10450.
2014-02-24 10:07:18 -08:00
Wayne Davison 4dfe7c9f3e Improve the *.spec file a bit. 2014-01-27 09:57:28 -08:00
Wayne Davison 7fb4c08c24 Use the patch's list of generated files for each patch. 2014-01-27 09:18:03 -08:00
Wayne Davison 8946cfc6f8 Preparing for release of 3.1.1pre1 2014-01-26 09:32:43 -08:00
Wayne Davison dfa5b49110 Bump the year to 2014. 2014-01-26 09:29:15 -08:00
Wayne Davison 1bf6203616 More NEWS improvements. 2014-01-26 09:21:47 -08:00
Wayne Davison a3f852bd78 Fix "unchanged" protocol designation. 2014-01-19 19:44:30 -08:00
Wayne Davison 72e7fb5b92 Mention the latest NEWS items. 2014-01-19 15:24:07 -08:00
Wayne Davison 740551d657 Undo the hard-link xattr optimization in 78286a03.
I'm backing out the xattr optimization that was put in to try
to make xattr data sending more optimal on hard-linked files.
The code was causing hard-to-reproduce bugs, and it's better to
get things done fully & correctly over fully optimally.
2014-01-19 14:59:43 -08:00
Wayne Davison a106ed78d5 Fix the leaving of a temp file w/o partial-file saving.
Fixed bug 10350.
2014-01-19 14:35:05 -08:00
Wayne Davison bba31ddf12 Avoid ACL and/or xattr lookups on IS_MISSING_FILE() entries.
Fixes bug 10381.
2014-01-19 12:24:01 -08:00
Wayne Davison 31825a94b3 Add IS_MISSING_FILE(statbuf) macro. 2014-01-19 12:23:39 -08:00
Wayne Davison 5dcef7c6dd Adding IVAL64() and SIVAL64(). 2014-01-19 12:02:38 -08:00
Wayne Davison 72e0c45078 Handle more x86 hosts w/o resorting to CAREFUL_ALIGNMENT. 2014-01-19 11:48:14 -08:00
Wayne Davison 0593471e99 We really depend on autoconf 2.60 these days. 2014-01-02 10:56:03 -08:00
Wayne Davison 1b29458ea4 Adding rsync-no-vanished script for bug 10356. 2014-01-01 10:35:08 -08:00
Wayne Davison d3414a7d23 Warn about lack of yodl2man at end of configure run. 2014-01-01 10:03:45 -08:00
Wayne Davison 9e2e7a1b2d Restoring use of socketpair on cygwin.
Use of socketpair is much faster on cygwin, and some folks report fewer
hangs using the modern socketpair implementation vs pipes.
2013-12-25 14:20:53 -08:00
Wayne Davison d34eaa8183 Use 0 (not NULL) for a non-pointer arg. 2013-12-25 14:19:30 -08:00
Wayne Davison b4ea93c676 Try to fix bug 7865 for some acl() EINVAL results. 2013-12-25 10:18:41 -08:00
Wayne Davison 6df5d81ce2 Fix a few issues with make_path().
The make_path() utility function was not returning the right status
when --dry-run was used, so I added some stat() checking that only
happens for -n.  I also noticed that the function was not handling
the case where the whole path needed to be created, so I fixed that.
Fixes bug 10209.
2013-12-23 10:30:28 -08:00
Wayne Davison 0e3152febd Change owner+group before setting xattrs to avoid xattr loss.
Fixes bug 10163.
2013-12-23 09:49:17 -08:00
Wayne Davison e9398b1dc5 Fix a typo that Stefan Beller pointed out. 2013-12-14 16:25:18 -08:00
Wayne Davison 83792c1cbf A delete_item() error should use FERROR_XFER.
Fixes bug 10024.
2013-12-01 15:58:17 -08:00
Wayne Davison 32540aa091 Tweak log_delete() to send MSG_DELETED more.
If the client is the sender and it is wanting to log deletes, the
current generator code neglects to send MSG_DELETED to the client side
unless some delete verbosity is enabled.  With this new version on the
generator side, the logfile will now mention deletes, even if the
sending (client) side is an older rsync.  Fixes bug 10182.
2013-11-28 11:13:05 -08:00
Wayne Davison 836e0c5df4 Create and use write_bigbuf() function for extra-large buffer sizes. 2013-11-25 13:12:35 -08:00
Wayne Davison 2cd87086f0 Use chunked xattr reading in OS X sys_lgetxattr(). 2013-11-25 13:12:09 -08:00
Wayne Davison eaa4e2d1ee Fix itemize bug with --link-dest, -X, and -n.
When running with --*-dest & -X, some alt-dest-found files would not
use the right name when looking up old attrs in itemize(), causing a
weird error for a --dry-run copy.  Fixes bug 10238.
2013-11-25 09:18:18 -08:00
Wayne Davison e461cefbab Avoid useless keepalive msgs that would kill an older rsync.
This fix avoids the sending of keep-alive messages from the receiver
to the sender when we are still sending the file list (at which time
an older rsync would die if it received such a keep-alive message).
The messages aren't actually needed, since we haven't forked yet, and
the single flow of data keeps the procs alive.
2013-11-10 16:15:39 -08:00
Wayne Davison 18217a94c4 Fix timeout checking in safe_read(). 2013-11-09 10:49:59 -08:00
Wayne Davison 090ef59b29 Change safe_read() to select() before reading. 2013-11-09 10:32:44 -08:00
Wayne Davison 708db6f772 Git rid of uneeded extern. 2013-10-27 11:26:29 -07:00
Wayne Davison 63f9197611 Return an error if a buffer overflows in do_mknod(). 2013-10-27 10:12:53 -07:00
Wayne Davison f643330eb1 Restore sending of "-ef" marker to the server. 2013-10-27 09:49:16 -07:00
Wayne Davison bc0d094d2a Don't use comma_num() in FLOG output. 2013-10-27 09:48:57 -07:00
Wayne Davison 64dff88db9 Don't forget about --debug and --info for rrsync. 2013-10-04 14:10:44 -07:00
Wayne Davison 637ebad048 A few more new options that rsync 3.1.0 can pass. 2013-10-04 13:59:04 -07:00
Wayne Davison 487bf9290c Prepare repository for more development. 2013-10-03 10:45:49 -07:00
Wayne Davison bc58313bf7 Preparing for release of 3.1.0 2013-09-28 13:55:54 -07:00
Wayne Davison de78297b61 Remove unused var. 2013-09-28 13:53:23 -07:00
Wayne Davison 9c7d755dfe Flush write buffer on an aborted in-place transfer. 2013-09-28 10:40:27 -07:00
Wayne Davison 60cc5d4b78 Mention latest news. 2013-09-16 09:23:35 -07:00
Wayne Davison 1220c93a99 Fix the visit-all-patches path. 2013-09-16 09:16:30 -07:00
Wayne Davison 7d7538d43c Fix error in write_sparse() on incomplete write.
Fix a problem where sparse_seek could get left non-zero when we
did not finish writing all the data that would take us to that
sparse gap.  Issue pointed out by David Taylor.
2013-09-16 09:02:46 -07:00
Wayne Davison de94193353 Remove bypassed checksums in --inplace to improve speed.
When checking a checksum that refers to a part of an --inplace file that
has been overwritten w/o getting SUMFLG_SAME_OFFSET set, we remove the
checksum from the list.  This will speed up files that have a lot of
identical checksum blocks (e.g. sequences of zeros) that we can't use
due to them not getting marked as being the same.  Patch provided by
Michael Chapman.
2013-08-03 09:59:38 -07:00
Wayne Davison 05fce6582a Preparing for release of 3.1.0pre1 2013-07-28 10:36:43 -07:00
Wayne Davison 62327b1281 We need a trailing dot when using --server --daemon. 2013-07-12 15:28:54 -07:00
Wayne Davison 99c9520ea7 Look for REMOTE_HOST before SSH_* environment options. 2013-07-12 15:24:58 -07:00
Wayne Davison 01959d6387 Set DESTDIR for make install. 2013-06-23 15:47:09 -07:00
Wayne Davison 807f3a44ba Mention latest changes. 2013-06-16 16:59:24 -07:00
Wayne Davison 2791e0b542 Be a little clearer about full-line comments. 2013-06-16 16:43:14 -07:00
Wayne Davison d6a7ed99c1 Get GPL name right. 2013-06-16 16:36:27 -07:00
Wayne Davison 4066d61a9d Mention right option when using --delete-delay. 2013-06-16 16:33:32 -07:00
Wayne Davison 70d4a945f7 Support rsync daemon over SSL via stunnel.
Added the client rsync-ssl script and various client/daemon support
files needed for talking to an rsync daemon over SSL on port 874 (no
tls support).  This uses an elegant stunnel setup that was detailed
by dozzie (see the resources page) now that stunnel4 has improved
command-spawning support.  Also incorporates some tweaks by devzero
(e.g. the nice no-tmpfile-config client-side code) and a few by me
(including logging of the actual remote IP that came in to the
stunnel process).  This probably still needs a little work.
2013-06-15 16:40:10 -07:00
Wayne Davison 0488a14b99 Fix "make check". 2013-06-11 18:06:53 -07:00
Wayne Davison a213d1cd6e Move some code from util.c to util2.c and add sum_as_hex(). 2013-06-11 13:36:44 -07:00
Wayne Davison fc2d6fabe7 Set number_separator the first time it gets used. 2013-06-11 13:28:45 -07:00
Wayne Davison a508e88fcf More NEWS changes. 2013-06-09 22:28:24 -07:00
Wayne Davison f0da824237 Upgrading zlib to 1.2.8. 2013-06-09 22:27:33 -07:00
Wayne Davison b74250037c Rename lsh.pl -> lsh. 2013-06-09 19:40:56 -07:00
Wayne Davison f93094fa9f Rename lsh -> lsh.sh. 2013-06-09 19:40:56 -07:00
Wayne Davison baf382d62e Updating NEWS with the latest changes. 2013-06-09 16:02:19 -07:00
Wayne Davison 12505e02b1 Allow --password-file=- for a stdin-supplied password. 2013-06-09 12:11:53 -07:00
Wayne Davison d6df07392e Fix module-name splitting with --protect-args.
Fixes bug 8838.
2013-06-02 15:54:48 -07:00
Wayne Davison d9ca1e4990 Tweak --checksum-seed docs. 2013-06-02 13:14:27 -07:00
Wayne Davison 0ab8e166f4 Avoid preallocation on inplace file that is already long enough. 2013-06-02 12:37:48 -07:00
Wayne Davison 1e9ee19a71 Look for got_kill_signal in a couple more spots. 2013-05-28 12:59:47 -07:00
Wayne Davison 9bf065860f Improve logic of code vs exit_code (etc.) in cleanup. 2013-05-26 16:23:37 -07:00
Wayne Davison 5b46454364 Forward a MSG_ERROR_EXIT value to generator too.
Fixes bug 9882.
2013-05-26 16:23:37 -07:00
Wayne Davison d4070db631 Avoid I/O via signal-handler thread.
The cleanup code will try to flush the output buffer in some
circumstances, which is not valid if we're handling an async signal
(since it might have interrupted some partial I/O in the main thread).
These signals now set a flag and try to let the main I/O handler take
care of the exit strategy.  Fixes a protocol error that could happen
when trying to exit after a kill signal.
2013-05-26 16:22:56 -07:00
Wayne Davison cb784f18ec Improve iconvbufs() to do more buffer size checks.
- If iconv() returns EINVAL or EILSEQ and the error is being ignored, make
  sure that there is room in the output buffer to store the erroneous char.
- When accepting an erroneous char, be sure to break if there are no more
  input characters (without calling iconv() with a zero input length).
2013-05-19 23:56:34 +00:00
Wayne Davison 2dc2070992 Fix msleep() if time goes backwards. Fixes bug 9789. 2013-05-19 22:55:08 +00:00
Wayne Davison 4442f8037b Fixed unused variable warnings in free_stat_x. 2013-05-19 22:01:29 +00:00
Wayne Davison 333e3a9ff0 Add an implementation of getpass for systems that lack one. 2013-05-19 22:01:29 +00:00
Wayne Davison 94073d20e4 Use S_IXUSR instead of the now-obsolete S_IEXEC. 2013-05-19 22:01:29 +00:00
Wayne Davison 750ec9bcdc Updated to the version dated 2013-04-24. 2013-05-19 22:01:07 +00:00
Wayne Davison 7fdba7aaf8 Updated to the version dated 2013-05-16. 2013-05-19 22:00:44 +00:00
Wayne Davison eec26089b1 Improve description of --max-delete. 2013-01-20 11:30:08 -08:00
Wayne Davison 76340ea44c Fix weird error in test programs on SunOS. 2013-01-19 12:07:38 -08:00
Wayne Davison 7e1a9c4d79 Update copyright year. 2013-01-19 11:05:53 -08:00
Wayne Davison 16a9883649 Update OLDNEWS with 3.0.9 info. 2013-01-19 11:05:15 -08:00
Wayne Davison bd7d36cc6c Free ACL/xattr info in try_dests_reg() loop. 2013-01-19 10:25:18 -08:00
Wayne Davison d42e7181d5 Add free_stat_x() inline function. 2013-01-19 10:25:18 -08:00
Wayne Davison c03bb3d181 Further improve non-empty-destination --link-dest behavior:
- Avoid relinking a file that is already linked correctly.
- Avoid trashing the stat buffer of an existing file in try_dests_reg().
2013-01-19 10:24:46 -08:00
Wayne Davison cee326436c Remove -3 return from try_dests_reg() again -- let it do a local copy
when the dest file is unlinked and the hard link fails.
2013-01-19 09:08:39 -08:00
Wayne Davison 0ae92567ed Align fileio's map_ptr() reads. Fixes bug 8177. 2013-01-18 15:30:08 -08:00
Wayne Davison 42f759ad9a Reformat a few things for wider lines. 2013-01-18 15:20:43 -08:00
Wayne Davison e1ded58983 Improve handling of existing files for alt-dest opts.
Fixes bug #5644.
2013-01-18 11:57:49 -08:00
Wayne Davison 0bacaccee6 Perl version of lsh that can change user w/o sudo. 2012-10-07 16:33:30 -07:00
Wayne Davison d46f8314b6 Fix bogus "vanished file" with "./" prefixes.
Fixes bug 9212.
2012-10-07 10:47:58 -07:00
Wayne Davison 7cefbf106f Fix indentation that used expanded tabs. 2012-10-03 15:20:43 -07:00
Wayne Davison f143b55ef1 Avoid an unused variable warning if no setvbuf. 2012-09-23 12:03:19 -07:00
Wayne Davison ee51a745c1 Make read_args() return the full request.
When a daemon is sent multiple request args, they are now joined into a
single return value (separated by spaces) so that the RSYNC_REQUEST
environment variable is accurate for any "pre-xfer exec".  The values
in RSYNC_ARG# vars are no longer truncated at the "." arg, so that all
the request values are also listed (separately) in RSYNC_ARG#.
2012-09-23 11:15:36 -07:00
Wayne Davison 0d34fbdf5a Make daemon listener exit w/code 0 on SIGTERM. 2012-06-16 10:31:14 -07:00
Wayne Davison d51a3adb4f Set the modtime to 0 on a partial file.
Fixes debian bug 624826.
2012-05-05 08:01:09 -07:00
Wayne Davison b55115ec6f Fix --only-write-batch hang with --hard-links.
Fixes bug 8565.
2012-01-28 12:04:26 -08:00
Wayne Davison f5e2b8f803 Add a slash-stripping version of rsync in support dir. 2012-01-28 10:42:01 -08:00
Wayne Davison 41c5ba641f flist->in_progress is only needed w/inc_recurese. 2012-01-28 10:42:01 -08:00
Wayne Davison 0dfd2a64ec Make stderr line-buffered w/--msgs2stderr. 2012-01-28 10:42:01 -08:00
Wayne Davison 6686b93a7a Add new --outbuf=N|L|B option. 2012-01-28 10:41:58 -08:00
Wayne Davison 9510fa9ab8 Allow --max-size=0 and --min-size=0.
Fixes bug 7965.
2011-12-24 12:35:37 -08:00
Wayne Davison d74512eb05 Complain if the --block-size=N value is too large.
Fixes bug 8036.
2011-12-24 12:35:37 -08:00
Wayne Davison 1a2704512a Improve the handling of verbose/debug messages
The sender no longer allows a filelist to be sent in the middle of
parsing an incoming message, so that the directory sending doesn't block
all further input reading.  The generator no longer allows recursive
reading of info/error messages when it is waiting for the message buffer
to flush.  This avoids a stack overflow when lots of messages are coming
from the receiver and the sender is not reading things quickly enough.
The I/O code now avoids sending debug messages that could mess up the
I/O buffer it was in the middle of tweaking.  This fixes an infinite
loop in reduce_iobuf_size() with high levels of debug enabled.  Several
I/O-related messages were changed to output only when --msgs2stderr is
enabled.
2011-12-21 12:14:49 -08:00
Wayne Davison a3b62ff4cf Avoid double-free of xattr/acl data in real_sx.
Fixes bug 8665.
2011-12-16 09:07:19 -08:00
Wayne Davison 60ef397057 Mention that %a and %h are daemon-only escapes. 2011-11-24 07:55:11 -08:00
Wayne Davison 89e049ad7f Another asprintf() return-value-check tweak. 2011-11-23 13:14:35 -08:00
Wayne Davison 036094d30e Committing missed manpage tweak. 2011-11-23 12:36:50 -08:00
Wayne Davison 48b51d0004 make repeated --fuzzy option look into alt-dest dirs. 2011-11-23 12:29:25 -08:00
Wayne Davison 7da17144fd Add compatibility with an unmodified zlib. 2011-11-21 09:22:14 -08:00
Wayne Davison cbdff74b44 Fix --compress data-duplication bug. 2011-11-21 09:17:17 -08:00
Wayne Davison 37a729768b Fix version expansion. 2011-11-21 09:10:49 -08:00
Wayne Davison 8dd6ea1f1e Fix --delete-missing-args when --relative is active. 2011-10-22 10:24:54 -07:00
Wayne Davison 7c8f180900 Test asprintf() failure with < 0, not <= 0. 2011-10-08 09:16:43 -07:00
Wayne Davison 3527677043 Let's cast getpid() to an int instead of a long for snprintf(). 2011-10-08 09:15:36 -07:00
Wayne Davison fd91c3b666 Fix two unused-variable compiler warnings. 2011-09-22 23:37:07 -07:00
Wayne Davison 15df927ae2 Fix xattr memory leak. Fixes bug 8475. 2011-09-22 09:13:31 -07:00
Ben Walton 8adceeb2b3 Testsuite/dir-sgid: use symbolic mode to set sgid bit
The chmod on Solaris (9 and 10) cannot set the sgid bit on a directory
using absolute mode, so use symbolic mode.  Avoids a skipped test.
2011-09-22 08:28:07 -07:00
Wayne Davison 0c7fdf705e Add solaris xattr support to the tests.
Change the xattr case statements to use $HOST_OS.
(Slightly tweaked version of a Ben Walton patch.)
2011-09-20 13:24:42 -07:00
Wayne Davison 439d5d8929 Better fakeroot support helps Solaris. 2011-09-20 13:17:42 -07:00
Wayne Davison de219101ed Change stat order for better ELOOP determination. 2011-09-20 13:02:12 -07:00
Wayne Davison 79853c30c0 Be sure to use STRUCT_STAT. 2011-09-20 12:54:06 -07:00
Wayne Davison 953feeadd2 Make do_readlink() support fake-super w/o O_NOFOLLOW. 2011-09-19 09:29:00 -07:00
Wayne Davison 3417881d5c Make --delete-excluded work better with --filter=merge. 2011-09-15 07:52:38 -07:00
Wayne Davison 397fb1acd2 When modifying PATH, export it (for Solaris). 2011-09-15 07:32:07 -07:00
Wayne Davison db0443cf90 Added "SORTED TRANSFER ORDER" manpage section. 2011-09-13 16:04:21 -07:00
Wayne Davison d5bff8ffe2 Cleanup some manpage & --help info. 2011-09-13 15:30:40 -07:00
Wayne Davison a7d7f52ae0 Use "|| true" in our xattr test runs. 2011-09-13 08:07:31 -07:00
Wayne Davison 847ddaf071 Make sure other early exit calls can't hang in noop_io_until_death(). 2011-09-12 17:56:23 -07:00
Wayne Davison c4c5dc68b9 Improve the usage for --help. 2011-09-11 22:46:57 -07:00
Ben Walton c4170cbaac Better configure support for Solaris xattrs
If we have the attropen() function, allow OS conditional enabling of
extended attribute support.  This removes the need to pass
--enable-extended-attributes to force the feature activation on Solaris.
2011-09-11 18:48:52 -07:00
Wayne Davison de407c03d2 Fix a potential hang on an empty file list.
Fixes bug 8423.
2011-09-11 11:08:14 -07:00
Wayne Davison c1005fb256 Document --msgs2stderr. 2011-09-11 11:01:04 -07:00
Wayne Davison 70c4bfb770 Error out if --password-file specifed and it fails.
Fixes bug 8440.
2011-09-06 21:22:22 -07:00
Wayne Davison a470b675af Dirs need +rx as well as +w for non-super xfers.
Fixes bug 8242.
2011-09-03 12:41:22 -07:00
Wayne Davison 5561c14978 Move implied_dot_dir=1, just to be safe. 2011-08-27 14:58:20 -07:00
Wayne Davison 865efe9456 Fix sending of "." attributes for implied-dot-dir. 2011-08-27 12:05:07 -07:00
Wayne Davison 18749579b5 Fix bwlimit multiplication overflow. Fixes bug 8375. 2011-08-27 12:05:07 -07:00
Wayne Davison a05758fde6 Some option-parsing clarifiation in the intro. 2011-08-27 12:04:45 -07:00
Wayne Davison fb0d4403f0 Fix misplaced parens on getnameinfo() call. 2011-08-06 11:21:40 -07:00
Wayne Davison 3fd0357f9f Ignore socketpair() on cygwin. Fixes bug 8356. 2011-08-06 11:19:00 -07:00
Wayne Davison 64fa23add9 Tweak includes to fix non-defined NULL on some systems. 2011-07-31 23:31:24 -07:00
Wayne Davison 0a77adee0b Fix Minix build errors. Fixes bug 8313. 2011-07-22 11:17:57 -07:00
Wayne Davison 0a04a80d9f Replace another inet_ntop() call with getnameinfo(). 2011-07-16 16:16:04 -07:00
Wayne Davison 7ae666d2a7 Add more connect debug info, as Carlos suggested. 2011-07-12 16:02:31 -07:00
Wayne Davison fbf4c261f4 Move freeaddrinfo() call after failure-reporting loop. 2011-07-11 18:15:51 -07:00
Wayne Davison 425747c2f3 Fix a comment. 2011-07-04 16:32:06 -07:00
Wayne Davison 03cd1ae4fa Handle FES_NO_SEND properly on a hard-linked file.
Fixes bug 8246.
2011-07-04 16:13:45 -07:00
Wayne Davison 01580c794a Fix #ifdef in unchanged_attrs(). Fixes bug 8268. 2011-06-26 09:52:47 -07:00
Wayne Davison a59a7b2423 Fix reading side of fake-symlink bug 7109. 2011-06-18 13:42:30 -07:00
Wayne Davison 5bfe006882 Make daemon-exclude errors more error-like.
Fixes bug 7765.
2011-06-18 12:44:26 -07:00
Wayne Davison 852585b1fc Check if sender file changed before allowing a remove.
Fixes bug 7691.
2011-06-18 12:29:21 -07:00
Wayne Davison e2c1e482e0 Set NO_SYMLINK_USER_XATTRS on linux. Fixes bug 7109. 2011-06-18 10:12:47 -07:00
Wayne Davison 4591bb2f66 Only skip deletions on IOERR_GENERAL. Fixes bug 7809. 2011-06-04 13:04:46 -07:00
Wayne Davison 810dc9fc2a Don't force \(em in the manpages. Fixes bug 7941. 2011-06-04 12:53:10 -07:00
Wayne Davison d80f7d6cc3 Add a colon if a non-empty pre-xfer exec message follows. 2011-06-04 12:12:49 -07:00
Wayne Davison e3bc529de9 Handle EINTR when reading the pre-xfer exec message. 2011-06-04 12:08:18 -07:00
Wayne Davison 820f7a7b57 Send error messages from pre-xfer exec script to the user. 2011-06-04 10:46:22 -07:00
Wayne Davison a131954b7b Linux needs symlink xattrs. Fixes bug 8201. 2011-06-04 09:47:24 -07:00
Wayne Davison f187ce36cc We need VA_COPY() defined more. Fix dangling #endif. 2011-05-30 12:48:04 -07:00
Wayne Davison 2fff0a4f28 Merge latest samba version to get va_end() fixes, etc. 2011-05-30 10:24:57 -07:00
Wayne Davison cb0db58fb3 Fix unwritable directory issue due to misordered chmod call. 2011-05-30 08:24:27 -07:00
Wayne Davison 582aead623 Expand NO_ENTRY items from fake-super ACLs in get_rsync_acl(). 2011-05-25 08:59:47 -07:00
Wayne Davison d518e02243 Use a union for idlist's name/max_id value. Fixes bug 8137. 2011-05-16 18:24:34 -07:00
Wayne Davison b9cc2d4c86 Explicitly mention spaces in the "path" setting. 2011-05-16 11:34:15 -07:00
Wayne Davison ac30d22ae5 Check for linux/falloc.h header file. 2011-05-16 08:26:40 -07:00
Wayne Davison 8bcd6a4aff Abort if the cd fails. 2011-05-07 13:01:21 -07:00
Wayne Davison 58663df432 Turn empty remote args into dot dirs. 2011-05-07 12:42:16 -07:00
Wayne Davison da7acc5e42 Make --files-from allow a missing trailing arg w/--server. 2011-05-07 12:16:50 -07:00
Wayne Davison 54d13604fa Mention the number of child args. 2011-05-07 12:11:56 -07:00
Wayne Davison 3ed84dbc98 Don't die if man-copy fails. 2011-04-28 18:50:21 -07:00
Wayne Davison 0308092914 Handle non-srcdir man copying when yodl isn't installed. 2011-04-28 16:41:03 -07:00
Wayne Davison a13d3b3d77 Avoid adding a slash to path '/'. 2011-04-22 16:06:15 -07:00
Wayne Davison 813d5a25e3 Fix a potential crash when trying to find a better block match. 2011-04-22 16:06:06 -07:00
Wayne Davison 591c224584 Improve lsh's handling of -l user option w/cd. 2011-04-09 08:51:26 -07:00
Wayne Davison b223d96bf0 Add some temp-name dot heuristics for OS X's sake.
- Drop one leading '.' from the filename (before adding our own).
 - Drop one trailing '.' from a (possibly truncated) name prior to
   the .XXXXXX suffix being added.
 - Allow the temp-name to collapse to just the .XXXXXX suffix
   if the path is long enough to require that.

Note that we don't try to remove multiple dots from a filename
that actually has multiple consecutive dots, since we might as
well learn early if the final name is going to fail or not.
2011-04-05 13:35:13 -07:00
Wayne Davison 28b519c93b Applying the preallocate patch. 2011-04-04 21:57:57 -07:00
Wayne Davison 8686d3abba Move var declaration for older C compilers. 2011-04-03 18:02:45 -07:00
Wayne Davison 73b9b90a0b Adding release info for 3.0.8 to the trunk. 2011-03-26 14:50:52 -07:00
Wayne Davison 98ec67d786 Verify the module list output of the daemon-via-ssh check. 2011-03-26 11:07:27 -07:00
Wayne Davison 0de5157564 Tweak dir xattrs after the writability fudging. 2011-03-26 10:19:04 -07:00
Wayne Davison 78286a03d1 Avoid re-setting (and sending) xattrs on a hard-linked file w/the same xattrs.
Improved the xattrs testing to include hard-linking.
2011-03-26 10:09:20 -07:00
Wayne Davison d699d815d6 Enhance the -liconv check for OS X. Fixes bug 8018. 2011-03-20 19:33:28 -07:00
Wayne Davison e5e6d3c410 Get the branch set right before listing names and handling --delete. 2011-03-19 16:34:37 -07:00
Wayne Davison 016ce71568 Make it possible to create a new patch file while on a patch branch. 2011-03-19 16:29:47 -07:00
Wayne Davison 2792a83d58 Don't send user/group names for ACLs with --numeric-ids.
Fixes bug 8020.
2011-03-18 14:59:03 -07:00
Wayne Davison 0b67d5e396 Fix xattrs test on OS X. 2011-03-13 20:48:18 -07:00
Wayne Davison d52aeae4e9 Improve the &merge/&include example explanation. 2011-03-11 17:39:33 -08:00
Wayne Davison f7c3a25052 Change rsyncd.conf &merge directive to match *.inc.
This allows the same rsyncd.d directory to be used for a set
of merge files (*.inc) and a set of include files (*.conf).
2011-03-11 16:13:33 -08:00
Wayne Davison 6da6b02bb7 Suggest a better solution for a make without wildcard support. 2011-02-26 08:12:28 -08:00
Wayne Davison da9b72b36c Clarify what extraneous hard link are. 2011-02-23 07:19:52 -08:00
Wayne Davison 75a1a04847 Get rid of obsolete tempfs warning. 2011-02-22 15:37:26 -08:00
Wayne Davison eee2c77a93 Some uid/gid fixes for (id_t)-1 and other large ID values.
The code now avoids any special internal meaning for uid/gid -1, which
allows it to be mapped to a better value (use 4294967295 instead of -1
as the ID to map).  Replaced atol() with something than can return a
value > 0x7FFFFFFF and that will error-out if the value overflows.  If
chown() is called with a uid or gid of -1, complain that the ID is not
settable and signal a transfer error.  Fixes bug 6936.
2011-02-22 10:27:35 -08:00
Wayne Davison 7766e67321 Allow a failure of EINVAL to mean no ACLs are available.
(If our POSIX types aren't valid, we can't handle the ACLs.)
2011-02-22 08:52:48 -08:00
Wayne Davison 4403b1332f Fix --force with --one-file-system w/o --delete. 2011-02-22 08:26:13 -08:00
Wayne Davison c82711b34e Fix issue with devices-fake test. 2011-02-22 08:25:11 -08:00
Wayne Davison b2e446d0cb Fix devices test on OS w/o hard-linked devices. 2011-02-22 07:39:10 -08:00
Wayne Davison 3bd9f51917 Improve some hard-link caveats in the manpage. 2011-02-20 23:30:53 -08:00
Wayne Davison 86e90c58f4 Add .hg dir exclude to default_cvsignore list.
Fixes bug 7957.
2011-02-17 22:09:08 -08:00
Wayne Davison e1fe1d55c4 Updated a comment to match a 3.0.x change. 2011-01-29 22:24:49 -08:00
Matt McCutchen 57edc4808f Avoid changing file_extra_cnt during deletion.
The I/O code can receive incremental file-list chunks during deletion,
and their OPT_EXTRA fields would get corrupted when file_extra_cnt is
incremented.

Instead of temporarily enabling uid_ndx to find out whether the user
owns a file, have make_file() set a flag for that purpose.

Applied with a few minor tweaks by Wayne.  Fixes bug 7936.
2011-01-29 22:05:16 -08:00
Wayne Davison 69be312b5e Some minor variable and flag cleanup. 2011-01-29 22:01:37 -08:00
Wayne Davison 4b4bcbe674 Optimize finding the sum that matches our --inplace position. 2011-01-16 17:35:50 -08:00
Wayne Davison 3f26945cb1 Include backup in map_ptr() to avoid backing up when reading. 2011-01-15 11:16:49 -08:00
Wayne Davison 580cffdec9 Sender realigns chunks with generator during an --inplace copy
when sending a sequence of zeros.
2011-01-14 21:32:15 -08:00
Wayne Davison 58a1c1a218 Make sure an alternate --inplace sum has the right length
and add missing break in --inplace same-offset loop.
2011-01-14 15:37:48 -08:00
Wayne Davison 53e6507e14 Fix a bug in the trailing-slash handling. 2011-01-13 23:00:30 -08:00
Wayne Davison 3feece5938 Improve the discussion of the absolute-filter alternative. 2011-01-13 17:16:32 -08:00
Wayne Davison 4ab6125214 Have build farm always use included popt. 2011-01-04 08:00:31 -08:00
Wayne Davison 49eb0c4a35 Mention that sorting the --files-from input is helpful. 2011-01-03 19:49:05 -08:00
Wayne Davison 050e5334d8 Added "listen backlog" daemon config paramater. 2011-01-03 19:42:27 -08:00
Wayne Davison bf4170ade8 Daemon supports forward-DNS lookups for simple hostnames
in hosts deny/allow config settings.
2011-01-03 19:04:06 -08:00
Wayne Davison 6500e0769a Avoid reading ACL/xattr info on filetypes not being copied.
Make OS X avoid xattr access on device/special files.
Fixes bug 5458.
2011-01-03 11:20:04 -08:00
Wayne Davison aa3faf5f8c Separate the dirs from the files in xattrs.text. 2011-01-01 21:23:19 -08:00
Wayne Davison 8030518dd0 Clarify incremental recursion's effect on --hard-link. 2011-01-01 18:21:40 -08:00
Wayne Davison e630fd1186 Some --inplace manpage enhancements. 2011-01-01 18:16:49 -08:00
Wayne Davison d3f5c628d7 Avoid directory permission issues with --fake-super.
Fixes bug 7070.
2011-01-01 18:09:57 -08:00
Wayne Davison 8b6ebde1f3 Be clear on which part(s) of testsuite's checkit() failed. 2011-01-01 18:09:16 -08:00
Wayne Davison 1c99b1d956 Report all socket connection errors if we fail.
Fixes bug 6588.
2011-01-01 14:00:40 -08:00
Wayne Davison 575933e9d0 Itemize xattrs of a missing dir from an alt-dest dir.
Fixes bug 6576.
2011-01-01 13:10:28 -08:00
Wayne Davison 14013906ca Use full_fname() for system error messages. 2011-01-01 13:09:58 -08:00
Wayne Davison d1b3118c16 Tweak the year. 2011-01-01 11:52:03 -08:00
Wayne Davison 8f30d21584 Protect a remote filename that starts with a dash. 2010-12-23 22:09:45 -08:00
Wayne Davison 3f770ab0a5 Set NO_SYMLINK_XATTRS on linux the easy way.
Fixes bug 7109.
2010-12-19 08:54:11 -08:00
Wayne Davison 743348e848 Fix issues with unchanged_attrs() for symlinks. 2010-12-18 08:48:07 -08:00
Wayne Davison 14ebc5b618 Fix crash when --backup-dir is excessively long. 2010-12-16 22:15:04 -08:00
Wayne Davison aef2b8ce41 Enhance --chmod to allow octal values. 2010-12-16 21:53:07 -08:00
Wayne Davison 9f5c16e51d Avoid splitting a multi-byte character when trimming a name.
Fixes bug 7816.
2010-11-26 09:35:43 -08:00
Wayne Davison 8484ddd3d1 A couple comment tweaks. 2010-11-20 09:30:35 -08:00
Wayne Davison 51b2ff03b3 Optimize --inplace chunck search to avoid a non-aligned search. 2010-11-12 09:07:58 -08:00
Wayne Davison 96e051c86a Use ftruncate() at the end of a --sparse file.
Fixes bug 7337.
2010-11-06 10:13:16 -07:00
Wayne Davison 55f767c5ca Mention seek effect of an unmoved --inplace chunk. 2010-11-06 08:14:21 -07:00
Wayne Davison 5ebe9a46d7 Add @group auth and overrides to "auth user" daemon config. 2010-10-12 10:34:38 -07:00
Wayne Davison d64bda1c1e Some quoting fixes/improvements. 2010-09-06 08:41:46 -07:00
Wayne Davison c6679e04f8 If we create an off_t type, define SIZEOF_OFF_T. 2010-09-06 08:26:41 -07:00
Wayne Davison 9cb20c605f Fix rsync_xal_set reference in an error. 2010-09-06 08:09:46 -07:00
Wayne Davison ba342e22e7 Undo unintended mode-reference tweak. 2010-08-28 18:02:22 -07:00
Wayne Davison 29358819ca Fix description of how to force new prototype generation. 2010-08-27 07:44:17 -07:00
Wayne Davison 2a189350f2 Move time setting to syscall.c and add syscall fallback.
See bug 5506 and bug 7621.
2010-08-26 15:31:07 -07:00
Wayne Davison 020df16def Make case_N.h more generic. 2010-08-26 11:13:02 -07:00
Wayne Davison 2624e005e2 Add --omit-link-times and use CAN_SET_SYMLINK_TIMES less. 2010-08-26 11:12:58 -07:00
Wayne Davison c9bf436e5b Mention need of wildcard support in make.
See bug 7625.
2010-08-26 08:50:34 -07:00
Wayne Davison 8b48c68285 Remove duplication for -x option. 2010-08-26 07:58:22 -07:00
Wayne Davison aff4850053 Add some new dont-compress suffixes.
As suggested in bug 6839.
2010-08-21 14:41:53 -07:00
Wayne Davison b32fd63459 Avoid a crash with --append-verify when discarding the received data.
Fixes bug 6293.
2010-08-21 14:25:48 -07:00
Wayne Davison 3b22184d4c Avoid a non-writable-by-the-user file when copying xattrs.
Fixes part of the problem in bug 5147.
2010-08-21 14:14:31 -07:00
Wayne Davison 929002a2d5 Avoid infinite loop if the file's length is negative.
Fixes bug 4664.
2010-08-21 13:48:01 -07:00
Wayne Davison a64840a29d Don't mention bug fixes that are queued up for 3.0.8. 2010-07-03 08:57:28 -07:00
Wayne Davison 0f6b683c8e Refer to the right lsetxattr() caller in a error message. 2010-07-03 08:14:02 -07:00
Wayne Davison 18070203c2 Replace another assert with a descriptive error. 2010-06-26 16:06:09 -07:00
Wayne Davison b3e41255a6 I like braces when multiple lines are indented. 2010-06-26 11:43:09 -07:00
Wayne Davison 9dbb94a7e6 Older protocols should send 1-incremented dev numbers. 2010-06-26 11:42:55 -07:00
Wayne Davison 1c9eafdda0 Mention more changes. 2010-06-26 10:59:30 -07:00
Wayne Davison cf0f454b8a More manpage improvements. 2010-06-26 10:52:04 -07:00
Wayne Davison d8c55a6e04 Mention more output changes. 2010-06-26 10:01:24 -07:00
Wayne Davison b320b7d6e5 Manpage improvements for --stats, --human-readable, and --list-only. 2010-06-26 10:01:13 -07:00
Wayne Davison 54504b8e4f Output list-only sizes using any human-readable setting. 2010-06-26 10:00:29 -07:00
Wayne Davison de32838ea2 Fix accessors F_LENGTH() and F_MOD_NSEC(). 2010-06-26 08:54:57 -07:00
Wayne Davison ce571e64b6 Close the socket fds in the "post-xfer exec" process. 2010-06-19 10:50:28 -07:00
Wayne Davison 9541770faf Get rid of some trailing whitespace. 2010-06-19 10:49:09 -07:00
Wayne Davison 3be1d9beb2 Fix compression-ignoring of upper-case suffixes.
Fixes bug 7512.
2010-06-19 09:47:00 -07:00
Wayne Davison 292a5c2b72 Fix a couple socketpair_tcp() issues (see bug 7514). 2010-06-19 09:39:55 -07:00
Wayne Davison e60c8b59fe Fix daemon-filter crash issue (bug 7489). 2010-06-04 23:09:20 -07:00
Wayne Davison 130cdd0d90 Avoid a double-increment of a file's st_dev value
while supporting older rsyncs that send dev == 0.
2010-05-29 10:57:09 -07:00
Wayne Davison db22e586da Make sure our idev_find() hashtable use is right. 2010-05-29 09:21:30 -07:00
Wayne Davison 60c25caa64 Make sure we never try to store a 0 key and tweak key64 init. 2010-05-29 09:20:26 -07:00
Wayne Davison c3541d30b6 Turn an assert into two more descriptive errors. 2010-05-29 08:52:59 -07:00
Wayne Davison 86be7a0e64 Mention --debug=hlink's level limits. 2010-05-29 08:52:26 -07:00
Wayne Davison 47573508f4 Update rrsync with the latest options. 2010-05-26 11:24:00 -07:00
Wayne Davison d1fe65fc5e Make an empty-string dest-dir the same as "." again. 2010-05-02 17:05:07 -07:00
Wayne Davison 9fbec6e44c May as well use do_mkdir() directly these days. 2010-04-30 12:58:17 -07:00
Wayne Davison 25082d1ef6 Reject passing an arg to an option that doesn't take one (bug 6915).
Based on a patch by Matt, but further tweaked to deal with -q=foo.

Ultimately this should be upstreamed, but for now lets get this
functionality into rsync.
2010-04-24 10:00:38 -07:00
Matt McCutchen 5deb19e4ea Man page description of --xattrs should not assume a push. 2010-04-24 09:58:09 -07:00
Matt McCutchen 0b8a9bd69d Minor restructuring/clarification to get_backup_name.
(Tweaked by Wayne to follow his preferred style.)
2010-04-24 09:56:34 -07:00
Matt McCutchen 7a3ce973c3 In "ignoring unsafe symlink" messages, show only the file-list path.
Rsync was showing the full destination path, which was confusing because
nothing is created at that path and was especially bogus in combination
with the source name of a solo file.

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=506830
2010-04-24 09:51:54 -07:00
Matt McCutchen be8234cd59 Point out that the file_struct in log_delete is zero-initialized
because it is static.

It took me long enough to realize this that I think it is worth
documenting.
2010-04-24 09:51:05 -07:00
Matt McCutchen d0f2bbb83e Rename configure.in to configure.ac, the current autoconf standard. 2010-04-24 09:34:43 -07:00
Matt McCutchen ef7441669b Fix erroneous "--fake-user" in the rsyncd.conf(5) man page. 2010-04-24 09:30:04 -07:00
Matt McCutchen 9a54a640f7 Don't set the umask to 0 any more: it's ugly and pointless. 2010-04-24 09:28:58 -07:00
Matt McCutchen 58a79f4b44 Amplify the man page description of --hard-links (see bug 3693), and
improve that of --inplace while I'm at it.
2010-04-24 08:11:54 -07:00
Matt McCutchen ae03e0e008 Document the "copy-some-dirlinks" trick in the man page.
Originally explained at:

http://lists.samba.org/archive/rsync/2006-February/014838.html
2010-04-24 08:07:56 -07:00
Wayne Davison 554dc122f2 Removing now-redundant path-size check from send_if_directory(). 2010-03-31 14:57:01 -07:00
Wayne Davison 8c934eba05 Fix a typo that Andrea Gelmini pointed out. 2010-03-27 12:06:45 -07:00
Wayne Davison 41ae04e09c Get rid of trailing whitespace. 2010-03-27 12:05:01 -07:00
Wayne Davison 9a7532e516 Fix directory-length overflow bug (7057). 2010-03-26 16:58:39 -07:00
Wayne Davison 9b594a530f Handle files that spring up while doing backup path checking. 2010-03-09 11:58:07 -08:00
Wayne Davison 5d8dcd1edb Write out the right compat_flags value into the batch file. 2010-02-17 14:55:34 -08:00
Wayne Davison afccb3d326 If a module has no path setting, return an error. 2010-02-06 13:40:12 -08:00
Wayne Davison 0d78a27893 Mention what -XX (repeated --xattrs) does. 2010-02-06 13:32:47 -08:00
Wayne Davison 05c36015f7 More --timeout improvements, especially for the receiving side:
- The receiver now sends keep-alive messages to the generator
  when it is actively doing work and hasn't sent anything
  recently.  This ensures that the generator won't timeout
  if the receiver is working hard.
- The perform_io() code has improved keep-alive participation.
- Allow the sender to send some keep-alive messages, which
  ensures that if it is in a lull, it can probe the socket.
2010-01-02 10:58:39 -08:00
Wayne Davison e34f43495c Don't try to send MSG_ERROR_EXIT after a timeout. 2010-01-02 09:14:19 -08:00
Wayne Davison 4256264991 Always use lchmod() if it is available. 2009-12-31 14:10:38 -08:00
Wayne Davison fa5a06ad0c Mention 2010 in the main copyright. 2009-12-31 13:43:10 -08:00
Wayne Davison 794f2cbab3 Adding release info for 3.0.7 to the trunk. 2009-12-31 13:41:23 -08:00
Wayne Davison 12e59929e2 Allow any gcc to make use of __builtin_alloca for alloca. 2009-12-30 20:04:20 -08:00
Wayne Davison fc4bb1230e Configure check for -Wno-unused-parameter now tries to link too. 2009-12-30 19:59:49 -08:00
Wayne Davison a01e3b490e Change naming of local patch-related branches and unify
the git-status checking into a library.
2009-12-30 13:57:39 -08:00
Wayne Davison 2b2a473831 Add understanding of RSYNC_PROTECT_ARGS environment var. 2009-12-30 12:29:47 -08:00
Wayne Davison 4c4a296209 Allow "./configure --with-protect-args" to make -s the default. 2009-12-29 12:08:41 -08:00
Wayne Davison e89a0fc094 Handle check-in and tagging of patches dir. 2009-12-26 14:29:51 -08:00
Wayne Davison 164faae84f Improve handling of MSG_IO_ERROR message. 2009-12-23 14:14:54 -08:00
Wayne Davison de6ab501b6 Pass the 'f' compatibility flag to the server (via -e)
so that 3.0.7 knows we support the safer flist-xfer method.
2009-12-21 14:37:47 -08:00
Wayne Davison 4286ea6036 Don't die if inflate() returns Z_BUF_ERROR in see_deflate_token(). 2009-12-21 10:15:13 -08:00
Wayne Davison 626b5ae839 Don't set last_io_out in check_timeout. 2009-12-19 13:42:35 -08:00
Wayne Davison 92d021488e Improve --timeout method to take into account all I/O that is going on.
The receiving side also switches timeout handling from the receiver to
the generator, which obviates the need for the sender to send any
keep-alive messages at all (for protocol 31 and beyond).  Given this
setup, all keep-alive messages are now sent as empty MSG_DATA messages,
with MSG_NOOP messages only being understood and (when necessary) acted
upon to forward a keep-alive event to an older receiver.  This is both
safer and more compatible with older versions.
2009-12-19 11:00:36 -08:00
Wayne Davison 82b2a31a46 Added an am_receiver variable. 2009-12-19 10:14:49 -08:00
Wayne Davison eeea1bbd72 Improved some I/O comments. 2009-12-17 09:00:52 -08:00
Wayne Davison aa3999d66c Fix the val reading for MSG_ERROR_EXIT. Use 0-length MSG_DATA when
MSG_NOOP is not available (is both safer and supports older rsyncs).
2009-12-16 12:38:54 -08:00
Wayne Davison 6e9ad2bb03 Allow per-test timeout overrides. Give hardlinks more time. 2009-12-15 08:44:46 -08:00
Wayne Davison c5130bc123 Improve the timeout messages. 2009-12-15 08:32:18 -08:00
Wayne Davison f8e1fa6272 Free a strdup() in do_cmd() that checker was complaining about. 2009-12-13 19:28:01 -08:00
Wayne Davison 8e7f3107a4 Avoid -u option to id since solaris doesn't support it. 2009-12-13 18:15:09 -08:00
Wayne Davison c2dd3ec32c Avoid a compiler warnings about a signed/unsigned mismatch. 2009-12-13 17:52:05 -08:00
Wayne Davison e9ad7bb1f8 Increase the testrun timeout for slow compilefarm systems. 2009-12-13 17:49:09 -08:00
Wayne Davison b9107ee61e Fix a compiler warning about a %d mismatch. 2009-12-13 17:47:35 -08:00
Wayne Davison 45426a7604 Run each testsuite test with a timeout. 2009-12-13 14:14:38 -08:00
Wayne Davison dc2a0923a2 Avoid another checker warning. 2009-12-13 13:21:30 -08:00
Wayne Davison ac32cdd7d4 Prevent the reading of another message before the end of the current one. 2009-12-12 21:54:52 -08:00
Wayne Davison 6d952fdbe7 Change a variable name in read_a_msg(). 2009-12-12 21:40:02 -08:00
Wayne Davison 870cf55287 This should fix another checker warning. 2009-12-12 15:58:53 -08:00
Wayne Davison 7d0fe4da70 Fix checker compile warning. 2009-12-12 15:51:03 -08:00
Wayne Davison 763880ba81 Turn iobuf.in into a circular input buffer. 2009-12-12 15:16:46 -08:00
Wayne Davison 2885270b52 Fix a hang that can happen when the sender is sending an extra file-list
and no one is reading (i.e. do advantageous reading in perform_io()).
2009-12-12 09:46:02 -08:00
Wayne Davison 0c2e8f9364 Don't send MSG_ERROR_EXIT messages at the end of the transfer.
Added some debug output for MSG_ERROR_EXIT messages.
2009-12-12 08:54:36 -08:00
Wayne Davison 24079e988f Don't (wrongly) retouch dir permissions with --fake-super.
(Patch from Matt.)
2009-11-28 21:46:42 -08:00
Wayne Davison 8a68cad1f7 Add IPv6 detection on cygwin. 2009-11-26 15:17:49 -08:00
Wayne Davison d03c0b1ed3 Fix a comment. 2009-11-23 22:45:29 -08:00
Wayne Davison 907e6a32a0 Change the handling of circular buffers to not waste 4 bytes
all the time (we only waste from 1-3 bytes some of the time).
2009-11-23 08:16:18 -08:00
Wayne Davison e4c598c830 Make some RERR_* choices better, and another noop_io_until_death() tweak. 2009-11-16 12:35:17 -08:00
Wayne Davison ae598f3847 Don't complain about a socket EOF unless it affects a read.
Make sure a write error drains any messages in the input buffer.
2009-11-15 12:54:55 -08:00
Wayne Davison d85d029b92 Tweak the noop_io_until_death() timeout and comment it. 2009-11-14 23:05:52 -08:00
Wayne Davison d620b907e4 Improved a couple comments and added some "else" optimizations. 2009-11-14 10:01:13 -08:00
Wayne Davison 5692657757 No need to check MIN_FILECNT_LOOKAHEAD w/extra_flist_sending_enabled. 2009-11-14 09:58:25 -08:00
Wayne Davison c493b6b81d Make the two "wrap-bytes" sections simpler and more similar. 2009-11-14 09:56:42 -08:00
Wayne Davison 4e2a7e59e5 Prefer send_msg_int() over send_msg() for better debug output. 2009-11-14 09:52:40 -08:00
Wayne Davison b82d8c9d1a Tweaked sizing checks in perform_io(). 2009-11-14 09:51:26 -08:00
Wayne Davison 75ea845904 Fixed the buffer-has-space check in write_buf(). 2009-11-14 09:43:02 -08:00
Wayne Davison ce795fcd75 Make --bwlimit take the same size suffixes as the --max-size option
(while keeping it backward compatible). Improve --bwlimit docs.
2009-11-12 23:55:21 -08:00
Wayne Davison aa381148a3 Fix the daemon test when running it as root. 2009-11-12 22:05:45 -08:00
Wayne Davison cece2e3f5e Make use of seteuid() determined by configure. 2009-11-08 20:17:02 -08:00
Wayne Davison f397616e00 Change an RERR_* to RERR_FILEIO. 2009-11-08 11:51:02 -08:00
Wayne Davison 4351c039ad Mention who got the unknown logcode. 2009-11-08 11:50:43 -08:00
Wayne Davison 304d7b5817 More improvements for abnormal exits. 2009-11-08 11:45:55 -08:00
Wayne Davison 9270e88d76 Save first filename and linenum in case exit_cleanup() recurses. 2009-11-08 00:12:33 -08:00
Wayne Davison 2907af472d Try to silence some warnings from "checker". 2009-11-07 09:46:20 -08:00
Wayne Davison 8346c62a95 Mention the error improvements. 2009-11-07 09:46:01 -08:00
Wayne Davison 4655dcf218 Give noop_io_until_death() a time limit. 2009-11-07 09:45:43 -08:00
Wayne Davison f9185203ee Added notifications about error-exit values:
- The receiver notifies the generator if it is exiting with an error,
  and then, if it is a server, waits around for the generator to die.
  This ensures that the client side has time to read the error.
- The generator or sender will notifiy the other side of the transfer of
  an error-exit value if protocol 31 is in effect.  This will get rid of
  some "connection unexpectedly closed" errors that are really expected
  events due to a fatal exit on the other side.
2009-11-07 01:22:11 -08:00
Wayne Davison 84c11e85a4 Fix MSG_IO_TIMEOUT when the daemon is the receiver. 2009-11-01 13:57:17 -08:00
Wayne Davison 6be8a8b14d A daemon treats --msgs2stderr as "output only to the log, not the user". 2009-11-01 13:43:29 -08:00
Wayne Davison ef9d3a152b Make sure rwrite() can handle any logcode value in --msgs2stderr mode. 2009-11-01 13:26:15 -08:00
Wayne Davison fe16d9a67d Fix a hang when dealing with really large numbers of files
in an incremental recursion scan.
2009-10-29 17:35:54 -07:00
Wayne Davison 23a0d1e200 Get rid of some unneeded externs. 2009-10-27 12:38:30 -07:00
Wayne Davison a0f1ed55cd Change the daemon-timeout conveyance into a protocol-31 message. 2009-10-27 12:38:04 -07:00
Wayne Davison 4dde3347fb Fix %b and %c so that they count per-transfer bytes again. 2009-10-25 22:27:01 -07:00
Wayne Davison 44a97a34b1 Enhance log_format_has() to understand the "'" modifier. 2009-10-25 21:24:48 -07:00
Wayne Davison 281a141ea7 Updated NEWS with some of the recent changes. 2009-10-25 13:51:09 -07:00
Wayne Davison 8e6b4ddbe8 A few more --files-from fixes, and an enhanced testsuite for it. 2009-10-24 12:39:58 -07:00
Wayne Davison 9ccc8f8bbb Add a check30 target. 2009-10-24 12:37:26 -07:00
Wayne Davison 2170667802 Make use of $suitedir. 2009-10-24 11:42:29 -07:00
Wayne Davison 705132bcee Fixed some backward-compatibility issues with --files-from. 2009-10-24 11:12:37 -07:00
Wayne Davison 0dd2310cac Moved some --iconv text that was supposed to be in --files-from. 2009-10-24 00:23:21 -07:00
Wayne Davison cbc63a09e8 Fixed a couple iconv loops to properly handle incomplete chars
that span two reads.
2009-10-24 00:19:40 -07:00
Wayne Davison a0a88e0ef3 Move free_xbuf() into ifuncs.h. 2009-10-23 22:55:06 -07:00
Wayne Davison d8a7290f86 Give iconvbufs() an ICB_INIT flag. 2009-10-23 22:51:29 -07:00
Wayne Davison 3b8f819222 A protocol 31 daemon will inform the client about its timeout setting
so that the client will be able to cooperate with keep-alive.
2009-10-20 15:07:51 -07:00
Wayne Davison 8b3e6fb985 Make sure daemon's io_timeout is used as a maximum value. 2009-10-20 15:05:15 -07:00
Wayne Davison d720569422 Moved a few group-related functions with some minor tweaks; 2009-10-20 07:54:21 -07:00
Wayne Davison 0a56ef128c Silence some rprintf() size_t warnings. 2009-10-19 08:06:21 -07:00
Wayne Davison 0a9fbe17de Allow %VAR% environment references in daemon-config parameter values. 2009-10-17 16:30:23 -07:00
Wayne Davison d23cc156aa Call seteuid() when calling setuid(). 2009-10-17 15:03:11 -07:00
Wayne Davison 6f098b0f8c Fix some man page problems Scott Kostyshak pointed out. 2009-10-17 11:06:49 -07:00
Wayne Davison 1ec57e4ddc Fix check for an empty output buffer and limit to flist_eof. 2009-10-17 11:06:20 -07:00
Wayne Davison 20caffd2b3 A major overhaul of I/O routines, creating perform_io().
Files-from data is now sent as multiplexed I/O so that it can mingle
with any messages (such as debug output).  Requires protocol 31.

Protocol 31 no longer disables output verbosity in a couple instances
that used to cause protocol issues.

Got rid of MSG_* messages that have implied raw data that follows after
them.  We instead send a negative index value as a part of the raw data
stream, which is guaranteed to be output together with the following
data.  This only affects the (in-progress) protocol 31 and the (self-
contained) communication stream from the receiver to the generator.

Added --debug=IO and improved --debug=FLIST.  Some --debug=IO output
requires --msgs2stderr to be used to see it (i.e. sending a message
about sending a message would send another message, ad infinitum).
2009-10-17 00:03:32 -07:00
Wayne Davison df6350a8b8 Avoid type-punned compiler warnings for the byteorder.h macros
by using inline functions for the 4-char <-> uint32 conversions.
2009-10-13 21:10:57 -07:00
Wayne Davison 7a7810aa2f Avoid calling send_extra_file_list() when we shouldn't. 2009-10-13 10:45:23 -07:00
Wayne Davison 41000dffc1 Avoid stopping multiplexed out over the message fd.
Use simpler multiplexed-out stopping method.
Make sure we can't false-match a socket fd.
2009-10-12 09:10:50 -07:00
Wayne Davison d8587b4690 Change the msg pipe to use a real multiplexed IO mode
for the data that goes from the receiver to the generator.
2009-10-02 14:18:51 -07:00
Wayne Davison e8bb37f567 Better mask handling, including some changes to help solaris. 2009-09-12 09:40:31 -07:00
Wayne Davison ee1c00fea8 Pass "new_mode" to set_acl() and change its return values. 2009-09-12 09:27:07 -07:00
Wayne Davison 1b502f3ec2 Put file descriptor arg at the start of the arg list for consistency. 2009-09-12 09:13:38 -07:00
Wayne Davison 44d7d045c7 Improve the "--delete does not work without -r or -d" message. 2009-09-07 14:22:59 -07:00
Wayne Davison 1a2e41af94 Add support for transferring & setting nsec time values. 2009-09-07 14:11:58 -07:00
Wayne Davison accc091fe9 Always use lutimes() if it is available. 2009-09-07 13:45:33 -07:00
Wayne Davison 4b660bae92 Add a few new "dont compress" suffixes and improve the docs. 2009-09-05 10:29:29 -07:00
Wayne Davison 2f1fb732d4 Improve error handling and get rid of a lingering fprintf(). 2009-09-05 10:25:42 -07:00
Wayne Davison 59924aa8cf Fix daemon's conveyance of io_error value from sender. 2009-09-05 09:40:53 -07:00
Wayne Davison b15fa9bd7b Avoid an dry-run error trying to stat a prior hard-link
file that hasn't really been created.
2009-09-05 08:18:35 -07:00
Wayne Davison 2c1aa2efac Need to use O_RDONLY in solaris sys_lremovexattr(). 2009-09-03 15:25:55 -07:00
Wayne Davison 6e310d38fc Have --fake-super turn a symlink into a file when
NO_SYMLINK_XATTRS is defined.
2009-09-02 09:06:29 -07:00
Wayne Davison 3b83a22057 Define and use "our_gid" variable. 2009-09-02 08:56:34 -07:00
Wayne Davison 8e15bd87dd Better compiling if SUPPORT_LINKS is not defined. 2009-09-02 08:53:40 -07:00
Wayne Davison 4a440e4be8 Rebuild proto.h if config.h changes. 2009-09-02 08:52:41 -07:00
Wayne Davison 486ecd3d9c Fix attropen() flags for writing an xattr on solaris. 2009-09-02 07:37:55 -07:00
Wayne Davison 17cc4c383b Fix read_xattr() for solaris. 2009-09-01 12:11:32 -07:00
Wayne Davison c55fb5e1d6 Create non-transferred files in a more atomic manner:
If a symlink, device, special-file, or hard-linked file is replacing
an existing non-directory, the new file is created using a temporary
filename and then renamed into place.  Also changed the handling of
a cluster of hard-linked symlinks/devices/special-files to always
ensure the first item in the cluster is correct, since it doesn't
really save any significant work to try to find an existing correct
item later in the cluster to link with.
2009-08-29 16:18:57 -07:00
Wayne Davison 8a21be11f0 Fix the chmod-temp-dir test if /var/tmp doesn't exist.
Fixes bug 6569.
2009-08-22 09:34:01 -07:00
Wayne Davison ce827c3e50 Have the sender use dead time to pad out the file list. 2009-08-22 08:15:26 -07:00
Wayne Davison 2523d0cc14 Allow Solaris sys_llistxattr() to return the list length when size == 0. 2009-08-15 06:43:06 -07:00
Wayne Davison 18bd04018d Fix some variable references. 2009-08-15 06:27:19 -07:00
Wayne Davison 4c3e9c09eb Fix a bogus free in uncache_tmp_xattrs(). 2009-08-14 07:05:48 -07:00
Wayne Davison 05a652d0b7 Some improvements to the solaris xattr routines.
Inspired by the patch to bug 6633.
2009-08-13 08:02:53 -07:00
Wayne Davison 845ed84d70 Fix a warning about a %d not getting an int (on some platforms). 2009-08-10 09:24:53 -07:00
Wayne Davison 049f8cbc8a Initial version of xattr routines for Solaris. 2009-08-08 13:27:58 -07:00
Wayne Davison 0d5ebab1d6 Add conditional support for excluding types of files from xattr ops. 2009-08-08 13:21:26 -07:00
Wayne Davison dab0fb7cf0 Get section reference right. Fixes bug #6573. 2009-08-01 11:29:55 -07:00
Wayne Davison 62342430bf Added solaris IPv6 checking to configure. Fixes #6438.
Patch from Tim Spriggs.
2009-06-05 13:02:55 -07:00
Wayne Davison 63070274f2 Mention that --whole-file is not the default for a local transfer when
writing a batch file.
2009-06-01 09:09:09 -07:00
Wayne Davison 06886d36cf Adding a new script that creates a local patch/* branch
for each file given on the command-line.
2009-05-23 13:40:34 -07:00
Wayne Davison 9ba4d44fa8 Improved the usage output. 2009-05-23 13:40:23 -07:00
Wayne Davison 350242c5c1 Determine the master's commit hash and note it in each patch
that is based on the master in the new "based-on:" line.
2009-05-23 13:12:01 -07:00
Wayne Davison 181c9faf92 Mention some recent changes. 2009-05-23 11:45:17 -07:00
Matt McCutchen c8fa85b23b Refactorings to the filter code, most notably:
- Improve function name: parse_rule -> parse_filter_str (to make the
  similarity with parse_filter_file clearer, and better indicate that
  it can parse multiple rules when FILTRULE_WORD_SPLIT is specified).

- In preparation for rule prefixes containing information beyond the
  rflags, change the code to pass around a full "template" filter_rule
  instead of just rflags.  Callers of parse_filter_{str,file} that want
  to specify only rflags can use rule_template(rflags) .

- Remove the MODIFIERS_* strings and instead hand-code the condition
  under which each modifier is valid.  This should make it easier to
  see that the conditions are correct.

- Tighten up default modifiers on merge rules:
  - Disallow "!" because it isn't useful.
  - If the merge rule specifies a side via "s" or "r", the rules in the
    file cannot also specify a side via "s", "r", "hide", etc.

[Patch was changed by Wayne a bit prior to application.]
2009-05-23 11:20:40 -07:00
Wayne Davison a61ec6b168 Improved a couple variable names. 2009-05-23 10:41:16 -07:00
Wayne Davison 7b6c5c7794 Use typedefs for the filter structures. 2009-05-23 09:07:43 -07:00
Wayne Davison b32d425451 Change filter MATCHFLGs to FILTRULEs. 2009-05-23 09:07:35 -07:00
Wayne Davison 134f97c9cc Support an older AIX system that doesn't have ENOTSUP. 2009-05-14 11:23:38 -07:00
Wayne Davison 87755c6cea Switch from inet_aton() to inet_pton() (since we supply a compatibility
function for the latter, it will always exist).
2009-05-14 11:22:37 -07:00
Wayne Davison 8f73c8a498 Adding release info for 3.0.6 to the trunk. 2009-05-08 11:13:37 -07:00
Wayne Davison 0cdb547f5c Fix typo pointed out by Chris Pepper. 2009-05-07 08:02:17 -07:00
Wayne Davison 56017d3150 Enhance name_to_{u,g}id() to optionally parse numbers and rename
to {user,group}_to_*().  Based on a patch by Matt McCutchen.
2009-05-07 07:34:42 -07:00
Matt McCutchen d960af72b2 Move the description of include/exclude modifiers to a better place
in the man page.
2009-05-05 07:46:54 -07:00
Wayne Davison fd2b6046cb Clarify which options are transfer rules, and what that means. 2009-04-27 07:26:23 -07:00
Wayne Davison a8e6e14869 Change sending/receiving/storing of the rdev value for special files.
Since the value is not needed, protocol 31 no longer sends it, while
older protocols are optimized so the sender just sends a valid rdev
value as efficiently as possible.  The receiver no longer caches an
rdev value for special files, and the generator will always pass a 0
rdev value to do_mknod() for special files. Fixes bug #6280.
2009-04-26 07:43:32 -07:00
Wayne Davison 307555eba3 Use upper-case HLINK in a --debug setting to avoid a really weird bug
in the strncasecmp() of OpenSUSE 10.2 (x86_64).
2009-04-13 21:07:12 -07:00
Wayne Davison 261808dac1 Switch to upper-case in the {debug,info}_verbosity arrays. 2009-04-13 07:37:49 -07:00
Wayne Davison 5250c3bc20 Changed the commands used to "make gen" without any stoppage. 2009-04-12 15:44:52 -07:00
Wayne Davison 9eaba7266c Don't allow --remove-s*-files with --read-batch. 2009-04-12 12:57:31 -07:00
Wayne Davison d510e82fc6 Fixed the use of --xattrs with --only-write-batch. 2009-04-12 12:51:20 -07:00
Wayne Davison 4e9c7fae8f The suffix must be non-empty if the backup-dir is the same as the dest
dir.
2009-04-11 17:31:13 -07:00
Wayne Davison 3696674bc6 More backup improvements:
- Changed get_backup_name() to verify the backup path, and make any
  missing directories.  This avoids accidental use of a symlink as a dir
  in a backup path, and gets rid of any other non-dirs that are in the
  way.  It also avoids the need for various operations to retry after
  calling make_bak_dir(), simplifying several pices of code.
- Changed create_directory_path() to make_path(), giving it flags that
  lets the caller decide if it should skip a leading slash or drop the
  trailing filename.
- Mention when we create the backup directory, so the user is not caught
  unaware when rsync uses a directory they didn't expect.
- Got rid of some dir-moving backup code that is not used.
- Added a little more backup-debug output.
2009-04-11 11:51:07 -07:00
Wayne Davison 5e2d51ee06 Fix "just in case" unlink. Prefer renaming of normal files
if hard-linking fails.
2009-04-11 06:37:49 -07:00
Wayne Davison d735fe2014 Improved link_or_rename() to handle prefer_rename better. 2009-04-10 23:33:08 -07:00
Wayne Davison 7e6c8ad653 Don't try to backup a file being removed from the backup area. 2009-04-10 23:14:06 -07:00
Wayne Davison 407ea78a62 Allow a "make reconfigure" to continue, even if the Makefile changes. 2009-04-10 16:24:12 -07:00
Wayne Davison cb197514d9 Fixed an ACL/xattr corruption issue where the --backup option could cause
rsync to associate the wrong ACL/xattr information with received files.
2009-04-10 16:22:44 -07:00
Wayne Davison a055dbdd88 Allow $RSYNC_TEST_TMP to indicate a good tmp dir for our tests. 2009-04-10 08:19:16 -07:00
Wayne Davison 5eb8bd4962 Don't try to simplify an ACL that has a mask w/o any named values. 2009-04-09 22:49:24 -07:00
Wayne Davison e129500c85 Some improvements to the rsync.yo manpage:
- Mention the switch from MD4 to MD5.
- Mention the default for the --log-file-format option.
2009-04-07 07:39:34 -07:00
Wayne Davison 7fc2ca2551 Make sure that config.h.in is up-to-date before allowing the
Makefile-updating rule to run ./config.status.
2009-04-04 07:52:26 -07:00
Wayne Davison 83c32ca572 Fixed --dry-run with --read-batch:
- Avoid sending MSG_NO_SEND to the generator.
- Check if the file is wanted before discarding the batched data.
2009-04-04 07:25:25 -07:00
Wayne Davison 1a85a969a7 Fixed improper deletion of mount-point hierarchies.
Fixes bug #6240.
2009-03-31 20:15:44 -07:00
Wayne Davison 7f2591eaea Fixed a word ending that Jesse Weinstein and revamp some of the text
to make it clearer.
2009-03-31 14:34:31 -07:00
Wayne Davison eecc969e9b Make symlink iconv work for a local copy.
Fixes issue mention in bug #5615.
2009-03-29 13:24:15 -07:00
Matt McCutchen f8605c5b89 Give a meaningful error message when we fail to write to a batch file. 2009-03-13 09:45:35 -07:00
Wayne Davison 42d8ec616d My version of Matt's improvements related to missing source args:
- Implement --ignore-missing-args.
- In the absence of --*-missing-args, a missing source arg is an
  FERROR_XFER, but doesn't need to be an IOERR_GENERAL.
- Revise the man page.
2009-03-13 09:37:31 -07:00
Wayne Davison 7781241889 Make missing args governed by protect filters, not hide. 2009-03-13 08:49:53 -07:00
Wayne Davison b5473dd4a9 Made --list-only output missing args as a "*missing" line. 2009-03-06 22:42:13 -08:00
Wayne Davison 4e0fa131fe Don't let --chmod tweak a 0 mode value (which marks a missing arg). 2009-03-06 22:41:23 -08:00
Wayne Davison 17a1676976 Simplify an "if" in ssh-basic.test. Fixes bug #6169; 2009-03-06 07:07:43 -08:00
Wayne Davison b4d30300b9 Improved the unsafe_symlink() code to not get fooled by extra '/' chars
in the symlink's path.  Added test cases.  This fixes bug #6151.
2009-03-03 08:46:57 -08:00
Wayne Davison e6f3a33c5e Make the backup code call unsafe_symlink() correctly. 2009-03-03 08:42:56 -08:00
Wayne Davison ce66f41791 Added the --delete-missing-args option to delete specified
files on the receiver that don't exist on the sender.
2009-02-28 09:25:26 -08:00
Wayne Davison 8d10cbfcb1 Made --progress use ir-chk instead of to-chk when the incremental
recursion scan is still active.  Mention the output change more
prominently in the NEWS file.  Updated the --progress output in
the manpage, with mention of the new "ir-chk" string's meaning.
2009-02-28 08:07:01 -08:00
Wayne Davison e8d6fe6261 Properly indent some lines. 2009-02-19 23:09:20 -08:00
Wayne Davison 7f367bb1b4 Added a way for supplementary groups to be specified in the rsyncd.conf
file.  Also made explicitly-set uid/gid values no longer ignored by a
daemon that was not run by a super-user.
2009-02-19 23:08:48 -08:00
Wayne Davison df7ec1cf42 Adding a way for log-format numbers to become more human readable. 2009-02-18 07:26:48 -08:00
Wayne Davison 6437b817c0 Mention that only the first line of a password-file is used. 2009-02-16 10:31:58 -08:00
Wayne Davison d39d60a129 Handle a link_stat() failure with errno ENOENT as a vanished file. 2009-02-14 08:35:57 -08:00
Wayne Davison 49c2407141 Added --disable-iconv-open option for configure to turn off all use
of the iconv_open() function.  Implies --disable-iconv (which turns
off the --iconv option).  Fixes bug #6107.
2009-02-14 07:50:09 -08:00
Wayne Davison 44ae54114a Moved the --disable-debug check sooner in configure.in so that it
happens prior to checking for the compiler.  Switched no-debug code
to setting ac_cv_prog_cc_g=no.  Fixes bug #6106.
2009-02-14 07:48:40 -08:00
Wayne Davison 43eb865f9a Made copy_section() and string_set() simpler, getting rid of a
"FIXME" comment that we don't need to fix.
2009-02-06 07:27:18 -08:00
Wayne Davison 789213909d Combine Globals and Locals into a Vars struct that parallels Defaults,
shortening some code.  Improve comments and make other minor cleanups.
Based on a patch that Matt McCutchen posted to the mailing list.
2009-02-05 07:35:33 -08:00
Wayne Davison 46cd1ef6bc Ensure that the sender turns off any msg_fd_in use earlier.
This avoids a problem where an extra message from the sender
could give the generator time to start sending data that will
not be understood by the sender's use of read_msg_fd().
2009-02-04 18:23:59 -08:00
Wayne Davison 6e80613717 Do not try to send a symlink with a 0-length value.
This avoids a transfer error in the receiver.
2009-02-04 18:13:09 -08:00
Wayne Davison dc6fb11b41 We only need to send --stats to a remote receiver now. 2009-01-30 08:45:54 -08:00
Wayne Davison 4d13a2fe55 A few more improvements to the hostspec-parsing code. 2009-01-28 23:17:46 -08:00
Wayne Davison 58cf354711 Fixed the parsing of IPv6 literal addresses with a username
prefixed.  Fixes bug #6067.
2009-01-28 15:59:06 -08:00
Wayne Davison 7a498f5b6c Check the right flist_num in gen_wants_ndx(). 2009-01-18 23:02:36 -08:00
Wayne Davison 243e9a366d Added a "Defaults" structure with both globals and locals in it.
Initialize both the Globals and Locals back to their default values
when reading the config.  This fixes a bug where locals set in the
global section were not getting reset to their default value if the
config item was removed from the file.
2009-01-18 22:42:41 -08:00
Wayne Davison b53c202e45 A couple minor function-call tweaks. 2009-01-18 22:40:17 -08:00
Wayne Davison aef68d7892 Renamed some typedefs:
- global -> global_vars
- section -> local_vars
- global_and_section -> all_vars
2009-01-18 22:39:57 -08:00
Wayne Davison 077e543b7d Renamed sDefault to Locals. 2009-01-18 22:39:20 -08:00
Wayne Davison 9784f01cbc Use a varint when sending the error_code. 2009-01-17 14:52:13 -08:00
Wayne Davison bc40a30503 Fixed the delete statistics with --delete-delay and --delete-after. 2009-01-17 14:46:42 -08:00
Wayne Davison 4079d6a684 Fixed a hang in the inc_recurse batch-reading code. 2009-01-17 13:59:08 -08:00
Wayne Davison df694f72ed Change some args from "char *" to "const char *" in order to get rid of
a compiler warning that was just introduced.  Also avoids changing the
host string to lower-case in access.c (by using iwildmatch()).
2009-01-15 00:23:07 -08:00
Matt McCutchen 11ef77b76a Added the "reverse lookup" daemon-config parameter. 2009-01-15 00:22:36 -08:00
Wayne Davison abd32c9585 Send the --stats option for proper del-stats operation. 2009-01-14 07:38:37 -08:00
Wayne Davison 01e293f1b5 Use "use warnings" rather than -w on the #! line. 2009-01-13 14:52:03 -08:00
Wayne Davison 8051aa5a34 Adding recent release info from the 3.0.x branch. 2009-01-13 13:53:01 -08:00
Matt McCutchen 46d68be3da Fixed recv_add_uid() to properly differentiate users and groups. 2009-01-13 11:21:06 -08:00
Matt McCutchen bb499bd7a0 Handle simultaneous arrival of multiple connections. 2009-01-13 09:42:54 -08:00
Wayne Davison 416cef36e9 Tweaked the --delete-* option summaries. 2009-01-08 17:01:38 -08:00
Wayne Davison 1fb6a4018d Avoid a server-side problem with -e is at the start of the short options.
(Bug #6020)
2009-01-07 16:39:22 -08:00
Wayne Davison fc4a695cdd Tweaked s### and m## to avoid vim highlighting issues. 2009-01-07 16:38:06 -08:00
Wayne Davison 83238ed0bb Fixed bug #6011: use of target in configure. 2009-01-03 20:50:54 -08:00
Wayne Davison 21cddef2b4 Improved the backup code:
- Backups do not interfere with an atomic update (when possible).
- Backing up a file will remove a directory that is in the way
  and visa versa.
- Unify the backup-dir and non-backup-dir code in backup.c.
- Improved the backup tests a little bit.
2009-01-03 12:02:47 -08:00
Wayne Davison b3bf9b9df9 Update the copyright year. 2009-01-03 10:57:14 -08:00
Wayne Davison 974e18191c Make delete_item() public, moving it into delete.c. 2009-01-03 10:52:50 -08:00
Wayne Davison 09ca0d15d3 Added init_stat_x() to avoid duplication of acl/xattr init code. 2009-01-03 08:53:59 -08:00
Wayne Davison c43c66125e Allow opendir() in send_directory() to fail with ENOENT. 2008-12-27 11:09:53 -08:00
Wayne Davison 8b7a752024 Mention the mapfrom/mapto scripts and how they work. 2008-11-15 18:32:21 -08:00
Wayne Davison 2df20057e3 Adding the --usermap/--groupmap/--chown options. 2008-11-15 18:13:00 -08:00
Wayne Davison 9556f156a9 Make it clearer which configure files changed. 2008-11-15 15:31:38 -08:00
Wayne Davison aade88bfc2 An ftruncate() failure should result in FERROR_XFER. 2008-11-15 14:50:40 -08:00
Wayne Davison 3795dafd97 Change clean_fname() to keep "//" at the start for cygwin. 2008-11-15 14:18:36 -08:00
Wayne Davison e4ed195bb7 Change some size_t vars to ints. 2008-11-11 18:06:11 -08:00
Wayne Davison faf980ffb5 Make sparse_seek an OFF_T (pointed out by Pedro Valasco). 2008-11-11 18:05:27 -08:00
Wayne Davison b3ad9649bc A "make reconfigure" doesn't stop if configure changes. 2008-11-11 15:55:14 -08:00
Matt McCutchen d4d56eed8a Add flist_find_ignore_dirness() and change delete_in_dir() to use it.
This fixes an issue with -K noticed by eric casteleijn, avoids some
inconsistent itemizing when a file/dir is replaced by a dir/file,
and removes a now-obsolete chunk of code from make_file().
2008-11-10 07:05:21 -08:00
Wayne Davison 3ce3cabe34 Fixed the use of a dot-dir path (foo/./bar) inside of a files-from file. 2008-11-09 21:37:04 -08:00
Wayne Davison 9411292489 Fixed a bunch of "warn_unused_result" compiler warnings. 2008-11-09 18:56:21 -08:00
Wayne Davison b4de848d75 Avoid a potential hang when --remove-*-files is active. 2008-11-09 17:59:11 -08:00
Matt McCutchen 89cb47212e The protect filter automatically added with --backup is not perishable
(see f41152d393), so remove the inaccurate
"p" from the man page.  Noticed by Jacob Balazer:

http://lists.samba.org/archive/rsync/2008-November/022022.html
2008-11-02 20:52:27 -08:00
Wayne Davison 1049378d9c Mention rsync's definition of client and server. 2008-10-25 09:43:50 -07:00
Wayne Davison 9ddc2b64da Fixed our supplied getnameinfo()'s ability to do a reverse lookup,
as reported in bug 5851.
2008-10-25 09:21:13 -07:00
Wayne Davison b3347e2a03 Adding hashtable debugging output (--debug=hash). 2008-10-15 07:51:45 -07:00
Wayne Davison d8e8ef323a Fixed a glitch when using -s with a remote-shell daemon. 2008-10-11 11:11:10 -07:00
Wayne Davison ea0f037930 Don't lookup address "0.0.0.0" when we're a remote-shell daemon.
Gets rid of a DNS delay waiting for a lookup failure.
2008-10-11 11:00:51 -07:00
Wayne Davison 08b7c3ed83 Fixed send_protected_args() to send "." in place of an empty arg. 2008-10-11 10:27:16 -07:00
Wayne Davison 76181461f5 Added a fully atomic update if the user has setup a symlink
to a *-1 or *-2 directory.  A few other minor improvements.
2008-10-11 09:30:26 -07:00
Wayne Davison 2c11e80e2e Fix the error message on one of the rename operations. 2008-09-29 21:54:49 -07:00
Wayne Davison e366e5303f Enhanced the --stats output:
- Mention how many files were created (protocol >= 29).
- Mention how many files were deleted (new in protocol 31).
- Follow the file-count, created-count, and deleted-count
  with a break-out list of each count by type.
2008-09-26 22:14:01 -07:00
Wayne Davison 0a23e33630 Properly ignore source args on a --read-batch command. 2008-09-26 20:45:49 -07:00
Wayne Davison 8ab02ccd52 More batch-mode fixes to handle redos properly (and without hanging). 2008-09-26 20:32:04 -07:00
Wayne Davison e0c572c5c6 Moved the flist_ndx_{push,pop}() routines from io.c into util.c. 2008-09-26 19:45:08 -07:00
Wayne Davison 315c2152d0 Initialize xattr data in a couple spots in the hlink code, which avoids
a crash when the xattr pointer's memory happens to start out non-zero.
Also fixed the itemizing of an alt-dest file's xattrs when hard-linking.
2008-09-24 08:00:50 -07:00
Wayne Davison 6d301fa3de Don't send a bogus "-" option to an older server if there were
no short options specified.
2008-09-23 19:35:36 -07:00
Wayne Davison af2dea6033 Fixed skipping of unneeded updates in a batch file when
incremental recursion is active.  Added test.
2008-09-23 19:31:14 -07:00
Wayne Davison 7fdb3bdab9 Remove bogus "non-empty" qualifier in '*' discussion. 2008-09-14 19:58:15 -07:00
Wayne Davison 794d033981 A couple instant-rsyncd improvements:
- Prompt the user for the parameters when missing.
- Allow the creation of a module without a user+password.
2008-09-11 08:50:14 -07:00
Matt McCutchen 45574a73c1 Add instant-rsyncd to support/ . 2008-09-11 08:05:06 -07:00
Wayne Davison 26e21efc5a Convey the cleaned-up module-path to the user in all cases.
Fixed a just-introduced problem with a relative module-path.
2008-09-11 07:35:40 -07:00
Wayne Davison 433c6753a8 Fix the %P logfile escape inside a chroot. 2008-09-10 16:45:06 -07:00
Wayne Davison 46b1361b41 Adding 3.0.4 release line to OLDNEWS. 2008-09-06 09:50:49 -07:00
Wayne Davison 9e7acc86c3 Adding human_readable var. 2008-09-03 16:10:45 -07:00
Wayne Davison 56c7ed9983 Changed some "rsync" commands into proper "$RSYNC" commands. 2008-09-03 12:15:36 -07:00
Wayne Davison 5dd14f0c33 Split up the ifuncs.h file into 3 .h files. 2008-09-01 19:11:36 -07:00
Wayne Davison 2a147e9fcb Don't define an array with no size. 2008-09-01 19:01:48 -07:00
Wayne Davison 7634fc8eb6 A little tidying up to follow my preferred style. 2008-09-01 17:08:26 -07:00
Matt McCutchen daa8d92094 Several fixes for merge file handling:
- Free a mergelist's parent_dirscanned filters the last time it is
  popped or as soon as the filters are discarded due to the "n"
  modifier.  Aside from not leaking memory, this is needed to clean up
  any mergelists defined during the parent_dirscan to avoid crashing by
  trying to restore nonexistent state for them in pop_local_filters.
- Make push_local_filters save the current mergelist_cnt, and make
  pop_local_filters assert that it has the correct number of mergelists
  before restoring their state.
- Assert that mergelists get deactivated in strict LIFO order to catch
  any glitches as soon as they happen.  Free linked lists of filters in
  reverse order to make that the case.
- Add a bunch of mergelist-related debug output (--debug=filter2).
2008-09-01 17:01:19 -07:00
Wayne Davison adc2476fa2 Output numbers in 3-digit groups by default (e.g. 1,234,567).
Also improved the human-readable output functions, including
adding the ability to output negative numbers.
2008-09-01 13:27:11 -07:00
Wayne Davison 34c3ca8f35 Verify that SUBPROTOCOL_VERSION is set correctly when making a
nightly tar file release.  Fixed the opening comments.
2008-08-31 11:55:09 -07:00
Wayne Davison 4f282b0b92 Added extra file-changing logic to ensure that the various files that
mention the protocol number have the right value, that the check-in date
for a protocol-change release is specified, and that a pre-release with
a protocol change doesn't have SUBPROTOCOL_VERSION set to 0.  Prompt for
releasing a branch if -b option was not used and we're on a branch.
2008-08-31 10:13:38 -07:00
Wayne Davison 8b3e60523a Improved the fix that ensures that the generator gets notified about an
I/O error for the incremental directory that generated the error.  The
PROTOCOL_VERSION was bumped to 31 to implement this.
2008-08-31 09:43:39 -07:00
Wayne Davison 1d891835e7 Improved rwrite() to handle a stderr exception without playing games
with the msgs2stderr value.
2008-08-24 14:07:10 -07:00
Wayne Davison 0b78944600 Some minor improvements to the flushing code to try to make it
even more solid.
2008-08-24 13:40:36 -07:00
Wayne Davison 79daa59618 Make the !flist_eof assumption explicit before the check_for_io_err
code calls wait_for_receiver().
2008-08-24 12:54:49 -07:00
Wayne Davison 72de4140d5 Added /support/savetransfer to .gitignore. 2008-08-17 09:28:50 -07:00
Wayne Davison 94a4a125bc An improved RERR_PARTIAL message. 2008-08-17 09:23:28 -07:00
Wayne Davison e982d59146 Changed flist_for_ndx() to optionally die with an error
if the index isn't found.
2008-08-14 07:40:56 -07:00
Wayne Davison c78e4ea905 Made an error of readlink_stat() use the right function name. 2008-08-10 07:32:54 -07:00
Wayne Davison 0b7bce2c7b Make sure that the hlink node->data allocation doesn't fail. 2008-08-08 07:48:41 -07:00
Wayne Davison 87678cefd1 Tweaked the symlink iconv buffer size and fixed a comment. 2008-08-02 13:45:50 -07:00
Wayne Davison 7c7462cd30 When using --iconv, if a server-side receiver can't convert a filename,
it now outputs the name back to the client without mangling the charset.
2008-08-02 10:26:17 -07:00
Wayne Davison e34ad4e925 Refer to the symlink's contents as "symlink data", not "symlink name". 2008-08-02 10:20:51 -07:00
Wayne Davison f303b749f2 Added logic to the receiving side to ensure that the --delete-during
code will not delete in a directory prior to receiving an I/O error
for that directory (or not receiving it, as the case may be).
2008-08-02 09:14:36 -07:00
Wayne Davison 91fd15b8b6 Skip new symlink conversion step if the remote rsync is not
new enough to do symlink content conversions.
2008-08-02 07:06:15 -07:00
Wayne Davison 6e5b682273 The --iconv option now converts the content of a symlink too. 2008-08-01 19:21:02 -07:00
Wayne Davison 902ee53ea4 Fixed a problem with checking for the '.' dir in the first file
list that is transferred.  This fixes a glitch where a failed
--iconv conversion on the receiving side could prevent deletions
from happening in the root-dir of the transfer.
2008-08-01 19:03:59 -07:00
Wayne Davison c9f540f37d Changed the iconv-related message that was being output as the
lone --info=misc2 message into a --debug=iconv message so that
all iconv info will be output when requesting iconv debugging.
2008-08-01 18:15:28 -07:00
Wayne Davison 0479eb7601 Fixed a couple minor problems in util.c:
- Make sure that handle_partial_dir() never returns a truncated fname.
- Make robust_rename() return that it failed to do a cross-device
  copy if the partial-dir could not be created.
2008-08-01 18:04:24 -07:00
Wayne Davison 459abd75f5 Properly handle a failure to create a partial directory, which is
especially important for --delay-updates, particularly when
--remove-source-files was also specified.
2008-08-01 18:03:57 -07:00
Wayne Davison 342bfb5e23 Output an FERROR* for a general io_error, and an FWARNING for other
io_error flags.
2008-07-31 07:59:45 -07:00
Wayne Davison 1fe0d14263 Mention a missing sender-side hash improvment that went out in 3.0.0. 2008-07-30 08:33:05 -07:00
Wayne Davison c8b823f9d8 Make hard-linking work when a device has an st_dev of 0. 2008-07-29 18:06:26 -07:00
Wayne Davison 45c37e737f Mention some mount options that can interfere with --link-dest. 2008-07-28 18:25:18 -07:00
Wayne Davison 41adbcec9f Added a client --munge-links option that works like the daemon
"munge symlinks" parameter.
2008-07-28 16:35:03 -07:00
Wayne Davison 582831a447 - Don't require a daemon config &directive to use an equal sign.
- Improved some daemon-config error messages.
2008-07-27 16:25:11 -07:00
Wayne Davison 312b68417f Made include_config() more efficient, and fixed a memory leak. 2008-07-27 15:14:20 -07:00
Wayne Davison 2206abf884 Added a command-line override for daemon config parameters:
--dparam=PARAMETER=VALUE (-M PARAMETER=VALUE).
2008-07-27 12:13:35 -07:00
Wayne Davison fcd613d6c7 - Got rid of unused pstring/P_GSTRING/P_SEP/P_SEPARATOR code.
- Made pointer-adding code a little better.
2008-07-27 12:06:26 -07:00
Wayne Davison 8a3ddcfc81 Added &include and &merge config-file directives that allow the
daemon's config file incorporate the contents of other files.
2008-07-26 20:03:45 -07:00
Wayne Davison c9604e2115 Changed the module array to use an item_list structure. 2008-07-26 19:57:02 -07:00
Wayne Davison b583594ac7 Change the references to "service" to be either "section" or "module". 2008-07-26 19:11:32 -07:00
Wayne Davison 36828daef1 Reorder the static functions to avoid the need for forward declarations. 2008-07-26 17:47:02 -07:00
Wayne Davison 8880d8ec5e Since the loadparm.c file is changing, I'm reformatting it to use the
rsync style.
2008-07-26 17:42:09 -07:00
Wayne Davison 56fc9f70d3 Enhanced the release scripts to be able to handle a branch release. 2008-07-23 23:40:06 -07:00
Wayne Davison aacd188034 Fixed a potential alignment issue in the IRIX ACL code when allocating
the initial struct acl object.  Also, cast mallocs to avoid warnings.
2008-07-22 08:31:17 -07:00
Wayne Davison bb640d3221 Explicitly cast a -1 that is being assigned to a size_t. 2008-07-21 23:22:40 -07:00
Wayne Davison 0566dc54b1 Use PTR_ADD for the new instances of void-pointer arithmetic. 2008-07-21 23:12:02 -07:00
Wayne Davison aad635f766 Explicitly cast an int64 to an int32. 2008-07-21 23:11:23 -07:00
Wayne Davison a72f37bb67 Got rid of a variable that was set but not used. 2008-07-21 23:11:04 -07:00
Wayne Davison 93465f51bc Improved var-checker and tweaked all the issues it found. 2008-07-21 00:10:22 -07:00
Wayne Davison 26a7af26aa Renamed extern-squish -> var-checker. 2008-07-21 00:09:16 -07:00
Wayne Davison b791d6802b Include the array-size in array externs so that IBM's code-checker
can do more checking for us.
2008-07-20 22:41:29 -07:00
Wayne Davison 741597c2df Turn off extra debugging now that the problem is fixed. 2008-07-20 22:28:19 -07:00
Wayne Davison 37077bd339 Improved the handling of --msgs2stderr a little more. 2008-07-20 22:27:23 -07:00
Wayne Davison ced4fd8993 Fixed a bug in match_hard_links() where an empty directory would try
to allocate 0 bytes of memory (which can fail on some OSes).
2008-07-20 20:34:06 -07:00
Wayne Davison eabc85ef5e Added a debug-helping option, --msgs2stderr, than should help all
messages to be seen in a situation where rsync is dying (as long
as stderr is a viable output method for the remote rsync).
2008-07-20 20:08:08 -07:00
Wayne Davison 5a18b34de3 Changed the chksum debug flag to deltasum. 2008-07-20 20:02:09 -07:00
Wayne Davison 56ac812359 A few more HLINK debug messages. 2008-07-20 13:54:53 -07:00
Wayne Davison e42fc158c9 Output even more debug messages. 2008-07-20 13:06:54 -07:00
Wayne Davison 886df221c1 Added a '%C' (MD5 checksum) flag for the output/logfile formatting. 2008-07-19 22:50:28 -07:00
Wayne Davison fb01d1fb07 Changed the POOL_QALIGN flag to POOL_NO_QALIGN, reversing the setting
(making pools aligned by default).  Added the missing code to make the
documented behavior of pool_free() with a NULL addr work.  Updated the
pool_alloc.3 manpage.
2008-07-19 09:20:56 -07:00
Wayne Davison 51ce67d599 Improved the alignment code and changed POOL_APPEND to POOL_PREPEND. 2008-07-18 20:57:52 -07:00
Wayne Davison a02348b5df We now pass the POOL_QALIGN flag to pool_create(). Also optimized
the verbose-message check at the start of recv_file_list().
2008-07-18 20:46:58 -07:00
Wayne Davison 28a5b78c6f Improved the hard-link logging. 2008-07-18 17:35:22 -07:00
Wayne Davison 7bbd60101e Turn on flist5 debugging. 2008-07-18 17:34:59 -07:00
Wayne Davison d239efa3ff Some minor tweaking for the info+debug option parsing. 2008-07-18 08:17:05 -07:00
Wayne Davison 269a8fb604 Make the hands.test use a higher hlink debug level. 2008-07-18 08:12:06 -07:00
Wayne Davison b1617a0825 Add --debug=hlink to hands.test. 2008-07-18 07:19:21 -07:00
Wayne Davison 806f530bcb Don't interrupt the make for a generated file didn't really change. 2008-07-17 20:02:56 -07:00
Wayne Davison 7f0db4fd8e Use big_num() in a few more places. 2008-07-17 17:01:10 -07:00
Wayne Davison 3a8fad7805 Moving big_num() into lib/compat.c so tls.c can use it. 2008-07-17 16:59:59 -07:00
Wayne Davison 0c096e29aa Added some HLINK debugging output and enabled it for hardlink tests. 2008-07-17 07:43:11 -07:00
Wayne Davison 6d56efa6ea Changed human_num() to big_num() with an extra arg so that it can
be used in place of all %.0f output idioms.
2008-07-17 07:37:31 -07:00
Wayne Davison 97dde6b620 A couple xattr fixes for --fake-super. 2008-07-14 23:48:33 -07:00
Wayne Davison 75d9697869 A few more minor improvements in the --info/--debug code. 2008-07-14 23:36:21 -07:00
Wayne Davison f35798a57e Added a "test_fail" function to 00-hello.test. 2008-07-14 22:47:03 -07:00
Wayne Davison b8993a1ee9 Made the info_verbosity array 1 element larger. 2008-07-14 07:40:10 -07:00
Wayne Davison 951e826b75 Added the --info=FLAGS an --debug=FLAGS options, which allows
fine-grained output control (in addition to the coarse -v).
2008-07-13 20:51:08 -07:00
Wayne Davison d8d1389348 Fixed the timeout/flush loop-check logic to work properly with
incremental recursion.
2008-07-13 17:29:47 -07:00
Wayne Davison 2970362338 If the user specifies --protocol=29, rsync will avoid sending an -e
option to the server (which is only useful for protocols 30 and above
anyway).  This gives the user an easy way to talk to a restricted
server that has overly restrictive option-checking.
2008-07-11 09:48:33 -07:00
Wayne Davison 7a2eca415b Added the --remote-option=OPT (-M OPT) option. 2008-07-05 08:31:16 -07:00
Wayne Davison 854411909b Got rid of some trailing whitespace. 2008-07-05 01:41:19 -07:00
Wayne Davison bb4e4d889f The --progress output now leaves the cursor at the end of the line
(instead of the start) in order to be extra sure that an error won't
overwrite it.  We also ensure that the progress option can't be enabled
on the server side.
2008-07-05 00:31:46 -07:00
Wayne Davison 93f3fbf73e Prepare repository for more development. 2008-07-04 23:45:57 -07:00
Wayne Davison d252e47d09 Improved the docs for various delete options. 2008-07-04 13:14:16 -07:00
755 changed files with 114045 additions and 27493 deletions

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
* text=auto eol=lf
# The rsync-web/ subdirectory holds the project website source content
# (mirrors what gets pushed to https://rsync.samba.org). Exclude it from
# `git archive` output so the release source tarball produced by
# packaging/release.py step_7_tarball does not bloat with HTML the
# tarball doesn't need.
/rsync-web/ export-ignore
# old_versions/ holds static binaries of historical rsync releases, used by the
# version-mixing test suite (.github/workflows/ubuntu-version-mix.yml) to run
# the current code against a real old peer over the daemon / remote-shell.
# Mark the binaries as binary so the `text=auto eol=lf` rule above can't try to
# normalise line endings and corrupt them; export-ignore keeps them out of the
# release source tarball.
/old_versions/rsync_* binary
/old_versions/rsync_* export-ignore
+4
View File
@@ -0,0 +1,4 @@
# These are supported funding model platforms
github: RsyncProject
patreon: AndrewTridgell
+46
View File
@@ -0,0 +1,46 @@
name: Lint GitHub Actions workflows
# Static-check the workflow YAML with rhysd/actionlint. Catches missing
# secrets, bad expressions, expression-type errors, unsupported runner
# images, and (via embedded shellcheck) common pitfalls in `run:` scripts.
# Trigger only on changes under .github/workflows/ so the rest of the
# matrix isn't billed when nothing here moves.
on:
push:
branches: [ master ]
paths:
- '.github/workflows/*.yml'
- '.github/actionlint.yaml'
- '.github/actionlint.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths:
- '.github/workflows/*.yml'
- '.github/actionlint.yaml'
- '.github/actionlint.yml'
permissions:
contents: read
jobs:
actionlint:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: actionlint
steps:
- uses: actions/checkout@v4
- name: install actionlint
# Pin a version so this job is reproducible; bump deliberately.
# The download script verifies a SHA256 of the release tarball.
run: |
bash <(curl --proto '=https' --tlsv1.2 -fsSL \
https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) \
1.7.12
echo "$PWD" >>"$GITHUB_PATH"
- name: actionlint --version
run: actionlint -version
- name: actionlint .github/workflows/*.yml
run: actionlint -color
+86
View File
@@ -0,0 +1,86 @@
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:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/almalinux-8-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
container:
image: almalinux:8
name: Test rsync on AlmaLinux 8
steps:
- name: install git
# actions/checkout needs git in the container before the checkout step.
run: dnf -y install git
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
# PowerTools is needed for libzstd-devel etc; xxhash and lz4 dev
# headers live in EPEL on RHEL 8. The default python3 on RHEL 8
# is 3.6, which is too old for runtests.py (uses capture_output=
# / text= introduced in 3.7), so install python39 and point
# /usr/bin/python3 at it.
run: |
dnf -y install epel-release
dnf config-manager --set-enabled powertools
dnf -y install gcc gcc-c++ make autoconf automake m4 \
python39 python39-pip diffutils \
openssl openssl-devel \
attr libattr-devel acl libacl-devel \
zstd libzstd-devel \
lz4 lz4-devel \
xxhash xxhash-devel
alternatives --set python3 /usr/bin/python3.9
pip3 install commonmark
- name: configure
run: ./configure --with-rrsync
- name: make
run: make
- name: info
run: ./rsync --version
- name: check
# In the container we already run as root, so no sudo. The
# crtimes-not-supported skip matches the other Linux jobs;
# daemon-chroot-acl and proxy-response-line-too-long skip because
# the default (secure) transport opens no listening socket.
run: RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check
- name: check (TCP daemon transport)
# Second run exercising the real loopback-TCP daemon path.
run: ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: ssl file list
run: ./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: almalinux-8-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+124
View File
@@ -0,0 +1,124 @@
name: Build static rsync for Android
# Cross-compiles statically-linked rsync binaries with the Android NDK,
# suitable for dropping onto a phone (adb push / Termux) with no shared
# libraries. arm64-v8a covers all modern phones; armeabi-v7a covers older
# 32-bit devices. The binaries are uploaded as workflow artifacts.
#
# These are cross-compiled, so the test suite can't run here; we sanity
# check that each binary is the right architecture, is static, and that
# it executes (`--version`) under qemu-user.
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/android-static-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/android-static-build.yml'
schedule:
- cron: '42 8 * * 1'
workflow_dispatch:
env:
# Minimum supported API level. 24 (Android 7.0) runs on every modern
# phone while keeping broad reach; bump if you need newer Bionic APIs.
ANDROID_API: 24
jobs:
build:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: ${{ matrix.abi }}
strategy:
fail-fast: false
matrix:
include:
- abi: arm64-v8a # modern phones
triple: aarch64-linux-android
qemu: qemu-aarch64-static
- abi: armeabi-v7a # older 32-bit phones
triple: armv7a-linux-androideabi
qemu: qemu-arm-static
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install build prerequisites
run: sudo apt-get update && sudo apt-get install -y autoconf automake gawk qemu-user-static
- name: Configure and build (${{ matrix.abi }})
shell: bash
run: |
set -euo pipefail
NDK="${ANDROID_NDK_LATEST_HOME:-$ANDROID_NDK_ROOT}"
TC="$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin"
export CC="$TC/${{ matrix.triple }}${ANDROID_API}-clang"
export AR="$TC/llvm-ar" RANLIB="$TC/llvm-ranlib" STRIP="$TC/llvm-strip"
export CFLAGS="-O2" LDFLAGS="-static"
# Bionic doesn't declare lchmod()/lutimes() until API 36, but the
# symbols link, so configure mis-detects them -- force them off so
# rsync uses its fallbacks. The other cache vars restore values
# that configure can't probe when cross-compiling (Android runs a
# normal Linux kernel, so these match the native Linux result).
export ac_cv_func_lchmod=no ac_cv_func_lutimes=no \
rsync_cv_HAVE_SOCKETPAIR=yes \
rsync_cv_MKNOD_CREATES_FIFOS=yes \
rsync_cv_MKNOD_CREATES_SOCKETS=yes
# Self-contained build: drop optional external libraries so the
# static binary needs nothing at runtime. rsync keeps md5/md4
# checksums and its bundled zlib.
./configure --host=${{ matrix.triple }} --build=x86_64-pc-linux-gnu \
--enable-ipv6 \
--disable-zstd --disable-lz4 --disable-xxhash --disable-openssl \
--disable-iconv --disable-iconv-open \
--disable-acl-support --disable-xattr-support \
--disable-md2man --disable-roll-simd \
--with-included-popt --with-included-zlib
# Generate the awk-built headers serially first so the parallel
# build can't race on proto.h <- daemon-parm.h.
make proto.h
make -j"$(nproc)" rsync
"$STRIP" rsync
- name: Verify binary
shell: bash
run: |
set -euo pipefail
file rsync
# Gate: must be a statically-linked executable (no interpreter).
file rsync | grep -q "statically linked"
if file rsync | grep -q "dynamically linked"; then
echo "ERROR: binary is not static" >&2; exit 1
fi
# Best-effort: confirm it actually runs under qemu-user.
${{ matrix.qemu }} ./rsync --version | head -3 || \
echo "WARNING: qemu smoke test did not run cleanly (check on a real device)"
- name: Package
shell: bash
run: |
set -euo pipefail
VER=$(sed -n 's/.*RSYNC_VERSION "\([^"]*\)".*/\1/p' version.h)
out="rsync-${VER}-android-${{ matrix.abi }}"
mkdir -p dist
cp rsync "dist/$out"
( cd dist && sha256sum "$out" > "$out.sha256" )
echo "ARTIFACT_NAME=rsync-android-${{ matrix.abi }}" >>"$GITHUB_ENV"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: ${{ env.ARTIFACT_NAME }}
path: dist/
+75
View File
@@ -0,0 +1,75 @@
name: rsync ASan+UBSan (clang)
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/asan-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/asan-build.yml'
schedule:
# Weekly (Mon 09:42 UTC): catch breakage from a moving ubuntu-latest/clang
# toolchain (a new clang can add a UBSan check, or change ASan behaviour)
# that no code push would otherwise trigger. Push/PR already gate every
# code change, so daily would just re-run an unchanged tree.
- cron: '42 9 * * 1'
workflow_dispatch:
jobs:
asan:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: rsync ASan+UBSan (clang)
env:
# rsync intentionally leaks small allocations at process exit, so leak
# detection would be all noise; chase only memory-safety errors.
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1
# UBSan is a gate: -fno-sanitize-recover=undefined (below) aborts on the
# first finding and halt_on_error=1 makes that fatal, so any undefined
# behaviour fails the run. This needs the tree to be UBSan-clean: the
# remaining findings are fixed in code (hashtable/mdfour shifts, xattrs,
# and log.c's file_struct, kept aligned via rounding.h); only byteorder.h's
# intentional unaligned accessors are suppressed, with no_sanitize.
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y clang acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev openssl
echo "/usr/local/bin" >>"$GITHUB_PATH"
- name: configure
# -DNDEBUG builds as a shipped release does (assert() compiled out), so
# AddressSanitizer catches the over-reads/over-writes that an "assert()
# instead of a real bounds check" bug would cause in a production build.
# UBSan rides along on the same build; -fno-sanitize-recover=undefined
# makes any undefined behaviour abort (and thus fail the run) instead of
# merely printing it.
run: |
CC=clang \
CFLAGS="-fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer -g -O1 -DNDEBUG" \
LDFLAGS="-fsanitize=address,undefined" \
./configure --with-rrsync --disable-md2man --enable-strict-confinement
- name: make
# check-progs builds rsync plus the test helper programs (tls, trimslash,
# t_unsafe, ...) that runtests.py requires; plain "make" builds only rsync
# and runtests aborts on the missing helpers.
run: make check-progs
- name: info
run: ./rsync --version
- name: check (stdio-pipe transport)
# ASan+UBSan-instrumented coverage of the transfer, daemon, sender,
# receiver and metadata paths over the default stdio-pipe transport.
run: ./runtests.py --rsync-bin="$PWD/rsync" -j8
- name: check (TCP daemon transport)
# --use-tcp also exercises the loopback rsyncd listener and the client's
# TCP connection path.
run: ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j8
+75
View File
@@ -0,0 +1,75 @@
name: Coverage (Ubuntu)
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/coverage.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/coverage.yml'
schedule:
- cron: '42 9 * * 1'
workflow_dispatch:
jobs:
coverage:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: gcov coverage
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl gcovr
echo "/usr/local/bin" >>"$GITHUB_PATH"
- name: configure
run: ./configure --enable-coverage --with-rrsync
- name: make
run: make
- name: info
run: rsync --version
# Two coverage runs: the default pipe transport, then a second pass over a
# real loopback rsyncd (--use-tcp) which also exercises the require_tcp-only
# tests. gcovr's --print-summary line/branch/decision totals go to the step
# log (and the job summary below), so the numbers are visible in CI.
# `make coverage` exits with the suite's status, so a regression fails CI.
- name: coverage (pipe transport)
run: |
set -o pipefail
sudo make coverage 2>&1 | tee cov-pipe.log
- name: coverage (TCP transport)
run: |
set -o pipefail
sudo make coverage-tcp 2>&1 | tee cov-tcp.log
- name: coverage summary
if: always()
run: |
{
echo "## gcov coverage"
echo "### Pipe transport (\`make coverage\`)"
echo '```'
grep -E '^(lines|functions|branches|decisions):' cov-pipe.log || echo '(no summary -- see step log)'
echo '```'
echo "### TCP transport (\`make coverage-tcp\`)"
echo '```'
grep -E '^(lines|functions|branches|decisions):' cov-tcp.log || echo '(no summary -- see step log)'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: upload HTML reports
if: always()
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: coverage-html
path: |
coverage
coverage-tcp
+69
View File
@@ -0,0 +1,69 @@
name: Test rsync on Cygwin
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/cygwin-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/cygwin-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: windows-2022
name: Test rsync on Cygwin
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: cygwin
run: choco install -y --no-progress cygwin cyg-get
- name: prep
run: |
cyg-get make autoconf automake gcc-core attr libattr-devel python39 python39-pip libzstd-devel liblz4-devel libssl-devel libxxhash0 libxxhash-devel
echo "C:/tools/cygwin/bin" >>$Env:GITHUB_PATH
- name: commonmark
run: bash -c 'python3 -mpip install --user commonmark'
- name: configure
run: bash -c './configure --with-rrsync'
- name: make
run: bash -c 'make'
- name: install
run: bash -c 'make install'
- name: info
run: bash -c '/usr/local/bin/rsync --version'
- name: check
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on Cygwin
# (rsyncfns.py drives xattrs via getfattr/setfattr from the `attr`
# package installed above), verified on a real Cygwin host. The real
# chown/devices tests still skip (need root/mknod), as do the
# RESOLVE_BENEATH symlink-race tests.
run: bash -c 'RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/cygwin.txt make check'
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd; the default
# 'make check' above uses the secure stdio-pipe transport.
run: bash -c './runtests.py --rsync-bin=`pwd`/rsync.exe --use-tcp -j 8'
- name: ssl file list
run: bash -c 'PATH="/usr/local/bin:$PATH" rsync-ssl --no-motd download.samba.org::rsyncftp/ || true'
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: cygwin-bin
path: |
rsync.exe
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+73
View File
@@ -0,0 +1,73 @@
name: Test fleettest harness
# Bitrot check for testsuite/fleettest.py (the developer fleet CI harness).
# fleettest is meant to be run by developers on a modern Ubuntu box, so this
# job runs only on ubuntu-latest: it stands up a one-host "fleet" of two
# targets that both ssh to localhost and runs a real fleettest pass against it.
# It does not run on the BSD/Solaris/macOS/Cygwin matrix.
on:
push:
branches: [ master ]
paths:
- 'testsuite/fleettest.py'
- '.github/workflows/fleettest.yml'
- 'runtests.py'
- 'testsuite/skiplist/**'
- 'testsuite/skiplist-spec_test.py'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths:
- 'testsuite/fleettest.py'
- '.github/workflows/fleettest.yml'
- 'runtests.py'
- 'testsuite/skiplist/**'
- 'testsuite/skiplist-spec_test.py'
workflow_dispatch:
schedule:
- cron: '17 7 * * 1'
jobs:
fleettest:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: fleettest against localhost
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y gcc g++ gawk autoconf automake \
acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev \
python3-cmarkgfm openssl rsync openssh-server
- name: set up ssh to localhost
run: |
mkdir -p ~/.ssh && chmod 700 ~/.ssh
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
sudo systemctl start ssh || sudo service ssh start
# fleettest connects with `ssh -o BatchMode=yes localhost`, which won't
# answer a host-key prompt -- so pre-trust localhost in known_hosts.
ssh-keyscan -H localhost 127.0.0.1 >> ~/.ssh/known_hosts 2>/dev/null
ssh -o BatchMode=yes -o ConnectTimeout=15 localhost 'echo ssh-to-localhost-ok'
- name: write localhost fleet config
run: |
cat > fleettest-ci.json <<'EOF'
{ "targets": [
{ "name": "local-a", "ssh_host": "localhost", "workflow": "none.yml",
"configure_flags": [], "builddir": "rsync-citest-a", "privilege": "sudo" },
{ "name": "local-b", "ssh_host": "localhost", "workflow": "none.yml",
"configure_flags": [], "builddir": "rsync-citest-b", "privilege": "sudo" }
] }
EOF
- name: fleettest --list (config sanity)
run: python3 testsuite/fleettest.py --fleet fleettest-ci.json --list
- name: run fleettest against localhost
# Two targets both on localhost exercise the parallel multi-target path
# and the per-run dir / port isolation; exit 0 iff every cell is OK.
run: python3 testsuite/fleettest.py --fleet fleettest-ci.json --timing
+55
View File
@@ -0,0 +1,55 @@
name: Test rsync on FreeBSD
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/freebsd-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/freebsd-build.yml'
schedule:
- cron: '42 8 * * 1'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: Test rsync on FreeBSD
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test in FreeBSD VM
id: test
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y bash autotools m4 devel/xxhash zstd liblz4 python3 archivers/liblz4 git
run: |
freebsd-version
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
make
./rsync --version
make check
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: freebsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+69
View File
@@ -0,0 +1,69 @@
name: Test rsync on macOS
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/macos-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/macos-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: macos-latest
name: Test rsync on macOS
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
brew install automake openssl xxhash zstd lz4
pip3 install --user --break-system-packages commonmark
echo "$(brew --prefix)/bin" >>"$GITHUB_PATH"
- name: configure
run: |
BREW_PREFIX=$(brew --prefix)
OPENSSL_PREFIX=$(brew --prefix openssl)
CPPFLAGS="-I${BREW_PREFIX}/include -I${OPENSSL_PREFIX}/include" \
LDFLAGS="-L${BREW_PREFIX}/lib -L${OPENSSL_PREFIX}/lib" \
./configure --with-rrsync
- name: make
run: make
- name: install
run: sudo make install
- name: info
run: rsync --version
- name: check
# chown-fake / devices-fake / xattrs / xattrs-hlink now RUN on macOS
# (rsyncfns.py drives xattrs via the `xattr` command), verified on a
# real macOS host, so they're no longer in the skip set.
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/macos.txt make check
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd; the default
# 'make check' above uses the secure stdio-pipe transport.
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: macos-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+56
View File
@@ -0,0 +1,56 @@
name: Test rsync on NetBSD
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/netbsd-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/netbsd-build.yml'
schedule:
- cron: '42 8 * * 1'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: Test rsync on NetBSD
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test in NetBSD VM
id: test
uses: vmactions/netbsd-vm@v1
with:
usesh: true
prepare: |
PATH=/usr/sbin:$PATH pkg_add autoconf automake python312
ln -sf /usr/pkg/bin/python3.12 /usr/pkg/bin/python3
run: |
uname -a
./configure --with-rrsync --disable-zstd --disable-md2man --disable-xxhash --disable-lz4
make
./rsync --version
make check
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: netbsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+72
View File
@@ -0,0 +1,72 @@
name: Test rsync on OpenBSD
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/openbsd-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/openbsd-build.yml'
schedule:
- cron: '42 8 * * 1'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
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
# The flipper (symlink-race) tests are excluded on OpenBSD, as on the
# fleet's OpenBSD box: this kernel has a connect()-under-rename-load
# lost-wakeup and an FFS rename-storm corruption that hang them to
# the 300s timeout for non-rsync reasons (see
# dev-notes/openbsd-connect-lost-wakeup-report.txt); the protections
# they exercise are verified on the Linux/BSD boxes.
export RSYNC_EXCLUDE=acl-symlink-race,sender-readlink-atfd,sender-remove-source-secure
make check
# The --use-tcp daemon tests run at -j2 here (vs -j8 elsewhere): this
# job runs inside a nested VM, and at -j8 the many concurrent loopback
# daemons occasionally lose a connection-handshake timing race under
# that resource pressure, hanging one test to the 300s timeout. It is
# an environment artifact, not an rsync bug (the handshake is
# deadlock-free and unreproducible elsewhere, even pinned to 1 CPU at
# -j8); -j2 keeps the VM from over-subscribing. The pipe `make check`
# above stays at the default parallelism.
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 2
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: openbsd-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+94
View File
@@ -0,0 +1,94 @@
name: rsync scan-build (clang analyzer)
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/scan-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/scan-build.yml'
workflow_dispatch:
jobs:
# GATING run: pinned clang-18 on a pinned runner so the checker set -- and
# thus the expected zero -- is deterministic. The tree is kept clean for
# clang-18, so --status-bugs (non-zero exit on any report) fails the build
# when a new finding appears. Pin both the analyzer (clang-18/clang-tools-18)
# and the runner (ubuntu-24.04, whose apt repos carry those packages).
gate-clang18:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-24.04
name: scan-build gate (clang-18, pinned)
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y clang-18 clang-tools-18 acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev libpopt-dev openssl
- name: configure (under scan-build)
# Run configure under scan-build so its analyzer compiler-wrapper is baked
# into the Makefile's $(CC); --disable-md2man avoids the doc toolchain.
run: scan-build-18 ./configure --with-rrsync --disable-md2man
- name: scan-build (gating)
# --status-bugs makes scan-build exit non-zero if it finds ANY report.
# pipefail + 'exit $status' propagate that through the tee so the job goes
# red while still printing the summary; the report uploads for triage.
run: |
set -o pipefail
status=0
scan-build-18 --status-bugs -o "$PWD/scan-report" make check-progs -j"$(nproc)" 2>&1 | tee scan-build.out || status=$?
echo '## scan-build gate (clang-18)' >>"$GITHUB_STEP_SUMMARY"
grep -E 'scan-build: .* bugs? found|scan-build: No bugs found' scan-build.out >>"$GITHUB_STEP_SUMMARY" || true
exit $status
- name: upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: scan-build-report-clang18
path: scan-report
if-no-files-found: ignore
# INFORMATIONAL run: whatever clang ubuntu-latest currently ships. Newer
# clang releases enable extra, FP-heavy checkers (e.g. unix.Chroot
# "no chdir after chroot", alpha.unix.Stream) that the gate deliberately
# avoids, so this is NOT a gate (no --status-bugs). It surfaces what the
# newest analyzer sees -- useful for spotting genuine new findings before a
# gate bump -- without blocking merges. continue-on-error keeps a noisy or
# broken run from affecting the workflow's required status.
informational-latest:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: scan-build (latest clang, informational)
continue-on-error: true
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y clang clang-tools acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev libpopt-dev openssl
- name: configure (under scan-build)
run: scan-build ./configure --with-rrsync --disable-md2man
- name: scan-build (informational)
run: |
scan-build -o "$PWD/scan-report" make check-progs -j"$(nproc)" 2>&1 | tee scan-build.out
echo '## scan-build informational (latest clang)' >>"$GITHUB_STEP_SUMMARY"
grep -E 'scan-build: .* bugs? found|scan-build: No bugs found' scan-build.out >>"$GITHUB_STEP_SUMMARY" || true
- name: upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: scan-build-report-latest
path: scan-report
if-no-files-found: ignore
+55
View File
@@ -0,0 +1,55 @@
name: Test rsync on Solaris
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/solaris-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/solaris-build.yml'
schedule:
- cron: '42 8 * * 1'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: Test rsync on Solaris
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test in Solaris VM
id: test
uses: vmactions/solaris-vm@v1
with:
usesh: true
prepare: |
pkg install bash automake gnu-m4 pkg://solaris/runtime/python-35 autoconf gcc git
run: |
uname -a
./configure --with-rrsync -disable-zstd --disable-md2man --disable-xxhash --disable-lz4
make
./rsync --version
make check
./runtests.py --rsync-bin=`pwd`/rsync --use-tcp -j 8
./rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: solaris-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+68
View File
@@ -0,0 +1,68 @@
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:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-22.04-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
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=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check
- name: check30
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check30
- name: check29
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt,@testsuite/skiplist/proto29.txt make check29
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd; the default
# 'make check' above uses the secure stdio-pipe transport.
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: ubuntu-22.04-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+82
View File
@@ -0,0 +1,82 @@
name: Test rsync on Ubuntu
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-build.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-build.yml'
schedule:
- cron: '42 8 * * *'
jobs:
test:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
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=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check
- name: check30
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check30
- name: check29
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt,@testsuite/skiplist/proto29.txt make check29
- name: check (TCP daemon transport)
# Second run with daemon tests over a real loopback rsyncd. The default
# 'make check' above uses the secure stdio-pipe transport (no listening
# sockets); this run exercises the real TCP accept/auth path. Skip-set
# is env-dependent here (chroot-acl), so leave RSYNC_EXPECT_SKIPPED unset.
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
- name: check (non-root, targeted)
# Every run above is root (sudo), so privilege-sensitive tests never hit
# their non-root path. Run those here as the unprivileged 'runner' user
# (NO sudo). Explicit test names make runtests.py full_run False, so
# RSYNC_EXPECT_SKIPPED is bypassed -- no per-platform skip list needed.
# daemon-namecvt-empty-response -- REQUIRES non-root (skips as root by
# design); the only test with no other CI coverage (Benjamin #2).
# ownership-depth -- non-root takes the group-only remap path.
# daemon -- non-root takes the default-config path.
# CONVENTION: a new test that requires/meaningfully exercises a non-root
# path must be added to the list below (kept in sync with the fleet
# harness's nonroot_tests).
run: |
sudo rm -rf testtmp # prior root steps left it root-owned
./runtests.py --rsync-bin="$PWD/rsync" \
daemon-namecvt-empty-response ownership-depth daemon
- name: ssl file list
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
- name: save artifact
uses: actions/upload-artifact@v4
with:
retention-days: 45
name: ubuntu-bin
path: |
rsync
rsync-ssl
rsync.1
rsync-ssl.1
rsyncd.conf.5
rrsync.1
rrsync
+80
View File
@@ -0,0 +1,80 @@
name: Test rsync version mixing on Ubuntu
# Runs the CURRENT test suite with two different rsync binaries: the freshly
# built ./rsync as the client/driver, and a committed OLD static binary
# (old_versions/rsync_<ver>) as the daemon / remote-shell peer. This exercises
# real version mixing over the wire -- more convincing than --protocol forcing,
# which only makes the current binary speak an old protocol.
#
# Direction is fixed: the current binary always drives (only it understands the
# new test scripts); the old binary is only ever the server/daemon side. The
# reverse (old client driving new scripts) is not possible -- but one test,
# reverse-daemon-delta, swaps the roles internally (current build as the daemon,
# old binary as the client) to cover the backward-compat direction: a current
# daemon serving the installed base of old clients.
#
# The per-version manifest testsuite/expect/rsync_<ver>.expect lists exactly
# which tests run and each one's expected outcome (pass/skip/fail/xfail), so an
# old peer's known feature gaps are recorded rather than treated as breakage.
#
# All peers run in a SINGLE job (looped, not a matrix) so the PR shows one check
# line rather than one per version. Each peer/transport is a foldable ::group::
# in the log, and a failure annotates which peer/transport broke.
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-version-mix.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/ubuntu-version-mix.yml'
schedule:
- cron: '52 8 * * 1'
jobs:
version-mix:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
name: rsync version-mix
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
echo "/usr/local/bin" >>"$GITHUB_PATH"
- name: configure
run: ./configure --with-rrsync
- name: make
# check-progs builds rsync AND the test helper programs (tls, trimslash,
# t_unsafe, ...) that runtests.py requires; plain `make` does not.
run: make check-progs
- name: info
run: ./rsync --version | head -1
- name: version mixing (all peers, pipe + TCP transports)
run: |
rc=0
for peer in old_versions/rsync_*; do
chmod +x "$peer"
name=$(basename "$peer")
expect="testsuite/expect/$name.expect"
for transport in pipe tcp; do
tcp=()
[ "$transport" = tcp ] && tcp=(--use-tcp)
echo "::group::$name ($transport): $("$peer" --version | head -1)"
if ! ./runtests.py --rsync-bin="$PWD/rsync" --rsync-bin2="$PWD/$peer" \
--expect-result "$expect" "${tcp[@]}" -j 8; then
echo "::error::version-mix failed: $name ($transport)"
rc=1
fi
echo "::endgroup::"
done
done
exit $rc
+99
View File
@@ -0,0 +1,99 @@
name: Valgrind memcheck
on:
push:
branches: [ master ]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/valgrind.yml'
pull_request:
types: [opened, synchronize, reopened, labeled]
paths-ignore:
- '.github/workflows/*.yml'
- '!.github/workflows/valgrind.yml'
schedule:
- cron: '17 4 * * *'
workflow_dispatch:
jobs:
memcheck:
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
# minutes; labels need triage access, so fork PRs can't self-enable.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
runs-on: ubuntu-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
privilege: [ user, root ]
transport: [ pipe, tcp ]
name: memcheck (${{ matrix.privilege }}, ${{ matrix.transport }})
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: prep
run: |
sudo apt-get update
sudo apt-get install -y valgrind 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 --enable-debug
- name: make
run: make check-progs # builds rsync + the test helper programs runtests.py needs
- name: info
run: ./rsync --version
# Run the whole suite under valgrind. We gate on memory *errors* (uninit
# reads, invalid read/write, bad frees, uninit syscall params), not leaks:
# rsync deliberately leaves file-list/socket/option memory unfreed at exit
# (short-lived process; the OS reclaims), so --leak-check=no avoids a sea of
# by-design "definitely lost" reports. Functional pass/fail is covered by
# the other workflows, so the suite is allowed to finish regardless of
# per-test results; the scan step below is the gate. --error-exitcode=0
# keeps valgrind from perturbing test exit codes; the bundled
# testsuite/valgrind.supp silences known-benign reports.
- name: run testsuite under valgrind
run: |
SUDO=
[ "${{ matrix.privilege }}" = root ] && SUDO="sudo -E"
TCP=
[ "${{ matrix.transport }}" = tcp ] && TCP="--use-tcp"
$SUDO ./runtests.py --valgrind \
--valgrind-opts="--leak-check=no --error-exitcode=0" \
$TCP -j8 --preserve-scratch || true
- name: scan for unsuppressed valgrind errors
run: |
sudo chown -R "$USER" testtmp 2>/dev/null || true
mapfile -t logs < <(find testtmp -name 'valgrind.*.log' 2>/dev/null)
if [ "${#logs[@]}" -eq 0 ]; then
echo "::error::no valgrind logs were produced -- the suite did not run"
exit 1
fi
echo "scanned ${#logs[@]} valgrind log(s)"
bad=()
for f in "${logs[@]}"; do
grep -qE 'ERROR SUMMARY: [1-9][0-9]* errors' "$f" && bad+=("$f")
done
if [ "${#bad[@]}" -ne 0 ]; then
echo "::error::valgrind reported unsuppressed errors in ${#bad[@]} run(s)"
for f in "${bad[@]}"; do
echo "===== $f ====="
sed 's/==[0-9]*== //' "$f" | grep -A18 \
-E 'depends on uninitialised|points to uninitialised|Invalid (read|write|free)|lost in loss record|Mismatched free' \
| head -60
done
exit 1
fi
echo "valgrind clean: no unsuppressed errors"
- name: upload valgrind logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: valgrind-logs-${{ matrix.privilege }}-${{ matrix.transport }}
path: testtmp/**/valgrind.*.log
if-no-files-found: ignore
retention-days: 7
+39 -2
View File
@@ -3,35 +3,72 @@
dummy
ID
Makefile
Makefile.old
configure.sh
configure.sh.old
config.cache
config.h
config.h.in
config.h.in.old
config.log
config.status
aclocal.m4
/proto.h
/proto.h-tstamp
/rsync.1
/rsyncd.conf.5
/rsync*.[15]
/rrsync
/rrsync*.1
/rsync*.html
/rrsync*.html
/help-rsync*.h
/default-cvsignore.h
/default-dont-compress.h
/daemon-parm.h
/.md2man-works
/autom4te*.cache
/confdefs.h
/conftest*
/dox
/getgroups
/gists
/gmon.out
/rsync
/stunnel-rsyncd.conf
/shconfig
/git-version.h
/testdir
/tests-dont-exist
/testtmp
/tls
/testrun
/trimslash
/t_unsafe
/t_acl
/t_chmod_secure
/t_rename_secure
/t_secure_relpath
/t_symlink_secure
/t_hashtable_overflow
/t_iwildmatch
/t_clean_fname
/t_safe_arg
/simdtest
/wildtest
/getfsdev
/t_safe_arg_main
/rounding.h
/doc/rsync.pdf
/doc/rsync.ps
/support/savetransfer
/testsuite/chown-fake.test
/testsuite/devices-fake.test
/testsuite/xattrs-hlink.test
/testsuite/fleettest.json
/fleettest-logs
/patches
/patches.gen
/build
/auto-build-save
.deps
/*.exe
*.dSYM/
+13 -4
View File
@@ -1,7 +1,16 @@
REGARDING OPENSSL AND XXHASH
In addition, as a special exception, the copyright holders give
permission to dynamically link rsync with the OpenSSL and xxhash
libraries when those libraries are being distributed in compliance
with their license terms, and to distribute a dynamically linked
combination of rsync and these libraries. This is also considered
to be covered under the GPL's System Libraries exception.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -645,7 +654,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@@ -664,11 +673,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.
-63
View File
@@ -1,63 +0,0 @@
To build and install rsync:
$ ./configure
$ make
# make install
You may set the installation directory and other parameters by options
to ./configure. To see them, use:
$ ./configure --help
Configure tries to figure out if the local system uses group "nobody" or
"nogroup" by looking in the /etc/group file. (This is only used for the
default group of an rsync daemon, which attempts to run with "nobody"
user and group permissions.) You can change the default user and group
for the daemon by editing the NOBODY_USER and NOBODY_GROUP defines in
config.h, or just override them in your /etc/rsyncd.conf file.
As of 2.4.7, rsync uses Eric Troan's popt option-parsing library. A
cut-down copy of release 1.6.4 is included in the rsync distribution,
and will be used if there is no popt library on your build host, or if
the --with-included-popt option is passed to ./configure.
If you configure using --enable-maintainer-mode, then rsync will try
to pop up an xterm on DISPLAY=:0 if it crashes. You might find this
useful, but it should be turned off for production builds.
RPM NOTES
---------
Under packaging you will find .spec files for several distributions.
The .spec file in packaging/lsb can be used for Linux systems that
adhere to the Linux Standards Base (e.g., RedHat and others).
HP-UX NOTES
-----------
The HP-UX 10.10 "bundled" C compiler seems not to be able to cope with
ANSI C. You may see this error message in config.log if ./configure
fails:
(Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
Install gcc or HP's "ANSI/C Compiler".
MAC OSX NOTES
-------------
Some versions of Mac OS X (Darwin) seem to have an IPv6 stack, but do
not completely implement the "New Sockets" API.
<http://www.ipv6.org/impl/mac.html> says that Apple started to support
IPv6 in 10.2 (Jaguar). If your build fails, try again after running
configure with --disable-ipv6.
IBM AIX NOTES
-------------
IBM AIX has a largefile problem with mkstemp. See IBM PR-51921.
The workaround is to append the below to config.h
#ifdef _LARGE_FILES
#undef HAVE_SECURE_MKSTEMP
#endif
+275
View File
@@ -0,0 +1,275 @@
# How to build and install rsync
When building rsync, you'll want to install various libraries in order to get
all the features enabled. The configure script will alert you when the
newest libraries are missing and tell you the appropriate `--disable-LIB`
option to use if you want to just skip that feature. What follows are various
support libraries that you may want to install to build rsync with the maximum
features (the impatient can skip down to the package summary):
## Ubuntu users: skip the build, use the PPA
If you are on a currently supported Ubuntu series (jammy 22.04 LTS, noble
24.04 LTS, questing 25.10, resolute 26.04 LTS) and just want the latest
upstream rsync, the rsync project maintains a Launchpad PPA that tracks
stable releases:
> sudo add-apt-repository ppa:rsyncproject/rsync
> sudo apt update && sudo apt install rsync
See [the PPA page][ppa] for current build status across architectures.
[ppa]: https://launchpad.net/~rsyncproject/+archive/ubuntu/rsync
To test the upcoming release instead, there is also a [`rsync-latest`
PPA][ppa-latest] that is rebuilt from the tip of the git master branch. These
are development snapshots whose version numbers (such as
`3.5.0~git20260601...`) deliberately sort below the matching stable release, so
the stable PPA above will never silently move you from a release onto a
snapshot. Use it for testing only -- it may contain unreleased changes:
> sudo add-apt-repository ppa:rsyncproject/rsync-latest
> sudo apt update && sudo apt install rsync
[ppa-latest]: https://launchpad.net/~rsyncproject/+archive/ubuntu/rsync-latest
The rest of this document covers building from source.
## The basic setup
You need to have a C compiler installed and optionally a C++ compiler in order
to try to build some hardware-accelerated checksum routines. Rsync also needs
a modern awk, which might be provided via gawk or nawk on some OSes.
## Autoconf & manpages
If you're installing from the git repo (instead of a release tar file) you'll
also need the GNU autotools (autoconf & automake) and your choice of 2 python3
markdown libraries: cmarkgfm or commonmark (needed to generate the manpages).
If your OS doesn't provide a python3-cmarkgfm or python3-commonmark package,
you can run the following to install the commonmark python library for your
build user (after installing python3's pip package):
> python3 -mpip install --user commonmark
You can test if you've got it fixed by running (from the rsync checkout):
> ./md-convert --test rsync-ssl.1.md
Alternately, you can avoid generating the manpages by fetching the very latest
versions (that match the latest git source) from the [generated-files][6] dir.
One way to do that is to run:
> ./prepare-source fetchgen
[6]: https://download.samba.org/pub/rsync/generated-files/
## ACL support
To support copying ACL file information, make sure you have an acl
development library installed. It also helps to have the helper programs
installed to manipulate ACLs and to run the rsync testsuite.
## Xattr support
To support copying xattr file information, make sure you have an attr
development library installed. It also helps to have the helper programs
installed to manipulate xattrs and to run the rsync testsuite.
## xxhash
The [xxHash library][1] provides extremely fast checksum functions that can
make the "rsync algorithm" run much more quickly, especially when matching
blocks in large files. Installing this development library adds xxhash
checksums as the default checksum algorithm. You'll need at least v0.8.0
if you want rsync to include the full range of its checksum algorithms.
[1]: https://cyan4973.github.io/xxHash/
## zstd
The [zstd library][2] compression algorithm that uses less CPU than
the default zlib algorithm at the same compression level. Note that you
need at least version 1.4, so you might need to skip the zstd compression if
you can only install a 1.3 release. Installing this development library
adds zstd compression as the default compression algorithm.
[2]: http://facebook.github.io/zstd/
## lz4
The [lz4 library][3] compression algorithm that uses very little CPU, though
it also has the smallest compression ratio of other algorithms. Installing
this development library adds lz4 compression as an available compression
algorithm.
[3]: https://lz4.github.io/lz4/
## openssl crypto
The [openssl crypto library][4] provides some hardware accelerated checksum
algorithms for MD4 and MD5. Installing this development library makes rsync
use the (potentially) faster checksum routines when computing MD4 & MD5
checksums.
[4]: https://www.openssl.org/docs/man1.0.2/man3/crypto.html
## Package summary
To help you get the libraries installed, here are some package install commands
for various OSes. The commands are split up to correspond with the above
items, but feel free to combine the package names into a single install, if you
like.
- For Debian and Ubuntu (Debian Buster users may want to briefly(?) enable
buster-backports to update zstd from 1.3 to 1.4):
> sudo apt install -y gcc g++ gawk autoconf automake python3-cmarkgfm
> sudo apt install -y acl libacl1-dev
> sudo apt install -y attr libattr1-dev
> sudo apt install -y libxxhash-dev
> sudo apt install -y libzstd-dev
> sudo apt install -y liblz4-dev
> sudo apt install -y libssl-dev
Or run support/install_deps_ubuntu.sh
- For CentOS (use EPEL for python3-pip):
> sudo yum -y install epel-release
> sudo yum -y install gcc g++ gawk autoconf automake python3-pip
> sudo yum -y install acl libacl-devel
> sudo yum -y install attr libattr-devel
> sudo yum -y install xxhash-devel
> sudo yum -y install libzstd-devel
> sudo yum -y install lz4-devel
> sudo yum -y install openssl-devel
> python3 -mpip install --user commonmark
- For Fedora 33:
> sudo dnf -y install acl libacl-devel
> sudo dnf -y install attr libattr-devel
> sudo dnf -y install xxhash-devel
> sudo dnf -y install libzstd-devel
> sudo dnf -y install lz4-devel
> sudo dnf -y install openssl-devel
- For FreeBSD (this assumes that the python3 version is 3.7):
> sudo pkg install -y autotools python3 py37-CommonMark
> sudo pkg install -y xxhash
> sudo pkg install -y zstd
> sudo pkg install -y liblz4
- For macOS:
> brew install automake
> brew install xxhash
> brew install zstd
> brew install lz4
> brew install openssl
- For Cygwin (with all cygwin programs stopped, run the appropriate setup program from a cmd shell):
> setup-x86_64 --quiet-mode -P make,gawk,autoconf,automake,gcc-core,python38,python38-pip
> setup-x86_64 --quiet-mode -P attr,libattr-devel
> setup-x86_64 --quiet-mode -P libzstd-devel
> setup-x86_64 --quiet-mode -P liblz4-devel
> setup-x86_64 --quiet-mode -P libssl-devel
Sometimes cygwin has commonmark packaged and sometimes it doesn't. Now that
its python38 has stabilized, you could install python38-commonmark. Or just
avoid the issue by running this from a bash shell as your build user:
> python3 -mpip install --user commonmark
## Build and install
After installing the various libraries, you need to configure, build, and
install the source:
> ./configure
> make
> sudo make install
The default install path is /usr/local/bin, but you can set the installation
directory and other parameters using options to ./configure. To see them, use:
> ./configure --help
Configure tries to figure out if the local system uses group "nobody" or
"nogroup" by looking in the /etc/group file. (This is only used for the
default group of an rsync daemon, which attempts to run with "nobody"
user and group permissions.) You can change the default user and group
for the daemon by editing the NOBODY_USER and NOBODY_GROUP defines in
config.h, or just override them in your /etc/rsyncd.conf file.
As of 2.4.7, rsync uses Eric Troan's popt option-parsing library. A
cut-down copy of a recent release is included in the rsync distribution,
and will be used if there is no popt library on your build host, or if
the `--with-included-popt` option is passed to ./configure.
If you configure using `--enable-maintainer-mode`, then rsync will try
to pop up an xterm on DISPLAY=:0 if it crashes. You might find this
useful, but it should be turned off for production builds.
If you want to automatically use a separate "build" directory based on
the current git branch name, start with a pristine git checkout and run
"mkdir auto-build-save" before you run the first ./configure command.
That will cause a fresh build dir to spring into existence along with a
special Makefile symlink that allows you to run "make" and "./configure"
from the source dir (the "build" dir gets auto switched based on branch).
This is helpful when using the branch-from-patch and patch-update scripts
to maintain the official rsync patches. If you ever need to build from
a "detached head" git position then you'll need to manually chdir into
the build dir to run make. I also like to create 2 more symlinks in the
source dir: `ln -s build/rsync . ; ln -s build/testtmp .`
## Make compatibility
Note that Makefile.in has a rule that uses a wildcard in a prerequisite. If
your make has a problem with this rule, you will see an error like this:
Don't know how to make ./*.c
You can change the "proto.h-tstamp" target in Makefile.in to list all the \*.c
filenames explicitly in order to avoid this issue.
## RPM notes
Under packaging you will find .spec files for several distributions.
The .spec file in packaging/lsb can be used for Linux systems that
adhere to the Linux Standards Base (e.g., RedHat and others).
## HP-UX notes
The HP-UX 10.10 "bundled" C compiler seems not to be able to cope with
ANSI C. You may see this error message in config.log if ./configure
fails:
(Bundled) cc: "configure", line 2162: error 1705: Function prototypes are an ANSI feature.
Install gcc or HP's "ANSI/C Compiler".
## Mac OS X notes
Some versions of Mac OS X (Darwin) seem to have an IPv6 stack, but do
not completely implement the "New Sockets" API.
[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
IBM AIX has a largefile problem with mkstemp. See IBM PR-51921.
The workaround is to append the following to config.h:
> #ifdef _LARGE_FILES
> #undef HAVE_SECURE_MKSTEMP
> #endif
+447 -93
View File
@@ -1,57 +1,77 @@
# Makefile for rsync. This is processed by configure to produce the final
# Makefile
# The Makefile for rsync (configure creates it from Makefile.in).
prefix=@prefix@
datarootdir=@datarootdir@
exec_prefix=@exec_prefix@
bindir=@bindir@
libdir=@libdir@/rsync
mandir=@mandir@
with_rrsync=@with_rrsync@
LIBS=@LIBS@
CC=@CC@
AWK=@AWK@
CFLAGS=@CFLAGS@
CPPFLAGS=@CPPFLAGS@
CXX=@CXX@
CXXFLAGS=@CXXFLAGS@
EXEEXT=@EXEEXT@
LDFLAGS=@LDFLAGS@
LIBOBJDIR=lib/
AR=@AR@
ARFLAGS=cr
RANLIB=@RANLIB@
INSTALLCMD=@INSTALL@
INSTALLMAN=@INSTALL@
srcdir=@srcdir@
MKDIR_P=@MKDIR_P@
VPATH=$(srcdir)
SHELL=/bin/sh
VERSION=@VERSION@
.SUFFIXES:
.SUFFIXES: .c .o
GENFILES=configure.sh config.h.in proto.h proto.h-tstamp rsync.1 rsyncd.conf.5
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h lib/pool_alloc.h
LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
util.o main.o checksum.o match.o syscall.o log.o backup.o
OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
OBJS3=progress.o pipe.o
DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
popt/popthelp.o popt/poptparse.o
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
ROLL_SIMD_x86_64=simd-checksum-x86_64.o
ROLL_ASM_x86_64=simd-checksum-avx2.o
MD5_ASM_x86_64=lib/md5-asm-x86_64.o
TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
GENFILES=configure.sh aclocal.m4 config.h.in rsync.1 rsync.1.html \
rsync-ssl.1 rsync-ssl.1.html rsyncd.conf.5 rsyncd.conf.5.html \
@GEN_RRSYNC@
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h ifuncs.h itypes.h inums.h \
lib/pool_alloc.h lib/mdigest.h lib/md-defines.h vfs/vfs.h
LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o lib/acl.o @LIBOBJS@
zlib_OBJS=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
OBJS1_NO_MAIN=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
util1.o util2.o checksum.o match.o log.o backup.o delete.o
OBJS1=$(OBJS1_NO_MAIN) main.o
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/popt.o popt/poptconfig.o \
popt/popthelp.o popt/poptparse.o popt/poptint.o
VFS_OBJ=vfs/vfs.o vfs/dirstack.o vfs/secure_open.o vfs/owner_walk.o vfs/dircache.o vfs/stat.o vfs/rename.o vfs/unlink.o vfs/open.o vfs/chmod.o vfs/symlink.o vfs/link.o vfs/mkdir.o vfs/chown.o vfs/mknod.o vfs/times.o vfs/fileio.o vfs/make_path.o vfs/copy_file.o vfs/robust.o
OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@ libvfs.a
TLS_OBJ = tls.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@ libvfs.a
# Programs we must have to run the test cases
CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
trimslash$(EXEEXT) t_unsafe$(EXEEXT) wildtest$(EXEEXT)
testrun$(EXEEXT) trimslash$(EXEEXT) t_unsafe$(EXEEXT) t_chmod_secure$(EXEEXT) \
t_rename_secure$(EXEEXT) t_symlink_secure$(EXEEXT) t_secure_relpath$(EXEEXT) t_acl$(EXEEXT) t_hashtable_overflow$(EXEEXT) t_iwildmatch$(EXEEXT) t_clean_fname$(EXEEXT) t_safe_arg$(EXEEXT) wildtest$(EXEEXT) simdtest$(EXEEXT)
CHECK_SYMLINKS = testsuite/chown-fake.test testsuite/devices-fake.test
CHECK_SYMLINKS = testsuite/chown-fake_test.py testsuite/devices-fake_test.py \
testsuite/xattrs-hlink_test.py testsuite/exclude-lsh_test.py
# Objects for CHECK_PROGS to clean
CHECK_OBJS=tls.o 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_rename_secure.o t_symlink_secure.o t_secure_relpath.o t_acl.o t_hashtable_overflow.o t_iwildmatch.o t_clean_fname.o t_safe_arg.o trimslash.o wildtest.o
# Compile-only feature-shape checks.
CHECK_COMPILE_OBJS=vfs-no-at-fdcwd.o
# note that the -I. is needed to handle config.h when using VPATH
.c.o:
@@ -59,28 +79,110 @@ CHECK_OBJS=tls.o getgroups.o getfsdev.o t_stub.o t_unsafe.o trimslash.o wildtest
$(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) -c $< @CC_SHOBJ_FLAG@
@OBJ_RESTORE@
all: conf_stop make_stop rsync$(EXEEXT) @MAKE_MAN@
# 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
# Compile-check the pre-*at() portability tier. syscall.c's *at wrappers were
# split into vfs/, so compile every vfs source with the AT_FDCWD primitives
# undefined (via vfs/vfs_internal.h's RSYNC_TEST_NO_AT_FDCWD block) and confirm
# the fallback arms still build. A shell loop keeps this portable (BSD/Solaris
# make have no pattern rules); the last object compiled is left as the target.
# $(VFS_OBJ:.o=.c) is POSIX suffix substitution, portable across makes.
vfs-no-at-fdcwd.o: $(VFS_OBJ:.o=.c) $(HEADERS) vfs/vfs.h vfs/vfs_internal.h
@rm -f $@ $@.tmp
@for f in $(VFS_OBJ:.o=.c); do \
echo " no-AT_FDCWD compile-check: $$f"; \
$(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) \
-DRSYNC_TEST_NO_AT_FDCWD -c $(srcdir)/$$f -o $@.tmp || exit 1; \
done
@mv $@.tmp $@
.PHONY: install
install: all
-mkdir -p ${DESTDIR}${bindir}
${INSTALLCMD} ${INSTALL_STRIP} -m 755 rsync$(EXEEXT) ${DESTDIR}${bindir}
-mkdir -p ${DESTDIR}${mandir}/man1
-mkdir -p ${DESTDIR}${mandir}/man5
if test -f rsync.1; then ${INSTALLMAN} -m 644 rsync.1 ${DESTDIR}${mandir}/man1; fi
if test -f rsyncd.conf.5; then ${INSTALLMAN} -m 644 rsyncd.conf.5 ${DESTDIR}${mandir}/man5; fi
-$(MKDIR_P) $(DESTDIR)$(bindir)
$(INSTALLCMD) $(INSTALL_STRIP) -m 755 rsync$(EXEEXT) $(DESTDIR)$(bindir)
$(INSTALLCMD) -m 755 $(srcdir)/rsync-ssl $(DESTDIR)$(bindir)
-$(MKDIR_P) $(DESTDIR)$(mandir)/man1
-$(MKDIR_P) $(DESTDIR)$(mandir)/man5
for fn in rsync.1 rsync-ssl.1; do \
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man1; \
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man1; fi; \
done
for fn in rsyncd.conf.5; do \
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man5; \
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man5; fi; \
done
if test "$(with_rrsync)" = yes; then \
$(INSTALLCMD) -m 755 rrsync $(DESTDIR)$(bindir); \
fn=rrsync.1; \
if test -f $$fn; then $(INSTALLMAN) -m 644 $$fn $(DESTDIR)$(mandir)/man1; \
elif test -f $(srcdir)/$$fn; then $(INSTALLMAN) -m 644 $(srcdir)/$$fn $(DESTDIR)$(mandir)/man1; fi; \
fi
install-ssl-daemon: stunnel-rsyncd.conf
-$(MKDIR_P) $(DESTDIR)/etc/stunnel
$(INSTALLCMD) -m 644 stunnel-rsyncd.conf $(DESTDIR)/etc/stunnel/rsyncd.conf
@if ! ls /etc/rsync-ssl/certs/server.* >/dev/null 2>/dev/null; then \
echo "Note that you'll need to install the certificate used by /etc/stunnel/rsyncd.conf"; \
fi
install-all: install install-ssl-daemon
install-strip:
$(MAKE) INSTALL_STRIP='-s' install
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)$(bindir)/rsync$(EXEEXT) $(DESTDIR)$(bindir)/rsync-ssl
rm -f $(DESTDIR)$(bindir)/rrsync
rm -f $(DESTDIR)$(mandir)/man1/rsync.1 $(DESTDIR)$(mandir)/man1/rsync-ssl.1
rm -f $(DESTDIR)$(mandir)/man1/rrsync.1
rm -f $(DESTDIR)$(mandir)/man5/rsyncd.conf.5
.PHONY: uninstall-ssl-daemon
uninstall-ssl-daemon:
rm -f $(DESTDIR)/etc/stunnel/rsyncd.conf
.PHONY: uninstall-all
uninstall-all: uninstall uninstall-ssl-daemon
rsync$(EXEEXT): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
rrsync: support/rrsync
cp -p $(srcdir)/support/rrsync rrsync
$(OBJS): $(HEADERS)
$(CHECK_OBJS): $(HEADERS)
$(VFS_OBJ): $(HEADERS)
$(VFS_OBJ): vfs/vfs_internal.h
tls.o xattrs.o: lib/sysxattrs.h
# The VFS layer is bundled into a static archive linked last on every target so
# that moving a filesystem family between files never breaks a test harness link
# (the linker pulls only the members each program references).
libvfs.a: $(VFS_OBJ)
rm -f $@
$(AR) $(ARFLAGS) $@ $(VFS_OBJ)
$(RANLIB) $@
usage.o: version.h latest-year.h help-rsync.h help-rsyncd.h git-version.h default-cvsignore.h
loadparm.o: default-dont-compress.h daemon-parm.h
flist.o: rounding.h
log.o: rounding.h
rounding.h: rounding.c rsync.h
default-cvsignore.h default-dont-compress.h: rsync.1.md define-from-md.awk
$(AWK) -f $(srcdir)/define-from-md.awk -v hfile=$@ $(srcdir)/rsync.1.md
help-rsync.h help-rsyncd.h: rsync.1.md help-from-md.awk
$(AWK) -f $(srcdir)/help-from-md.awk -v hfile=$@ $(srcdir)/rsync.1.md
daemon-parm.h: daemon-parm.txt daemon-parm.awk
$(AWK) -f $(srcdir)/daemon-parm.awk $(srcdir)/daemon-parm.txt
rounding.h: rounding.c rsync.h proto.h
@for r in 0 1 3; do \
if $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o rounding -DEXTRA_ROUNDING=$$r -I. $(srcdir)/rounding.c >rounding.out 2>&1; then \
echo "#define EXTRA_ROUNDING $$r" >rounding.h; \
@@ -98,103 +200,212 @@ rounding.h: rounding.c rsync.h
fi
@rm -f rounding.out
git-version.h: ALWAYS_RUN
$(srcdir)/mkgitver
.PHONY: ALWAYS_RUN
ALWAYS_RUN:
simd-checksum-x86_64.o: simd-checksum-x86_64.cpp
@$(srcdir)/cmd-or-msg disable-roll-simd $(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $(srcdir)/simd-checksum-x86_64.cpp
simd-checksum-avx2.o: simd-checksum-avx2.S
@$(srcdir)/cmd-or-msg disable-roll-asm $(CC) $(CFLAGS) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/simd-checksum-avx2.S
lib/md5-asm-x86_64.o: lib/md5-asm-x86_64.S lib/md-defines.h
@$(srcdir)/cmd-or-msg disable-md5-asm $(CC) -I. @NOEXECSTACK@ -c -o $@ $(srcdir)/lib/md5-asm-x86_64.S
tls$(EXEEXT): $(TLS_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TLS_OBJ) $(LIBS)
testrun$(EXEEXT): testrun.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ testrun.o
getgroups$(EXEEXT): getgroups.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getgroups.o $(LIBS)
getfsdev$(EXEEXT): getfsdev.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o
TRIMSLASH_OBJ = trimslash.o util2.o t_stub.o lib/compat.o lib/snprintf.o libvfs.a
trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
T_UNSAFE_OBJ = t_unsafe.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o libvfs.a
t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS)
gen: conf proto.h man
T_HASHTABLE_OVERFLOW_OBJ = t_hashtable_overflow.o hashtable.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o libvfs.a
t_hashtable_overflow$(EXEEXT): $(T_HASHTABLE_OVERFLOW_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_HASHTABLE_OVERFLOW_OBJ) $(LIBS)
gensend: gen
rsync -aivzc $(GENFILES) samba.org:/home/ftp/pub/rsync/generated-files/
T_IWILDMATCH_OBJ = t_iwildmatch.o lib/wildmatch.o
t_iwildmatch$(EXEEXT): $(T_IWILDMATCH_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_IWILDMATCH_OBJ) $(LIBS)
conf:
cd $(srcdir) && $(MAKE) -f prepare-source.mak conf
T_CLEAN_FNAME_OBJ = t_clean_fname.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o libvfs.a
t_clean_fname$(EXEEXT): $(T_CLEAN_FNAME_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_CLEAN_FNAME_OBJ) $(LIBS)
conf_stop: configure.sh config.h.in
# safe_arg lives in options.c alongside the whole option parser. Rather than
# rely on a non-portable linker --gc-sections to drop the parser (GNU ld only;
# macOS ld64 and the cygwin PE linker do not), link the real rsync objects so
# every dep resolves. t_safe_arg_main.o is main.c with main() renamed out, to
# supply main.c's globals while letting t_safe_arg.o provide the test's main().
# OBJS minus main.o is spelled out via OBJS1_NO_MAIN because $(filter-out) is
# GNU-make-only; BSD and Solaris make expand it to nothing.
t_safe_arg_main.o: main.c $(HEADERS)
$(CC) -I. -I$(srcdir) $(CFLAGS) $(CPPFLAGS) -Dmain=rsync_unused_main -c $(srcdir)/main.c -o t_safe_arg_main.o
T_SAFE_ARG_OBJ = t_safe_arg.o t_safe_arg_main.o $(OBJS1_NO_MAIN) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@ libvfs.a
t_safe_arg$(EXEEXT): $(T_SAFE_ARG_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_SAFE_ARG_OBJ) $(LIBS)
configure.sh config.h.in: configure.in aclocal.m4
T_CHMOD_SECURE_OBJ = t_chmod_secure.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o libvfs.a
t_chmod_secure$(EXEEXT): $(T_CHMOD_SECURE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_CHMOD_SECURE_OBJ) $(LIBS)
T_RENAME_SECURE_OBJ = t_rename_secure.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o libvfs.a
t_rename_secure$(EXEEXT): $(T_RENAME_SECURE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_RENAME_SECURE_OBJ) $(LIBS)
T_SYMLINK_SECURE_OBJ = t_symlink_secure.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o libvfs.a
t_symlink_secure$(EXEEXT): $(T_SYMLINK_SECURE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_SYMLINK_SECURE_OBJ) $(LIBS)
T_SECURE_RELPATH_OBJ = t_secure_relpath.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o lib/permstring.o libvfs.a
t_secure_relpath$(EXEEXT): $(T_SECURE_RELPATH_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_SECURE_RELPATH_OBJ) $(LIBS)
# Unit test for lib/acl.c: compares our fd/at ACL ops against the system libacl
# (linked via $(LIBS), which carries -lacl). lib/acl.o references no rsync
# globals, so this links with no stubs. Self-skips (exit 77) when built
# without SUPPORT_ACL_FD.
T_ACL_OBJ = t_acl.o lib/acl.o
t_acl$(EXEEXT): $(T_ACL_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_ACL_OBJ) $(LIBS)
.PHONY: conf
conf: configure.sh config.h.in
.PHONY: gen
gen: conf proto.h man git-version.h
aclocal.m4: $(srcdir)/m4/*.m4
aclocal -I $(srcdir)/m4
configure.sh config.h.in: configure.ac aclocal.m4
@if test -f configure.sh; then cp -p configure.sh configure.sh.old; else touch configure.sh.old; fi
@if test -f config.h.in; then cp -p config.h.in config.h.in.old; else touch config.h.in.old; fi
autoconf -o configure.sh
autoheader && touch config.h.in
@echo 'Configure files changed -- perhaps run:'
@echo ' make reconfigure'
@exit 1
@if diff configure.sh configure.sh.old >/dev/null 2>&1; then \
echo "configure.sh is unchanged."; \
rm configure.sh.old; \
else \
echo "configure.sh has CHANGED."; \
fi
@if diff config.h.in config.h.in.old >/dev/null 2>&1; then \
echo "config.h.in is unchanged."; \
rm config.h.in.old; \
else \
echo "config.h.in has CHANGED."; \
fi
@if test -f configure.sh.old || test -f config.h.in.old; then \
if test "$(MAKECMDGOALS)" = reconfigure; then \
echo 'Continuing with "make reconfigure".'; \
else \
echo 'You may need to run:'; \
echo ' make reconfigure'; \
exit 1; \
fi \
fi
.PHONY: reconfigure
reconfigure: configure.sh
./config.status --recheck
./config.status
make_stop: Makefile
.PHONY: restatus
restatus:
./config.status
Makefile: Makefile.in config.status
Makefile: Makefile.in config.status configure.sh config.h.in
@if test -f Makefile; then cp -p Makefile Makefile.old; else touch Makefile.old; fi
@./config.status
@echo "Makefile updated -- rerun your make command."
@exit 1
@if diff Makefile Makefile.old >/dev/null 2>&1; then \
echo "Makefile is unchanged."; \
rm Makefile.old; \
else \
if test "$(MAKECMDGOALS)" = reconfigure; then \
echo 'Continuing with "make reconfigure".'; \
else \
echo "Makefile updated -- rerun your make command."; \
exit 1; \
fi \
fi
stunnel-rsyncd.conf: $(srcdir)/stunnel-rsyncd.conf.in Makefile
sed 's;\@bindir\@;$(bindir);g' <$(srcdir)/stunnel-rsyncd.conf.in >stunnel-rsyncd.conf
.PHONY: proto
proto: proto.h-tstamp
proto.h: proto.h-tstamp
@if test -f proto.h; then :; else cp -p $(srcdir)/proto.h .; fi
proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c
perl $(srcdir)/mkproto.pl $(srcdir)/*.c $(srcdir)/lib/compat.c
proto.h-tstamp: $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
$(AWK) -f $(srcdir)/mkproto.awk $(srcdir)/*.c $(srcdir)/lib/compat.c daemon-parm.h
man: rsync.1 rsyncd.conf.5
@if test -f rsync.1; then :; else cp -p $(srcdir)/rsync.1 .; fi
@if test -f rsyncd.conf.5; then :; else cp -p $(srcdir)/rsyncd.conf.5 .; fi
.PHONY: man
man: rsync.1 rsync-ssl.1 rsyncd.conf.5 @MAKE_RRSYNC_1@
rsync.1: rsync.yo
yodl2man -o rsync.1 $(srcdir)/rsync.yo
-$(srcdir)/tweak_manpage rsync.1
rsync.1: rsync.1.md md-convert version.h Makefile
@$(srcdir)/maybe-make-man rsync.1.md
rsyncd.conf.5: rsyncd.conf.yo
yodl2man -o rsyncd.conf.5 $(srcdir)/rsyncd.conf.yo
-$(srcdir)/tweak_manpage rsyncd.conf.5
rsync-ssl.1: rsync-ssl.1.md md-convert version.h Makefile
@$(srcdir)/maybe-make-man rsync-ssl.1.md
rsyncd.conf.5: rsyncd.conf.5.md md-convert version.h Makefile
@$(srcdir)/maybe-make-man rsyncd.conf.5.md
rrsync.1: support/rrsync.1.md md-convert Makefile
@$(srcdir)/maybe-make-man support/rrsync.1.md
.PHONY: clean
clean: cleantests
rm -f *~ $(OBJS) $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_SYMLINKS) \
rounding rounding.h
rm -f *~ $(OBJS) $(VFS_OBJ) libvfs.a $(CHECK_PROGS) $(CHECK_OBJS) $(CHECK_COMPILE_OBJS) $(CHECK_COMPILE_OBJS:.o=.o.tmp) $(CHECK_SYMLINKS) @MAKE_RRSYNC@ \
git-version.h rounding rounding.h *.old rsync*.1 rsync*.5 @MAKE_RRSYNC_1@ \
*.html daemon-parm.h help-*.h default-*.h proto.h proto.h-tstamp
rm -f *.gcno *.gcda lib/*.gcno lib/*.gcda zlib/*.gcno zlib/*.gcda popt/*.gcno popt/*.gcda vfs/*.gcno vfs/*.gcda
rm -rf coverage coverage-tcp coverage-all coverage-fallback
.PHONY: cleantests
cleantests:
rm -rf ./testtmp*
# We try to delete built files from both the source and build
# directories, just in case somebody previously configured things in
# the source directory.
.PHONY: distclean
distclean: clean
rm -f Makefile config.h config.status
rm -f lib/dummy popt/dummy zlib/dummy
rm -f $(srcdir)/Makefile $(srcdir)/config.h $(srcdir)/config.status
rm -f $(srcdir)/lib/dummy $(srcdir)/popt/dummy $(srcdir)/zlib/dummy
rm -f config.cache config.log
rm -f $(srcdir)/config.cache $(srcdir)/config.log
rm -f shconfig $(srcdir)/shconfig
rm -f $(GENFILES)
rm -rf autom4te.cache
for dir in $(srcdir) . ; do \
(cd "$$dir" && rm -rf Makefile config.h config.status stunnel-rsyncd.conf \
lib/dummy popt/dummy zlib/dummy config.cache config.log shconfig \
$(GENFILES) autom4te.cache) ; \
done
# this target is really just for my use. It only works on a limited
# range of machines and is used to produce a list of potentially
# dead (ie. unused) functions in the code. (tridge)
.PHONY: finddead
finddead:
nm *.o */*.o |grep 'U ' | awk '{print $$2}' | sort -u > nmused.txt
nm *.o */*.o |grep 'T ' | awk '{print $$3}' | sort -u > nmfns.txt
comm -13 nmused.txt nmfns.txt
@rm nmused.txt nmfns.txt
# 'check' is the GNU name, 'test' is the name for everybody else :-)
.PHONY: check test
.PHONY: test
test: check
# There seems to be no standard way to specify some variables as
@@ -207,28 +418,180 @@ test: check
# catch Bash-isms earlier even if we're running on GNU. Of course, we
# might lose in the future where POSIX diverges from old sh.
check: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh
# `make check` runs tests in parallel by default. Override with
# `make check CHECK_J=1` (serial) or any other value.
CHECK_J = 8
check29: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
rsync_bin=`pwd`/rsync$(EXEEXT) $(srcdir)/runtests.sh --protocol=29
# Parallelism for `make coverage`. Defaults to the same as CHECK_J: the
# coverage build sets -fprofile-update=atomic (atomic in-memory counters) and
# gcc's libgcov serializes the per-source .gcda read-modify-write merge with a
# file lock, so concurrent rsync processes (incl. the forked sender/generator/
# receiver) accumulate exactly -- verified by a count-linearity check (a hot
# line accumulates identically at -j1 and -P16). Override with
# `make coverage COVERAGE_J=1` if your libgcov does not lock .gcda merges.
COVERAGE_J = $(CHECK_J)
wildtest.o: wildtest.c lib/wildmatch.c rsync.h config.h
# Output directory and extra runtests.py flags for `make coverage`. The
# `coverage-tcp` target reuses the coverage recipe with --use-tcp (real
# loopback rsyncd, which exercises the TCP accept/auth path and the
# require_tcp-only tests) and a separate output directory.
COVERAGE_DIR = coverage
COVERAGE_RUNFLAGS =
# Excluded from the coverage report so the percentages reflect rsync's own
# runtime source. Three buckets:
# (1) Bundled third-party code rsync ships but does not own: zlib/, popt/, and
# the named lib/ imports (PostgreSQL getaddrinfo, ISC inet_ntop/inet_pton,
# standalone getpass). The other lib/*.c are rsync's own and stay in.
# (2) Test-helper / build-time programs that link against rsync objects but are
# not the rsync runtime: t_*.c, tls.c, wildtest.c, testrun.c, getgroups.c,
# getfsdev.c, trimslash.c, rounding.c. These have their own main() and are
# either driven directly by a test (counted there) or are configure-time
# probes; counting them as "rsync uncovered" is noise.
# (3) Compile-time-dead fallbacks under this build's config.h: lib/md5.c (the
# reference md5 -- openssl's EVP path is used when HAVE_OPENSSL) and
# lib/snprintf.c (only the #include line survives under
# HAVE_C99_VSNPRINTF). Covering these would mean a separate non-openssl /
# non-C99 build, which is out of scope for this report.
COVERAGE_EXCLUDE = -e '(^|/)zlib/' -e '(^|/)popt/' \
-e '(^|/)lib/(getaddrinfo|getpass|inet_ntop|inet_pton)\.' \
-e '(^|/)(t_[a-z_]+|tls|wildtest|testrun|getgroups|getfsdev|trimslash|rounding)\.c$$' \
-e '(^|/)lib/(md5|snprintf)\.c$$'
# Build everything the test suite needs (rsync + helper programs + symlinks)
# WITHOUT running it. Used by CI jobs that invoke runtests.py directly with
# custom options (e.g. the version-mix workflow's --rsync-bin2/--expect-result).
.PHONY: check-progs
check-progs: all $(CHECK_PROGS) $(CHECK_COMPILE_OBJS) $(CHECK_SYMLINKS)
.PHONY: check
check: all $(CHECK_PROGS) $(CHECK_COMPILE_OBJS) $(CHECK_SYMLINKS)
"$(srcdir)/runtests.py" --rsync-bin="`pwd`/rsync$(EXEEXT)" -j $(CHECK_J)
.PHONY: check29
check29: all $(CHECK_PROGS) $(CHECK_COMPILE_OBJS) $(CHECK_SYMLINKS)
"$(srcdir)/runtests.py" --rsync-bin="`pwd`/rsync$(EXEEXT)" -j $(CHECK_J) --protocol=29
.PHONY: check30
check30: all $(CHECK_PROGS) $(CHECK_COMPILE_OBJS) $(CHECK_SYMLINKS)
"$(srcdir)/runtests.py" --rsync-bin="`pwd`/rsync$(EXEEXT)" -j $(CHECK_J) --protocol=30
# Whole-suite gcov coverage report (HTML, with branch + decision coverage).
# Requires a build configured with --enable-coverage and the `gcovr` tool
# (pip install gcovr). Runs the suite in parallel (COVERAGE_J, default CHECK_J):
# this is safe because the coverage build uses -fprofile-update=atomic and
# libgcov locks the per-source .gcda during its merge, so concurrent rsync
# processes accumulate exactly (see COVERAGE_J above). Use COVERAGE_J=1 if your
# toolchain's libgcov does not lock .gcda merges.
.PHONY: coverage
coverage: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
@case '$(CFLAGS)' in *--coverage*) ;; \
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
find . -name '*.gcda' -delete
@# Daemon modules with `uid = <non-root>` setuid the per-connection child
@# (and so the forked generator/receiver), which then cannot create or
@# merge .gcda files in a root-owned build dir -- silently dropping ALL
@# coverage from those processes. Make every .gcno's directory
@# world-writable so any uid can create the sibling .gcda, and set a
@# default ACL of o::rw so the .gcda are world-mergeable regardless of
@# the creator's umask (every test process resets umask to 022 via
@# rsyncfns.py, so a Makefile-level `umask 0` would not survive).
@find . -name '*.gcno' -printf '%h\n' 2>/dev/null | sort -u | \
while read d; do \
chmod a+rwx "$$d"; \
setfacl -m 'd:u::rwx,d:g::rwx,d:o::rwx' "$$d" 2>/dev/null || true; \
done
@rc=0; "$(srcdir)/runtests.py" --rsync-bin="`pwd`/rsync$(EXEEXT)" -j $(COVERAGE_J) $(COVERAGE_RUNFLAGS) || rc=$$?; \
rm -rf $(COVERAGE_DIR) && mkdir -p $(COVERAGE_DIR); \
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
--gcov-ignore-parse-errors=negative_hits.warn_once_per_file \
--html-details -o $(COVERAGE_DIR)/index.html . || exit $$?; \
echo "Coverage report written to $(COVERAGE_DIR)/index.html"; \
if test $$rc != 0; then \
echo "*** test suite FAILED (status $$rc) -- coverage report still written above"; \
fi; \
exit $$rc
# Same as `make coverage` but with the daemon tests run over a real loopback
# rsyncd (--use-tcp), into a separate report directory.
.PHONY: coverage-tcp
coverage-tcp:
$(MAKE) coverage COVERAGE_RUNFLAGS=--use-tcp COVERAGE_DIR=coverage-tcp
# Comprehensive single report: run the suite under several configurations,
# accumulating into the shared .gcda counters (NOT cleared between runs), then
# emit one merged, rsync-scoped report. Covers the default (pipe) transport, the
# protocol-29/30 compat branches, and the real-TCP daemon path (which also runs
# the require_tcp-only tests). Run under sudo to additionally cover root-only
# paths (devices, chown, use-chroot, protected-regular). Local target -- CI uses
# the plain `coverage`/`coverage-tcp` targets.
.PHONY: coverage-all
coverage-all: all $(CHECK_PROGS) $(CHECK_SYMLINKS)
@case '$(CFLAGS)' in *--coverage*) ;; \
*) echo "*** not a coverage build; reconfigure with --enable-coverage"; exit 1 ;; esac
@command -v gcovr >/dev/null 2>&1 || { echo "*** gcovr not found (pip install gcovr)"; exit 1; }
find . -name '*.gcda' -delete
@# See the `coverage` target above for why: setuid'd daemon children must
@# be able to create/merge .gcda owned by a different uid.
@find . -name '*.gcno' -printf '%h\n' 2>/dev/null | sort -u | \
while read d; do \
chmod a+rwx "$$d"; \
setfacl -m 'd:u::rwx,d:g::rwx,d:o::rwx' "$$d" 2>/dev/null || true; \
done
@rc=0; \
for cfg in '' '--protocol=30' '--protocol=29' '--use-tcp'; do \
echo "===== coverage-all: runtests.py $$cfg ====="; \
"$(srcdir)/runtests.py" --rsync-bin="`pwd`/rsync$(EXEEXT)" -j $(COVERAGE_J) $$cfg || rc=$$?; \
done; \
rm -rf coverage-all && mkdir -p coverage-all; \
gcovr --root $(srcdir) $(COVERAGE_EXCLUDE) --decisions --print-summary \
--gcov-ignore-parse-errors=negative_hits.warn_once_per_file \
--html-details -o coverage-all/index.html . || exit $$?; \
echo "Merged coverage report written to coverage-all/index.html"; \
if test $$rc != 0; then \
echo "*** some suite runs FAILED (status $$rc) -- report still written above"; \
fi; \
exit $$rc
# Coverage for the portable (non-openat2) resolver tier. Requires a SEPARATE
# build configured with --enable-coverage --disable-openat2: its .gcno differ
# from the openat2 build, so this report cannot be merged with the others.
.PHONY: coverage-fallback
coverage-fallback:
$(MAKE) coverage COVERAGE_DIR=coverage-fallback
wildtest.o: wildtest.c t_stub.o lib/wildmatch.c rsync.h config.h
wildtest$(EXEEXT): wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ wildtest.o lib/compat.o lib/snprintf.o @BUILD_POPT@ $(LIBS)
testsuite/chown-fake.test:
ln -s chown.test $(srcdir)/testsuite/chown-fake.test
simdtest$(EXEEXT): simd-checksum-x86_64.cpp $(HEADERS)
@if test x"@ROLL_SIMD@" != x; then \
$(CXX) -I. $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DTEST_SIMD_CHECKSUM1 \
-o $@ $(srcdir)/simd-checksum-x86_64.cpp @ROLL_ASM@ $(LIBS); \
else \
touch $@; \
fi
testsuite/devices-fake.test:
ln -s devices.test $(srcdir)/testsuite/devices-fake.test
testsuite/chown-fake_test.py:
ln -s chown_test.py $(srcdir)/testsuite/chown-fake_test.py
testsuite/devices-fake_test.py:
ln -s devices_test.py $(srcdir)/testsuite/devices-fake_test.py
testsuite/xattrs-hlink_test.py:
ln -s xattrs_test.py $(srcdir)/testsuite/xattrs-hlink_test.py
testsuite/exclude-lsh_test.py:
ln -s exclude_test.py $(srcdir)/testsuite/exclude-lsh_test.py
# This does *not* depend on building or installing: you can use it to
# check a version installed from a binary or some other source tree,
# if you want.
.PHONY: installcheck
installcheck: $(CHECK_PROGS) $(CHECK_SYMLINKS)
POSIXLY_CORRECT=1 TOOLDIR=`pwd` rsync_bin="$(bindir)/rsync$(EXEEXT)" srcdir="$(srcdir)" $(srcdir)/runtests.sh
"$(srcdir)/runtests.py" --rsync-bin="$(bindir)/rsync$(EXEEXT)" --srcdir="$(srcdir)" --tooldir="`pwd`" -j $(CHECK_J)
# TODO: Add 'dist' target; need to know which files will be included
@@ -237,21 +600,12 @@ installcheck: $(CHECK_PROGS) $(CHECK_SYMLINKS)
splint:
splint +unixlib +gnuextensions -weak rsync.c
rsync.dvi: doc/rsync.texinfo
texi2dvi -o $@ $<
rsync.ps: rsync.dvi
dvips -ta4 -o $@ $<
rsync.pdf: doc/rsync.texinfo
texi2dvi -o $@ --pdf $<
.PHONY: doxygen
doxygen:
cd $(srcdir) && rm dox/html/* && doxygen
# for maintainers only
.PHONY: doxygen-upload
doxygen-upload:
rsync -avzv $(srcdir)/dox/html/ --delete \
samba.org:/home/httpd/html/rsync/doxygen/head/
$${RSYNC_SAMBA_HOST-samba.org}:/home/httpd/html/rsync/doxygen/head/
-89
View File
@@ -1,89 +0,0 @@
NEWS for rsync 3.0.3 (29 Jun 2008)
Protocol: 30 (unchanged)
Changes since 3.0.2:
BUG FIXES:
- Fixed a wildcard matching problem in the daemon when a module has
"use chroot" enabled.
- Fixed a crash bug in the hard-link code.
- Fixed the sending of xattr directory information when the code finds a
--link-dest or --copy-dest directory with unchanged xattrs -- the
destination directory now gets these unchanged xattrs properly applied.
- Fixed an xattr-sending glitch that could cause an "Internal abbrev"
error.
- Fixed the combination of --xattrs and --backup.
- The generator no longer allows a '.' dir to be excluded by a daemon-
exclude rule.
- Fixed deletion handling when copying a single, empty directory (with no
files) to a differently named, non-existent directory.
- Fixed the conversion of spaces into dashes in the %M log escape.
- Fixed several places in the code that were not returning the right
errno when a function failed.
- Fixed the backing up of a device or special file into a backup dir.
- Moved the setting of the socket options prior to the connect().
- If rsync exits in the middle of a --progress output, it now outputs a
newline to help prevent the progress line from being overwritten.
- Fixed a problem with how a destination path with a trailing slash or
a trailing dot-dir was compared against the daemon excludes.
- Fixed the sending of large (size > 16GB) files when talking to an older
rsync (protocols < 30): we now use a compatible block size limit.
- If a file's length is so huge that we overflow a checksum buffer count
(i.e. several hundred TB), warn the user and avoid sending an invalid
checksum struct over the wire.
- If a source arg is excluded, --relative no longer adds the excluded
arg's implied dirs to the transfer. This fix also made the exclude
check happen in the better place in the sending code.
- Use the overflow_exit() function for overflows, not out_of_memory().
- Improved the code to better handle a system that has only 32-bit file
offsets.
ENHANCEMENTS:
- The rsyncd.conf manpage now consistently refers to the parameters in
the daemon config file as "parameters".
- The description of the --inplace option was improved.
EXTRAS:
- Added a new script in the support directory, deny-rsync, which allows
an admin to (temporarily) replace the rsync command with a script that
sends an error message to the remote client via the rsync protocol.
DEVELOPER RELATED:
- Fixed a testcase failure if the tests are run as root and made some
compatibility improvements.
- Improved the daemon tests, including checking module comments, the
listing of files, and the ensuring that daemon excludes can't affect
a dot-dir arg.
- Improved some build rules for those that build in a separate directory
from the source, including better install rules for the man pages, and
the fixing of a proto.h-tstamp rule that could make the binaries get
rebuild without cause.
- Improved the testsuite to work around a problem with some utilities
(e.g. cp -p & touch -r) rounding sub-second timestamps.
- Ensure that the early patches don't cause any generated-file hunks to
bleed-over into patches that follow.
+5805
View File
File diff suppressed because it is too large. Load diff
-2802
View File
File diff suppressed because it is too large. Load diff
+51 -36
View File
@@ -23,8 +23,18 @@ options. To get a complete list of supported options type:
rsync --help
See the manpage for more detailed information.
See the [manpage][0] for more detailed information.
[0]: https://download.samba.org/pub/rsync/rsync.1
BUILDING AND INSTALLING
-----------------------
If you need to build rsync yourself, check out the [INSTALL][1] page for
information on what libraries and packages you can use to get the maximum
features in your build.
[1]: https://github.com/RsyncProject/rsync/blob/master/INSTALL.md
SETUP
-----
@@ -55,17 +65,17 @@ RSYNC DAEMONS
-------------
Rsync can also talk to "rsync daemons" which can provide anonymous or
authenticated rsync. See the rsyncd.conf(5) man page for details on how
to setup an rsync daemon. See the rsync(1) man page for info on how to
authenticated rsync. See the rsyncd.conf(5) manpage for details on how
to setup an rsync daemon. See the rsync(1) manpage for info on how to
connect to an rsync daemon.
WEB SITE
--------
The main rsync web site is here:
For more information, visit the [main rsync web site][2].
http://rsync.samba.org/
[2]: https://rsync.samba.org/
You'll find a FAQ list, downloads, resources, HTML versions of the
manpages, etc.
@@ -77,64 +87,69 @@ MAILING LISTS
There is a mailing list for the discussion of rsync and its applications
that is open to anyone to join. New releases are announced on this
list, and there is also an announcement-only mailing list for those that
want official announcements. See the mailing-list page for full
details:
want official announcements. See the [mailing-list page][3] for full
details.
http://rsync.samba.org/lists.html
[3]: https://rsync.samba.org/lists.html
DISCORD
-------
There is also an rsync [Discord server][d] for real-time chat about rsync
and its development.
[d]: https://discord.gg/Avfvy9zhdp
BUG REPORTS
-----------
To visit this web page for full the details on bug reporting:
The [bug-tracking web page][4] has full details on bug reporting.
http://rsync.samba.org/bugzilla.html
[4]: https://rsync.samba.org/bug-tracking.html
That page contains links to the current bug list, and information on how
to report a bug well. You might also like to try searching the Internet
for the error message you've received, or looking in the mailing list
archives at:
That page contains links to the current bug list, and information on how to
do a good job when reporting a bug. You might also like to try searching
the Internet for the error message you've received, or looking in the
[mailing list archives][5].
http://mail-archive.com/rsync@lists.samba.org/
[5]: https://mail-archive.com/rsync@lists.samba.org/
To send a bug report, follow the instructions on the bug-tracking
page of the web site.
Alternately, email your bug report to rsync@lists.samba.org .
Alternately, email your bug report to <rsync@lists.samba.org>.
For security issues please email details of the issue to <rsync.project@gmail.com>.
GIT REPOSITORY
--------------
If you want to get the very latest version of rsync direct from the
source code repository then you can use git:
source code repository, then you will need to use git. The git repo
is hosted [on GitHub][6] and [on Samba's site][7].
git clone git://git.samba.org/rsync.git
[6]: https://github.com/RsyncProject/rsync
[7]: https://git.samba.org/?p=rsync.git;a=summary
See the download page for full details on all the ways to grab the
source, including nightly tar files, web-browsing of the git repository,
etc.:
See [the download page][8] for full details on all the ways to grab the
source.
http://rsync.samba.org/download.html
[8]: https://rsync.samba.org/download.html
COPYRIGHT
---------
Rsync was originally written by Andrew Tridgell and is currently
maintained by Wayne Davison. It has been improved by many developers
from around the world.
Rsync was originally written by Andrew Tridgell and Paul Mackerras. Many
people from around the world have helped to maintain and improve it.
Special thanks go to Wayne Davison, who maintained rsync from 2004 to 2024.
Rsync may be used, modified and redistributed only under the terms of
the GNU General Public License, found in the file COPYING in this
distribution, or at:
the GNU General Public License, found in the file [COPYING][9] in this
distribution, or at [the Free Software Foundation][10].
http://www.fsf.org/licenses/gpl.html
AVAILABILITY
------------
The main web site for rsync is http://rsync.samba.org/
The main ftp site is ftp://rsync.samba.org/pub/rsync/
This is also available as rsync://rsync.samba.org/rsyncftp/
[9]: https://github.com/RsyncProject/rsync/blob/master/COPYING
[10]: https://www.fsf.org/licenses/gpl.html
+542
View File
@@ -0,0 +1,542 @@
# Security Policy
## Supported Versions
Only the current release of the software is actively supported. If you need
help backporting fixes into an older release, feel free to ask.
## Reporting a Vulnerability
Email your vulnerability information to rsync's maintainer:
Rsync Project <rsync.project@gmail.com>
## Approach to platform residuals
rsync hardens its security-sensitive operations — path resolution, metadata
application, file/socket creation — against local attacks such as parent-symlink
TOCTOU races. Some of these operations can only be made race-safe with a
primitive the underlying OS provides (an `*at()` syscall on a held directory fd,
an fdescfs-style `/proc/self/fd` magic symlink, `mknodat()`, the `*xattrat`
syscalls, and so on), and that primitive is not available on every supported
platform.
The guiding rule for those cases is:
> **On a modern Linux system every issue described in this document is fully
> addressed.** Where an operation *can* be secured on some platforms but *cannot*
> be secured on others, and the residual risk is a *local* privilege-escalation
> or data-disclosure class (an attacker who already has write access inside the
> transferred tree), rsync prefers keeping the operation functional on the
> platforms that lack the primitive over disabling a long-standing feature for
> everyone on those platforms.
So a hardened operation takes the race-safe path wherever the platform offers one
and falls back to the historical (path-based, unconfined) behaviour only where it
does not — rather than refusing the operation outright. Each such fallback is an
accepted residual, documented under "Known residuals" below, and on the daemon it
can be turned off per feature with `refuse options = ...`. The residuals are
therefore confined to non-Linux platforms (the BSDs, macOS, Solaris/illumos),
Cygwin, and — for a few features — pre-6.13 Linux kernels; a current, normally
configured Linux deployment carries none of them. (The `/proc/self/fd`-based
fallbacks assume a mounted `/proc`, which every standard Linux provides; a
deliberately `/proc`-less container is the one Linux case that can still hit a
residual.)
The one deliberate exception is an operation whose unconfined fallback would
*create a new filesystem object at an attacker-influenceable path* rather than set
metadata on the object rsync already transferred: the nested-socket `bind()` on
platforms without a race-safe socket-create (no `bindat()`). There the unsafe path
is an out-of-tree write/create primitive, not a same-object metadata race, and a
transferred socket inode is a worthless placeholder, so rsync refuses (skips) it
rather than keeping it functional. A leaf permission change is likewise failed
closed rather than applied through a raced symlink, but only as a rare backstop:
the common file/dir/FIFO case is secured on every platform via `fchmod` on a held
fd, so no real functionality is lost.
This trade-off applies only to these local-attacker residual classes. Remotely
reachable defects — memory safety, authentication bypass, protocol parsing, input
bounds — are fixed unconditionally on all platforms, never left as a residual.
## Robustness against malicious peers
rsync treats everything the peer sends — the file list, checksum headers,
multiplexed messages, forwarded daemon arguments, filter rules — as untrusted,
and bounds-checks it before use. A peer-triggerable crash of a connection's
worker process is treated as a defect to be fixed, even though the daemon's
fork-per-connection model confines such a fault to that one connection rather
than the whole service.
Alongside the issues enumerated elsewhere in this document, the code is hardened
continuously through protocol fuzzing (driving the daemon protocol against a
writable module) and static analysis, with a CI gate. This release closes a
batch of peer-triggerable faults found that way: NULL-dereference and
reachable-assert crashes from crafted file lists or indices, reads past a
file-list allocation (mostly bounded over-reads of an entry's extra slots),
unbounded merge-file and suffix-list recursion, and several bounded
out-of-bounds writes driven by peer-supplied lengths or option arguments. Each
is fixed at the root with a bounds or validity check plus a defence-in-depth
guard at the use site, and carries a regression test.
Two further peer-input hardenings in this release: a peer-supplied I/O-error
value (the `MSG_IO_ERROR` message and the file-list trailer) is masked to the
defined `IOERR_*` bits, so a peer cannot set arbitrary error flags in the local
`io_error` that would be stored and re-forwarded upstream; and control
characters in a (peer-controlled) filename written to the log file are escaped,
so a name carrying C0/C1 terminal-escape bytes cannot inject sequences into an
administrator's terminal when the log is viewed (CWE-117). The number of
equal-weak-checksum blocks `hash_search()` examines per offset is also bounded
(issue #217), so a crafted or degenerate checksum set with a very long
equal-checksum chain cannot drive the sender's per-offset match-verify into a
quadratic walk and pin one connection's CPU.
Contributors adding code that consumes peer input should validate it at the
point of receipt rather than relying on a downstream check.
## Symlink-race-safe path resolution
This section documents how rsync defends against parent-directory symlink races
(a TOCTOU / confused-deputy class) and the per-platform approach it takes, so
that contributors and automated agents extend the code consistently rather than
reintroducing the weakness.
### The threat
Many rsync operations resolve pathnames that an unprivileged party can partially
control: a receiver writing into a destination tree, a sender reading a source
tree, and temp and partial files, and so on. (The operator-chosen directory
paths — `--link-dest`/`--compare-dest`/`--copy-dest`/`--backup-dir`/`--temp-dir`/
`--partial-dir` — may legitimately point outside the tree, so they are resolved
by the ownership walk described under *Symlink defense for operator-supplied
paths* below rather than the strict transfer-path resolver here.) If someone who
can write inside that tree races a
parent directory component between a real directory and a symlink ("symlink
flipping"), a path-based syscall — `open`, `stat`, `chmod`, `chown`, `utimes`,
`rename`, `unlink`, `mkdir`, `mknod`, `symlink`, hard-link creation — can be
redirected to a target *outside* the intended tree. When rsync resolves that
path with more authority than the component's controller and without a
confinement boundary, this is a confused-deputy bug (e.g. a root nightly backup
capturing `/etc/shadow`, or a root receiver chmod/chown/unlink-ing a system
file).
`O_NOFOLLOW` on the final component is **not** sufficient: the *parent*
components must be resolved safely.
The boundary that matters is **authority plus confinement**, not "daemon vs
non-daemon". A non-chroot daemon module, a root-run local transfer, and a
two-user transfer are all unconfined privileged path resolvers. Where a real
confinement boundary already exists (e.g. a per-module `chroot`) that is the
strongest protection; otherwise rsync must resolve paths defensively.
A `chroot` is only a boundary for the *outer* path it confines. A daemon module
written as `path = /outer/./inner` (`use chroot = yes`) chroots to `/outer` but
treats `/inner` as the module root, so a symlink inside the module that points to
a sibling of `/inner` is still inside the chroot yet outside the module — the
inner module therefore needs the same defensive resolution as a non-chroot
module. The single gate that decides when hardened resolution applies is
"unconfined privileged resolver": `am_daemon && (!am_chrooted || module_dirlen)`
for the daemon (any non-chroot module, plus a `/./` inner-module chroot), and any
non-chroot receiver. The local sender's content open is confined the same way for
default symlink handling; only the symlink-following modes (`-L`/`--copy-links`/
`--copy-unsafe-links`/`-k`) and `--insecure-links` are excluded, so those keep
following symlinks by design.
### The mechanism
Resolution of attacker-influenceable paths goes through `secure_relative_open()`
and the `do_*_at()` wrappers in `syscall.c`, never a raw `open()`/`rename()`/
`chmod()` on a full path string. The principle is: **trust the operator-named
transfer root, and confine all resolution beneath it**, rejecting escapes via
`..` above the anchor, absolute symlinks, or out-of-tree symlinks.
`secure_relative_open()` resolves the parent directory by walking it one
component at a time on a stack of held directory fds, then operates on the final
component with an at-style call on the resulting directory fd.
For per-entry work the receiver and generator go one step further and hold the
parent directory open: `open_dir_secure()` resolves an entry's directory once
(via `secure_relative_open()`), `held_dfd_for()` caches that descriptor for the
duration of the entry, and every operation on the entry — `lstat`, the temp-file
`mkstemp`, the temp->final `rename`, `chmod`/`chown`/`utimes`, `mkdir`, special-
file and symlink creation, the delta-basis open, and the recursive delete — runs
through that one held fd via an `*at()` call (`do_*_atfd()`). Because the
descriptor is pinned to the directory inode, a parent component flipped to a
symlink *after* the open cannot redirect any of those operations. The alternate-
destination lookups are confined the same way (`basis_link_stat()` in
`generator.c` and `secure_basis_open()` in `receiver.c`), so a peer-chosen
`--link-dest`/`--compare-dest`/`--copy-dest` basis index cannot reach an
out-of-module file through a symlinked parent.
The sender's source-directory *enumeration* is confined the same way as its
content open. `send_directory()` opens each scanned directory through
`secure_opendir()` — which resolves it via `secure_relative_open()` /
`secure_relative_open_at()` and turns the held fd into the `DIR*` with
`fdopendir()` — so a parent component raced into a symlink, or (for a daemon
following mode) an in-module symlink pointing outside the module, cannot redirect
the scan to enumerate an out-of-tree directory and leak its entry names, metadata
and symlink targets. For a daemon, both the enumeration and the content open
anchor at the served module root **pinned by identity**: `module_dirfd` is opened
(`open(".")`) the moment the daemon `chdir`s into the module, while still
privileged, and module-relative paths resolve beneath that fd via
`secure_relative_open_at()`. Anchoring at the held fd rather than re-resolving the
absolute module path keeps the confinement working after the daemon drops to the
module uid even when the module sits under a directory that uid cannot traverse
(e.g. a `0700` home — re-resolving the absolute path would `EACCES`), and is
immune to the logical-path-versus-real-cwd skew a followed in-tree directory
symlink would otherwise introduce.
### Path resolution
`secure_relative_open()` resolves a path with a single portable mechanism on
every platform: a per-component walk on a stack of held directory fds. Each
component is opened relative to the held parent with `openat(parent_fd,
"component", O_NOFOLLOW)`; descending into a real subdirectory pushes its fd, a
`..` pops back to the already-held parent (a pop at the anchor is refused), and an
in-tree directory symlink is followed by reading its target and walking that off
the same stack (absolute targets refused, symlink hops bounded). The final
component is opened `O_NOFOLLOW`.
Because every component is opened relative to a *pinned* fd under `O_NOFOLLOW`,
and `..` is resolved by the held-fd stack rather than by the kernel, the walk is
race-free by construction: no rename or symlink swap of any path name can redirect
resolution outside the anchor subtree, and no kernel "beneath" primitive
(`openat2(RESOLVE_BENEATH)` / `openat(O_RESOLVE_BENEATH)`) is required. The
confinement is therefore uniform across Linux, the BSDs, macOS and
Solaris/illumos, on old and new kernels alike, with nothing to probe or fall back
to at runtime (and so no `openat2`/seccomp interaction to worry about in sandboxed
environments). Cygwin is the exception, because its directory descriptors and
symlink emulation do not give the held-fd walk the same inode pinning — see the
Cygwin residual below.
Legitimate *in-tree* directory symlinks are followed, so `--keep-dirlinks` /
`--copy-links` and a symlinked module path keep working. A relative alternate-dest
such as `--compare-dest=../01` may legitimately climb to a sibling still inside the
module; such a `..` path is re-anchored at the module root and its in-module climb
adjudicated by the walk (the `..` pops to the held parent), while escapes above the
anchor are still rejected.
### Leaf operations
The final operation is hardened as well, following `cp`: reads use `O_NOFOLLOW`
so a flipped leaf symlink is not followed, and new or destination files are
created with `O_CREAT|O_EXCL` (rsync's temporary files use `mkstemp`) so a
planted symlink at the target cannot be written through. A leaf `chmod` is the
one operation with no portable no-follow form: it is closed by opening the leaf
`O_RDONLY|O_NOFOLLOW` and `fchmod`-ing the held fd (refusing a symlink leaf with
`ELOOP`), falling back to `fchmodat(AT_SYMLINK_NOFOLLOW)` and then the
`fchmodat2()` syscall, and failing closed with a warning rather than ever
chmod-ing through a raced leaf symlink.
### Guidance for contributors
* When adding code that performs a path-based syscall on a path that can be
influenced by the remote peer or by another local user, use a `do_*_at()`
wrapper (or `secure_relative_open()`), not a raw full-path syscall.
* When introducing a new operation, add a matching `do_<op>_at()` wrapper that
resolves the parent with `secure_relative_open()` and acts via an at-style call
on the returned dirfd.
* Do not assume a non-daemon transfer is safe; the question is whether rsync has
more authority than whoever controls the path components.
* On platforms whose API lacks an at-style equivalent (e.g. `setattrlist()`),
follow the residuals policy at the top of this document: for a metadata
operation on the already-transferred object (ACLs, xattrs, crtimes, permissions)
fall back to the path-based call to keep the feature functional and document the
residual; but where the unsafe fallback would *create a new object on an
unconfined path* (the nested-socket `bind()` case), refuse it instead — that is
an out-of-tree write/create primitive, not a same-object metadata race, and the
lost functionality is negligible.
## Symlink defense for operator-supplied paths
rsync opens several operator-supplied paths during normal operation. These fall
into two groups, both governed by the same ownership-walk policy below:
* operator **files**: `--log-file`, `--password-file`, `--early-input` (a client
read whose contents are forwarded to the daemon's early-exec), `--files-from`,
`--include-from`, `--exclude-from`, `--filter=. file`, `--write-batch`,
`--read-batch`, per-directory filter merge files (`-C` / `-F` / `dir-merge`),
and on the daemon side `motd file =`, `secrets file =`, `lock file =`, and
`rsyncd.conf` itself.
* operator **directories**: `--backup-dir`, `--temp-dir`/`-T`, `--partial-dir`,
and the `--link-dest`/`--compare-dest`/`--copy-dest` basis lookup. These take
a directory the operator chose, which may legitimately point outside the
transfer tree (`--backup-dir=/var/backups`), so they are resolved with the
ownership walk rather than the strict transfer-path resolver.
The daemon module-root `chdir()` under
`use chroot = no` and the non-daemon receiver's `chdir()` into the
operator-named destination directory are in the same class: both follow
the operator's/root's own symlinked target (the `/backup -> /mnt/disk`
admin pattern) but refuse one an attacker raced in from another uid,
unless `--insecure-links` restores the legacy plain `chdir()`.
Each of these reads or writes a path the operator or sender chose, which
may transit attacker-influenceable parent directories (the `/tmp/somedir/`
class) or be planted directly (the `/home/$user/.cvsignore` class when
root runs `rsync -a /home /backup`).
rsync's defense, applied uniformly to all of the above, is a
component-by-component path walk (`open_no_attacker_symlinks` in
`util1.c`) that allows symlinks **only** when the symlink itself is owned
by uid 0 or the running process's effective uid. Symlinks owned by any
other uid are refused with `ELOOP` at any path component (parent or leaf).
Plain `O_NOFOLLOW` would be leaf-only and would not defend the
`/tmp/somedir/log` parent-component plant; this walk does.
The trust model preserves legitimate setups such as `/var/log -> /data/log`
(root-owned dir-symlink) and a non-root user's own `~/log -> /data/me`
symlink; it refuses an attacker's `/tmp/somedir -> /attack/path` plant.
For `--read-batch` an additional `fstat()` check refuses non-regular
files (FIFOs, devices) at the batch path, since the batch content drives
the receiver's protocol parser.
**Policy.** A symlink at **any** path component (parent or leaf) is **followed
iff it is owned by uid 0 or the process's effective uid, and refused (`ELOOP`)
otherwise**, identically for **absolute and relative** operator paths. The trust
signal is **authority (ownership)**, not **location**: an operator path may
legitimately point outside the transfer tree, so it cannot be confined by
location the way a transfer path is. This is deliberately distinct from the
transfer-path resolver `secure_relative_open()` (see *Symlink-race-safe path
resolution* above), which refuses **all** symlinks and anchors **beneath the
transfer root** — correct for peer-named paths, which never legitimately escape.
For the operator directory paths, a refused symlink simply makes the target look
absent (no backup/temp/basis is taken through it) and the transfer proceeds
normally; the operator's own symlinked target keeps working.
**The daemon `exclude`/`filter` chain is not a symlink boundary.** The daemon
filter chain (`exclude`, `exclude from`, `filter`, …) matches the *logical*
module-relative **name** of each item, not the physical file it resolves to. It
is a visibility/tamper filter — a peer cannot *name* a daemon-excluded path to
pull, push to, or delete it — but it is **not** a security boundary against
symlinks: an in-module symlink whose own name is not excluded can be followed to
an excluded target (the name the filter sees, e.g. `link`, is not the excluded
name, e.g. `secret`). This is by design and is the long-standing behaviour of
stock rsync; the defense for a writable module against symlink trickery is
`munge symlinks` (enabled by default for a writable, non-chrooted module), **not**
the filter. Do not rely on `exclude`/`filter` to confine a peer who can introduce
or traverse a symlink; see `rsyncd.conf(5)` ("filter" and "munge symlinks").
What *is* enforced for a *peer-supplied* operator path (`--partial-dir`,
`--backup-dir`, the alt-dest basis) is confinement to the **module root**: the
ownership walk refuses a foreign-uid symlink (the symlink-race defense) and
refuses a resolved target *outside* the module. That module-boundary confinement
is independent of `exclude`/`filter` — it holds whether or not the module sets an
exclude — and is what the operator-path tests cover.
**`--insecure-links`.** This flag is a **local** opt-out that restores the legacy
follow-any-symlink behaviour for the paths above. It is **not forwarded** to the
remote (a remote-shell peer that wants the opt-out must set it on its own side,
e.g. via `--rsync-path`), and a **daemon never honors it**: the opt-out predicate
reads the client-controllable flag only off a daemon, so a peer-forwarded or
`-M`-injected `--insecure-links` cannot weaken a daemon's confinement — the daemon
additionally hard-refuses it (drops the connection) via the refused-options path.
A daemon admin who wants the legacy behaviour for one isolated/trusted module
sets `insecure links = yes` in that module's `rsyncd.conf` stanza (see
`rsyncd.conf(5)`); this is admin-only and re-opens the symlink-escape
vulnerabilities for that module on purpose. The `operator-path-*` and
`insecure-links-*` tests enforce this consistency across every path-taking
option and across absolute/relative, leaf/parent, and same-uid/cross-uid plants.
For `support/rrsync` (the SSH-restricted-rsync wrapper), the same TOCTOU
class is closed in Python by opening each validated path component with
`O_RDONLY|O_NOFOLLOW`, verifying via `readlink('/proc/self/fd/N')` that the
pinned inode is still in-tree, and passing `/proc/self/fd/N` as the exec'd
rsync's argument (so the kernel routes the child's open through the pinned
inode rather than re-resolving the path). A receiver-side new destination
has no inode of its own yet, so its existing parent directory is pinned the
same way and the leaf is created at `/proc/self/fd/<parent>/<leaf>`. This pin
relies on an fdescfs-style magic symlink and is not available on every
platform -- see the rrsync residual below.
### Known residuals
The following are documented as out of scope for this release:
* The source-directory *enumeration* confinement needs `fdopendir()` (to form a
`DIR*` from the securely-resolved held fd) and `dirfd()`; on a platform lacking
either, `send_directory()` falls back to the legacy `opendir()` on the path, so
the scan is unconfined there — the same resolver-fallback shape as the other
`*at()`-less residuals. Every current target provides both; the per-entry
operations and the content open remain confined regardless.
* On **Cygwin**, the per-component held-fd walk does not provide the same
inode-pinning guarantee as on a POSIX kernel: Cygwin tracks a process's
current directory and resolves directory descriptors by path name rather than
by a pinned inode, and emulates symlinks as special files. Static out-of-tree
symlinks are still refused (the walk sees and rejects them), and a daemon
module path anchored at an absolute `module_dir` is confined; but an entry
whose parent component is *raced* from a directory to a symlink mid-resolution
can still slip past confinement that is anchored at the process CWD (e.g. the
sender's content open), because the descriptor is not bound to the original
inode. The parent-component symlink-race tests are therefore not enforced on
Cygwin (see `RSYNC_EXPECT_SKIPPED` in `.github/workflows/cygwin-build.yml` and
the Cygwin-only xfail in `symlink-race-source_test.py`). Cygwin is a
development/interoperability target, not a privilege boundary host, so this is
accepted for this release.
* On a platform with no `mknodat()` at all -- macOS before 13 is the
supported example, where `mknod()` and `mkfifo()` exist but neither
`mknodat()` nor `mkfifoat()` does -- creating a device node or FIFO
falls back to plain `do_mknod()`, which resolves the whole path by name.
What is lost is the *pinned parent*: the directory components are
re-resolved by the kernel at create time, so an attacker who can swap a
parent component races the create and can place the node outside the
transfer. The final component is not at risk -- `mknod()` and
`mkfifo()` do not follow a symlink at the leaf, they fail `EEXIST`.
Where `AT_FDCWD` exists -- which is every platform rsync 3.5.0 supports,
macOS 10.13 included -- fake-super placeholders still return through
`openat(..., O_NOFOLLOW)`, reached before either `*at` primitive is
tested, so ordinary in-tree placeholder creation stays confined;
fake-super loses parent confinement and the `O_NOFOLLOW` leaf only on the
paths that reach plain `do_mknod()` (the cache-declined/cross-tree
wrapper and the backup paths). On a build with no `AT_FDCWD` at all
there is no fd-relative primitive of any kind, so nothing above applies
and every special-file create, fake-super included, is unconfined. Transferring specials there (`--devices`, `--specials`) carries
the parent-component race. `symlink-mknod-fakesuper-symlink-race` skips
itself on such a build, since the property it asserts is one the build
deliberately does not have.
* On platforms where `mknod()`/`mknodat()` cannot create a socket inode
(the BSDs, macOS, Solaris), a transferred socket is recreated with
`socket()` + `unlink` + `bind(path)`, which cannot be confined (there is
no portable `bindat()`). Linux creates it race-safely with `mknodat()`
on a held dirfd; on the others a *nested* socket is skipped with a
warning rather than bound on an unconfined path, leaving only a
top-level, operator-named socket binding by path.
* `support/rrsync`'s race-free inode-pin -- of both existing path
components and a new destination's parent -- depends on materialising a
held fd as a path that the exec'd rsync re-resolves to the same inode.
rrsync validates and pins in its own process, but it then *exec*s a
separate rsync that re-resolves the paths from `argv`, so the confining
reference must be expressible as an argument. A held dirfd is not: it is
usable as a path only through an fdescfs-style magic symlink. rrsync
implements this for Linux only, via `/proc/self/fd/N`; it does not use the
`/dev/fd/N` equivalent that macOS/FreeBSD expose with `fdescfs` mounted. So on
every non-Linux platform (the BSDs, macOS, Solaris -- whose `/proc/self/fd`
entries are not magic symlinks -- and Cygwin), and on a `/proc`-less Linux
namespace, rrsync falls through to the realpath-validated path unpinned,
so a parent-component or between-pin-and-exec flip remains possible there;
a deeper `-R` new path whose parent does not exist yet is likewise
unpinned. The portable closure is an rsync-side fd-passing API -- rrsync
hands rsync the confined dirfd (inherited across `exec`) and rsync
resolves that argument relative to it with the same `secure_relative_open`
resolver the daemon uses, needing no magic-symlink filesystem -- a
protocol/CLI addition under discussion on the rsync-security list.
* The operator-directory ownership walk refuses a foreign-owned symlink on a
`--backup-dir`/`--temp-dir`/`--partial-dir`/`--link/compare/copy-dest` path, so
a *statically planted* symlink is rejected and the dependent operation does not
escape. Both the data writes and the *source-metadata reads* of those
operations are now confined to held no-follow fds: the `--copy-dest`
`copy_file()`/`copy_xattrs()` source read goes through the held basis content fd
(`sys_fgetxattr`), and `make_backup()` reads the backed-up file's ACL/xattrs
through a `backup_source_fd()`-pinned fd -- so a parent-component flip can no
longer redirect them to disclose an out-of-module value. The cross-tree
metadata *apply* on those leaves (the `%stat`/ACL/xattr write on a
`--temp-dir`/`--backup-dir` staging file) is fd-pinned the same way, now
including under `--fake-super`: the `set_file_attrs()` no-follow leaf fd was
previously opened only when `am_root >= 0`, so a `fake super = yes` daemon fell
back to a path-based `sys_lsetxattr()`/chmod a raced parent could redirect; the
pin is now opened for fake-super too (a raced leaf is refused, not redirected).
Two narrow follow-ons
re-resolve the (now-validated) operator path by name and remain a
*post-validation* parent-component race:
* the in-place backup (`--inplace --backup`) writes the backup file's data
through a confined create, but its `set_file_attrs()` metadata set
(chmod/chown/times) re-resolves the `--backup-dir` path by name afterwards
(it is not placed under operator mode, which would force the shared
`set_file_attrs()` path off its held-O_NOFOLLOW-fd xattr write and re-open
the very parent-symlink xattr race `copy-xattrs-symlink-race` pins closed); and
* the abbreviated-xattr optimisation reuses a basis xattr value for the
destination only when its checksum matches the digest the sender sent; that
basis read (`rsync_xal_set()`) re-resolves the basis path by name. This is a
*constrained checksum-oracle*, not a disclosure: it confirms that some raced
out-of-module xattr hashes to a value the sender already chose, rather than
copying an unknown value onto a readable file, and needs a colluding sender
plus a local racer.
An attacker who flips a parent component in the window *after* the confined data
write/stat can thus still affect those narrow metadata/oracle operations. This
is the same local-attacker post-confinement TOCTOU class as the ACL/crtimes
residuals below; the data-write and direct source-read escapes are closed, and
`--insecure-links` (or a module's `insecure links = yes`) is orthogonal to it.
* POSIX ACL application (`-A`/`--acls`) is race-safe on every Linux kernel —
6.13+ via the `*xattrat` syscalls (or a patched libacl's `*_at` bindings), and
older kernels via the `/proc/self/fd` compat that pins the same inode, provided
`procfs` is mounted — and a transferred file/dir/FIFO has its xattrs (`-X`)
applied through the held no-follow fd, so the apply cannot be redirected by a
raced parent component. Where neither primitive is available — the BSDs,
Solaris and macOS (no `*xattrat` syscalls and no `/proc/self/fd` magic
symlinks), plus the edge case of a Linux instance with no usable `/proc` (a
`/proc`-less container/namespace) — the ACL apply falls back to the path-based
`acl_set_file()` /
`sys_acl_*file()` calls — the long-standing 3.4.x behaviour — to keep `--acls`
functional rather than silently skipping it, so a parent-component flip can
have the received ACL written onto an object outside the module/destination
boundary (and, because the attacker controls the ACL bytes, granted to a chosen
uid). As with the macOS crtime tier below, this is an accepted residual under
the functionality-over-refusal policy; a daemon operator who does not want it
can disable the feature with `refuse options = acls`.
* macOS creation-time (`--crtimes`) preservation uses the path-based
`setattrlist()`/`getattrlist()` with `FSOPT_NOFOLLOW`, which protects only
the final component; there is no `setattrlistat()` targeting
`ATTR_CMN_CRTIME`. As with POSIX ACLs where the OS offers no race-safe
primitive, `--crtimes` is kept functional (daemon and non-daemon) and the
parent-component symlink race is an accepted residual: an attacker who
flips a parent component can have a crtime read/write target an object
outside the module/destination boundary. The mtime/atime path is *not*
affected -- `set_times()` resolves it race-safely through `utimensat()` on a
held dirfd in hardened mode. A daemon operator who does not want the crtime
residual can disable the feature with `refuse options = crtimes` in
`rsyncd.conf`.
* Pulling with `-o`/`-g` (or `-a`) **as root from an untrusted sender** is by
design a trust relationship, not a confinement boundary: the sender dictates
each received file's owner/group, including uid/gid 0. rsync maps the
sender's id/name pairs through the local id database; an empty or unknown
sender name falls back to the sender's numeric id (the value `--numeric-ids`
would use), and a sender can equally request root via the literal name
`root`. A root receiver must therefore only pull with `-o`/`-g` from a
trusted source (or use a non-root receiver / a uid-gid policy). The daemon
*name-converter* path is guarded separately — an unknown name there maps to
the sender's numeric id rather than 0 (see `clientserver.c`).
## Daemon authentication digest
Daemon authentication is a secret-prefix challenge-response: the client returns
`base64(H(secret || challenge))`, where `H` is a digest the two sides negotiate.
The negotiation is unauthenticated and ordered by the connecting side, and the
`md5`/`md4` digests remain available for backward compatibility, so a peer that
sends no digest list (any rsync before 3.2.0, including the openrsync that ships
with macOS) falls back to `md5` (or `md4` below protocol 30), and an on-path
attacker can rewrite the negotiation to force `md5`/`md4` even between two modern
peers. This is **not** an authentication bypass — `md4`/`md5` have no practical
preimage break — but a weak digest makes a *captured* `(challenge, response)`
pair far cheaper to brute-force offline, recovering a guessable shared secret.
The challenge itself is seeded from the kernel CSPRNG (`/dev/urandom`), so it is
an unpredictable per-connection nonce. An earlier time/pid-based challenge was
low-entropy enough (~35 bits) that recovering the `(sec, usec, pid)` tuple from
one observed challenge let an on-path observer predict every subsequent challenge
from that daemon process and pre-compute a dictionary against a captured
response. (If `/dev/urandom` is unavailable the daemon logs a warning and falls
back to the legacy time-based challenge rather than a constant.)
A daemon operator whose clients are all modern (rsync 3.2.7+ built with openssl,
when the SHA digests were added) can require a strong digest with the `auth
digest` module parameter, e.g. `auth digest = sha256`, which refuses any
connection that negotiates — or falls back to — a weaker digest (see
`rsyncd.conf`).
Residual: there is **no default floor**, because requiring one would break every
pre-3.2.0 client (notably the macOS-bundled openrsync, which authenticates only
with `md4`). An operator who cannot raise the floor should run the daemon behind
a verified TLS transport (`rsync-ssl`/stunnel) or over ssh — which removes the
on-path capture/downgrade vector at the transport layer — and should use a
high-entropy shared secret, which is infeasible to brute-force regardless of the
digest.
+8 -19
View File
@@ -15,7 +15,6 @@ Create more granular verbosity 2003/05/15
DOCUMENTATION --------------------------------------------------------
Keep list of open issues and todos on the web site
Perhaps redo manual as SGML
LOGGING --------------------------------------------------------------
Memory accounting
@@ -49,7 +48,7 @@ Create test makefile target for some tests
RELATED PROJECTS -----------------------------------------------------
rsyncsh
http://rsync.samba.org/rsync-and-debian/
https://rsync.samba.org/rsync-and-debian/
rsyncable gzip patch
rsyncsplit as alternative to real integration with gzip?
reverse rsync over HTTP Range
@@ -66,8 +65,8 @@ Use chroot only if supported
If running as non-root, then don't fail, just give a warning.
(There was a thread about this a while ago?)
http://lists.samba.org/pipermail/rsync/2001-August/thread.html
http://lists.samba.org/pipermail/rsync/2001-September/thread.html
https://lists.samba.org/pipermail/rsync/2001-August/thread.html
https://lists.samba.org/pipermail/rsync/2001-September/thread.html
-- --
@@ -94,7 +93,7 @@ Handling IPv6 on old machines
platforms that have a half-working implementation, so redefining
these functions clashes with system headers, and leaving them out
breaks. This affects at least OSF/1, RedHat 5, and Cobalt, which
are moderately improtant.
are moderately important.
Perhaps the simplest solution would be to have two different files
implementing the same interface, and choose either the new or the
@@ -204,7 +203,7 @@ Create more granular verbosity 2003/05/15
fine grained selection of statistical reporting and what
actions are logged.
http://lists.samba.org/archive/rsync/2003-May/006059.html
https://lists.samba.org/archive/rsync/2003-May/006059.html
-- --
@@ -213,16 +212,6 @@ DOCUMENTATION --------------------------------------------------------
Keep list of open issues and todos on the web site
-- --
Perhaps redo manual as SGML
The man page is getting rather large, and there is more information
that ought to be added.
TexInfo source is probably a dying format.
Linuxdoc looks like the most likely contender. I know DocBook is
favoured by some people, but it's so bloody verbose, even with emacs
support.
@@ -236,7 +225,7 @@ Memory accounting
At exit, show how much memory was used for the file list, etc.
Also we do a wierd exponential-growth allocation in flist.c. I'm
We also do a weird exponential-growth allocation in flist.c. I'm
not sure this makes sense with modern mallocs. At any rate it will
make us allocate a huge amount of memory for large file lists.
@@ -287,7 +276,7 @@ Perhaps flush stdout like syslog
Perhaps flush stdout after each filename, so that people trying to
monitor progress in a log file can do so more easily. See
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=48108
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=48108
-- --
@@ -495,7 +484,7 @@ rsyncsh
-- --
http://rsync.samba.org/rsync-and-debian/
https://rsync.samba.org/rsync-and-debian/
-- --
+81 -34
View File
@@ -2,7 +2,7 @@
* Routines to authenticate access to a daemon (hosts allow/deny).
*
* Copyright (C) 1998 Andrew Tridgell
* Copyright (C) 2004-2008 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
@@ -19,15 +19,64 @@
*/
#include "rsync.h"
#include "ifuncs.h"
#ifdef HAVE_NETGROUP_H
#include <netgroup.h>
#endif
static int match_hostname(char *host, char *tok)
static int allow_forward_dns;
extern const char undetermined_hostname[];
static int match_hostname(const char **host_ptr, const char *addr, const char *tok, int deny)
{
struct hostent *hp;
unsigned int i;
const char *host = *host_ptr;
if (!host || !*host)
return 0;
return wildmatch(tok, host);
#ifdef HAVE_INNETGR
if (*tok == '@' && tok[1])
return innetgr(tok + 1, host, NULL, NULL);
#endif
/* First check if the reverse-DNS-determined hostname matches. */
if (iwildmatch(tok, host))
return 1;
if (!allow_forward_dns)
return 0;
/* Fail quietly if tok is an address or wildcarded entry, not a simple hostname. */
if (!tok[strspn(tok, ".0123456789")] || tok[strcspn(tok, ":/*?[")])
return 0;
/* Now try forward-DNS on the token (config-specified hostname) and see if the IP matches. */
if (!(hp = gethostbyname(tok))) {
/* A deny-list hostname token we cannot resolve must fail CLOSED:
* we can't prove the peer isn't the denied host, so treat the
* unresolvable token as a match (deny). Allow-list tokens keep
* failing as a non-match. Sibling of CVE-2026-43617, which fixed
* only the reverse-lookup path. */
return deny;
}
for (i = 0; hp->h_addr_list[i] != NULL; i++) {
if (strcmp(addr, inet_ntoa(*(struct in_addr*)(hp->h_addr_list[i]))) == 0) {
/* If reverse lookups are off, we'll use the conf-specified
* hostname in preference to UNDETERMINED. */
if (host == undetermined_hostname)
*host_ptr = strdup(tok);
return 1;
}
}
return 0;
}
static int match_binary(char *b1, char *b2, char *mask, int addrlen)
static int match_binary(const char *b1, const char *b2, const char *mask, int addrlen)
{
int i;
@@ -56,7 +105,7 @@ static void make_mask(char *mask, int plen, int addrlen)
return;
}
static int match_address(char *addr, char *tok)
static int match_address(const char *addr, char *tok)
{
char *p;
struct addrinfo hints, *resa, *rest;
@@ -70,24 +119,16 @@ static int match_address(char *addr, char *tok)
#endif
char mask[16];
char *a = NULL, *t = NULL;
unsigned int len;
if (!addr || !*addr)
return 0;
p = strchr(tok,'/');
if (p) {
if (p)
*p = '\0';
len = p - tok;
} else
len = strlen(tok);
/* Fail quietly if tok is a hostname (not an address) */
if (strspn(tok, ".0123456789") != len
#ifdef INET6
&& strchr(tok, ':') == NULL
#endif
) {
/* Fail quietly if tok is a hostname, not an address. */
if (tok[strspn(tok, ".0123456789")] && strchr(tok, ':') == NULL) {
if (p)
*p = '/';
return 0;
@@ -130,8 +171,7 @@ static int match_address(char *addr, char *tok)
break;
#ifdef INET6
case PF_INET6:
{
case PF_INET6: {
struct sockaddr_in6 *sin6a, *sin6t;
sin6a = (struct sockaddr_in6 *)resa->ai_addr;
@@ -143,20 +183,19 @@ static int match_address(char *addr, char *tok)
addrlen = 16;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
if (sin6t->sin6_scope_id &&
sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
if (sin6t->sin6_scope_id && sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
ret = 0;
goto out;
}
#endif
break;
}
}
#endif
default:
rprintf(FLOG, "unknown family %u\n", rest->ai_family);
ret = 0;
goto out;
rprintf(FLOG, "unknown family %u\n", rest->ai_family);
ret = 0;
goto out;
}
bits = -1;
@@ -210,20 +249,15 @@ static int match_address(char *addr, char *tok)
return ret;
}
static int access_match(char *list, char *addr, char *host)
static int access_match(const char *list, const char *addr, const char **host_ptr, int deny)
{
char *tok;
char *list2 = strdup(list);
if (!list2)
out_of_memory("access_match");
strlower(list2);
if (host)
strlower(host);
for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
if (match_hostname(host, tok) || match_address(addr, tok)) {
if (match_hostname(host_ptr, addr, tok, deny) || match_address(addr, tok)) {
free(list2);
return 1;
}
@@ -233,16 +267,21 @@ static int access_match(char *list, char *addr, char *host)
return 0;
}
int allow_access(char *addr, char *host, char *allow_list, char *deny_list)
int allow_access(const char *addr, const char **host_ptr, int i)
{
const char *allow_list = lp_hosts_allow(i);
const char *deny_list = lp_hosts_deny(i);
if (allow_list && !*allow_list)
allow_list = NULL;
if (deny_list && !*deny_list)
deny_list = NULL;
allow_forward_dns = lp_forward_lookup(i);
/* If we match an allow-list item, we always allow access. */
if (allow_list) {
if (access_match(allow_list, addr, host))
if (access_match(allow_list, addr, host_ptr, 0))
return 1;
/* For an allow-list w/o a deny-list, disallow non-matches. */
if (!deny_list)
@@ -251,9 +290,17 @@ int allow_access(char *addr, char *host, char *allow_list, char *deny_list)
/* If we match a deny-list item (and got past any allow-list
* items), we always disallow access. */
if (deny_list && access_match(deny_list, addr, host))
if (deny_list && access_match(deny_list, addr, host_ptr, 1))
return 0;
/* Allow all other access. */
return 1;
}
int allow_proxy_protocol_peer(const char *list, const char *addr, const char **host_ptr)
{
if (!list || !*list)
return 0;
allow_forward_dns = 0;
return access_match(list, addr, host_ptr, 0);
}
Vendored
-92
View File
@@ -1,92 +0,0 @@
dnl AC_VALIDATE_CACHE_SYSTEM_TYPE[(cmd)]
dnl if the cache file is inconsistent with the current host,
dnl target and build system types, execute CMD or print a default
dnl error message.
AC_DEFUN(AC_VALIDATE_CACHE_SYSTEM_TYPE, [
AC_REQUIRE([AC_CANONICAL_SYSTEM])
AC_MSG_CHECKING([config.cache system type])
if { test x"${ac_cv_host_system_type+set}" = x"set" &&
test x"$ac_cv_host_system_type" != x"$host"; } ||
{ test x"${ac_cv_build_system_type+set}" = x"set" &&
test x"$ac_cv_build_system_type" != x"$build"; } ||
{ test x"${ac_cv_target_system_type+set}" = x"set" &&
test x"$ac_cv_target_system_type" != x"$target"; }; then
AC_MSG_RESULT([different])
ifelse($#, 1, [$1],
[AC_MSG_ERROR(["you must remove config.cache and restart configure"])])
else
AC_MSG_RESULT([same])
fi
ac_cv_host_system_type="$host"
ac_cv_build_system_type="$build"
ac_cv_target_system_type="$target"
])
dnl Check for socklen_t: historically on BSD it is an int, and in
dnl POSIX 1g it is a type of its own, but some platforms use different
dnl types for the argument to getsockopt, getpeername, etc. So we
dnl have to test to find something that will work.
dnl This is no good, because passing the wrong pointer on C compilers is
dnl likely to only generate a warning, not an error. We don't call this at
dnl the moment.
AC_DEFUN([TYPE_SOCKLEN_T],
[
AC_CHECK_TYPE([socklen_t], ,[
AC_MSG_CHECKING([for socklen_t equivalent])
AC_CACHE_VAL([rsync_cv_socklen_t_equiv],
[
# Systems have either "struct sockaddr *" or
# "void *" as the second argument to getpeername
rsync_cv_socklen_t_equiv=
for arg2 in "struct sockaddr" void; do
for t in int size_t unsigned long "unsigned long"; do
AC_TRY_COMPILE([
#include <sys/types.h>
#include <sys/socket.h>
int getpeername (int, $arg2 *, $t *);
],[
$t len;
getpeername(0,0,&len);
],[
rsync_cv_socklen_t_equiv="$t"
break
])
done
done
if test "x$rsync_cv_socklen_t_equiv" = x; then
AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
fi
])
AC_MSG_RESULT($rsync_cv_socklen_t_equiv)
AC_DEFINE_UNQUOTED(socklen_t, $rsync_cv_socklen_t_equiv,
[type to use in place of socklen_t if not defined])],
[#include <sys/types.h>
#include <sys/socket.h>])
])
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],
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
AC_INCLUDES_DEFAULT
$2]],
[[$1 foo;]])],
[eval "ac_cv_type_$cv=yes"],
[eval "ac_cv_type_$cv=no"]))dnl
ac_foo=`eval echo \\$ac_cv_type_$cv`
AC_MSG_RESULT($ac_foo)
if test "$ac_foo" = yes; then
ac_tr_hdr=HAVE_`echo $1 | sed 'y%abcdefghijklmnopqrstuvwxyz./- %ABCDEFGHIJKLMNOPQRSTUVWXYZ____%'`
if false; then
AC_CHECK_TYPES($1)
fi
AC_DEFINE_UNQUOTED($ac_tr_hdr, 1, [Define if you have type `$1'])
fi
])
+534 -116
View File
@@ -3,7 +3,7 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2006-2008 Wayne Davison
* Copyright (C) 2006-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,10 @@
#include "rsync.h"
#include "lib/sysacls.h"
#include "lib/acl.h"
#ifdef HAVE_LIBACL_AT
#include <fcntl.h> /* AT_EMPTY_PATH / AT_SYMLINK_NOFOLLOW */
#endif
#ifdef SUPPORT_ACLS
@@ -28,9 +32,11 @@ 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;
extern int preserve_specials;
/* Flags used to indicate what items are being transmitted for an entry. */
#define XMIT_USER_OBJ (1<<0)
@@ -46,7 +52,7 @@ extern int inc_recurse;
/* When we send the access bits over the wire, we shift them 2 bits to the
* left and use the lower 2 bits as flags (relevant only to a name entry).
* This makes the protocol more efficient than sending a value that would
* be likely to have its hightest bits set. */
* be likely to have its highest bits set. */
#define XFLAG_NAME_FOLLOWS 0x0001u
#define XFLAG_NAME_IS_USER 0x0002u
@@ -88,6 +94,9 @@ static const rsync_acl empty_rsync_acl = {
static item_list access_acl_list = EMPTY_ITEM_LIST;
static item_list default_acl_list = EMPTY_ITEM_LIST;
static size_t prior_access_count = (size_t)-1;
static size_t prior_default_count = (size_t)-1;
/* === Calculations on ACL types === */
static const char *str_acl_type(SMB_ACL_TYPE_T type)
@@ -112,10 +121,11 @@ static int calc_sacl_entries(const rsync_acl *racl)
/* A System ACL always gets user/group/other permission entries. */
return racl->names.count
#ifdef ACLS_NEED_MASK
+ 4;
+ 1
#else
+ (racl->mask_obj != NO_ENTRY) + 3;
+ (racl->mask_obj != NO_ENTRY)
#endif
+ 3;
}
/* Extracts and returns the permission bits from the ACL. This cannot be
@@ -129,15 +139,21 @@ static int rsync_acl_get_perms(const rsync_acl *racl)
/* Removes the permission-bit entries from the ACL because these
* can be reconstructed from the file's mode. */
static void rsync_acl_strip_perms(rsync_acl *racl)
static void rsync_acl_strip_perms(stat_x *sxp)
{
rsync_acl *racl = sxp->acc_acl;
racl->user_obj = NO_ENTRY;
if (racl->mask_obj == NO_ENTRY)
racl->group_obj = NO_ENTRY;
else {
if (racl->group_obj == racl->mask_obj)
int group_perms = (sxp->st.st_mode >> 3) & 7;
if (racl->group_obj == group_perms)
racl->group_obj = NO_ENTRY;
racl->mask_obj = NO_ENTRY;
#ifndef HAVE_SOLARIS_ACLS
if (racl->names.count != 0 && racl->mask_obj == group_perms)
racl->mask_obj = NO_ENTRY;
#endif
}
racl->other_obj = NO_ENTRY;
}
@@ -156,8 +172,6 @@ static rsync_acl *create_racl(void)
{
rsync_acl *racl = new(rsync_acl);
if (!racl)
out_of_memory("create_racl");
*racl = empty_rsync_acl;
return racl;
@@ -320,14 +334,11 @@ static BOOL unpack_smb_acl(SMB_ACL_T sacl, rsync_acl *racl)
if (temp_ida_list.count) {
#ifdef SMB_ACL_NEED_SORT
if (temp_ida_list.count > 1) {
qsort(temp_ida_list.items, temp_ida_list.count,
sizeof (id_access), id_access_sorter);
qsort(temp_ida_list.items, temp_ida_list.count, sizeof (id_access), id_access_sorter);
}
#endif
if (!(racl->names.idas = new_array(id_access, temp_ida_list.count)))
out_of_memory("unpack_smb_acl");
memcpy(racl->names.idas, temp_ida_list.items,
temp_ida_list.count * sizeof (id_access));
racl->names.idas = new_array(id_access, temp_ida_list.count);
memcpy(racl->names.idas, temp_ida_list.items, temp_ida_list.count * sizeof (id_access));
} else
racl->names.idas = NULL;
@@ -336,15 +347,6 @@ static BOOL unpack_smb_acl(SMB_ACL_T sacl, rsync_acl *racl)
/* Truncate the temporary list now that its idas have been saved. */
temp_ida_list.count = 0;
#ifdef ACLS_NEED_MASK
if (!racl->names.count && racl->mask_obj != NO_ENTRY) {
/* Throw away a superfluous mask, but mask off the
* group perms with it first. */
racl->group_obj &= racl->mask_obj;
racl->mask_obj = NO_ENTRY;
}
#endif
return True;
}
@@ -420,7 +422,7 @@ static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl)
#ifdef ACLS_NEED_MASK
mask_bits = racl->mask_obj == NO_ENTRY ? racl->group_obj & ~NO_ENTRY : racl->mask_obj;
COE( sys_acl_create_entry,(smb_acl, &entry) );
COE( sys_acl_set_info,(entry, SMB_ACL_MASK, mask_bits, NULL) );
COE( sys_acl_set_info,(entry, SMB_ACL_MASK, mask_bits, 0) );
#else
if (racl->mask_obj != NO_ENTRY) {
COE( sys_acl_create_entry,(smb_acl, &entry) );
@@ -471,11 +473,129 @@ static int find_matching_rsync_acl(const rsync_acl *racl, SMB_ACL_TYPE_T type,
return *match;
}
static int get_rsync_acl(const char *fname, rsync_acl *racl,
SMB_ACL_TYPE_T type, mode_t mode)
/* These two bridge lib/acl.c's neutral (tag,perm,id) entry array; with
* HAVE_LIBACL_AT the libacl *_at path uses unpack_smb_acl/pack_smb_acl directly,
* so they are unused there. */
#if defined(SUPPORT_ACL_FD) && !defined(HAVE_LIBACL_AT)
/* Convert a packed system ACL into the neutral (tag,perm,id) entry array that
* lib/acl.c serializes. Reuses pack_smb_acl()+change_sacl_perms() output so
* the bytes we write match exactly what acl_set_file() would have written.
* Returns the entry count and a malloc'd array in *ents_p, or -1 on error. */
static int sacl_to_entries(SMB_ACL_T sacl, rsync_acl_ent **ents_p)
{
static item_list ent_list = EMPTY_ITEM_LIST;
SMB_ACL_ENTRY_T entry;
rsync_acl_ent *out;
int rc;
ent_list.count = 0;
for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry); rc == 1;
rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {
SMB_ACL_TAG_T tag_type;
uint32 access;
id_t g_u_id;
rsync_acl_ent *e;
uint16_t tag;
if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0)
break;
switch (tag_type) {
case SMB_ACL_USER_OBJ: tag = RACL_USER_OBJ; break;
case SMB_ACL_USER: tag = RACL_USER; break;
case SMB_ACL_GROUP_OBJ: tag = RACL_GROUP_OBJ; break;
case SMB_ACL_GROUP: tag = RACL_GROUP; break;
case SMB_ACL_MASK: tag = RACL_MASK; break;
case SMB_ACL_OTHER: tag = RACL_OTHER; break;
default: continue; /* skip an unrecognized tag */
}
e = EXPAND_ITEM_LIST(&ent_list, rsync_acl_ent, -10);
e->tag = tag;
e->perm = access & 7;
e->id = (tag == RACL_USER || tag == RACL_GROUP) ? (uint32_t)g_u_id : RACL_UNDEFINED_ID;
}
if (rc) {
rsyserr(FERROR_XFER, errno, "sacl_to_entries: sys_acl_get_entry/info()");
return -1;
}
out = new_array(rsync_acl_ent, ent_list.count ? ent_list.count : 1);
if (ent_list.count)
memcpy(out, ent_list.items, ent_list.count * sizeof (rsync_acl_ent));
*ents_p = out;
return ent_list.count;
}
/* Unpack a neutral entry array (from lib/acl.c) into an rsync_acl, mirroring
* unpack_smb_acl()'s tag handling. */
static BOOL unpack_acl_entries(const rsync_acl_ent *ents, int n, rsync_acl *racl)
{
static item_list temp_ida_list = EMPTY_ITEM_LIST;
int i;
temp_ida_list.count = 0;
for (i = 0; i < n; i++) {
uint32 access = ents[i].perm & 7;
id_access *ida;
switch (ents[i].tag) {
case RACL_USER_OBJ:
if (racl->user_obj == NO_ENTRY)
racl->user_obj = access;
continue;
case RACL_GROUP_OBJ:
if (racl->group_obj == NO_ENTRY)
racl->group_obj = access;
continue;
case RACL_MASK:
if (racl->mask_obj == NO_ENTRY)
racl->mask_obj = access;
continue;
case RACL_OTHER:
if (racl->other_obj == NO_ENTRY)
racl->other_obj = access;
continue;
case RACL_USER:
access |= NAME_IS_USER;
break;
case RACL_GROUP:
break;
default:
continue;
}
ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10);
ida->id = ents[i].id;
ida->access = access;
}
if (temp_ida_list.count) {
#ifdef SMB_ACL_NEED_SORT
if (temp_ida_list.count > 1)
qsort(temp_ida_list.items, temp_ida_list.count, sizeof (id_access), id_access_sorter);
#endif
racl->names.idas = new_array(id_access, temp_ida_list.count);
memcpy(racl->names.idas, temp_ida_list.items, temp_ida_list.count * sizeof (id_access));
} else
racl->names.idas = NULL;
racl->names.count = temp_ida_list.count;
temp_ida_list.count = 0;
return True;
}
#endif /* SUPPORT_ACL_FD */
static int get_rsync_acl(int fd, int dirfd, const char *leaf, const char *fname,
rsync_acl *racl, SMB_ACL_TYPE_T type, mode_t mode)
{
SMB_ACL_T sacl;
#ifndef SUPPORT_ACL_FD
#ifndef HAVE_SOLARIS_ACLS
(void)fd; /* Solaris drives the ACL via facl(2) on fd but has no SUPPORT_ACL_FD. */
#endif
(void)dirfd;
(void)leaf;
#endif
#ifdef SUPPORT_XATTRS
/* --fake-super support: load ACLs from an xattr. */
if (am_root < 0) {
@@ -483,7 +603,7 @@ static int get_rsync_acl(const char *fname, rsync_acl *racl,
size_t len;
int cnt;
if ((buf = get_xattr_acl(fname, type == SMB_ACL_TYPE_ACCESS, &len)) == NULL)
if ((buf = get_xattr_acl(fname, fd, type == SMB_ACL_TYPE_ACCESS, &len)) == NULL)
return 0;
cnt = (len - 4*4) / (4+4);
if (len < 4*4 || len != (size_t)cnt*(4+4) + 4*4) {
@@ -492,15 +612,19 @@ static int get_rsync_acl(const char *fname, rsync_acl *racl,
}
racl->user_obj = IVAL(buf, 0);
if (racl->user_obj == NO_ENTRY)
racl->user_obj = (mode >> 6) & 7;
racl->group_obj = IVAL(buf, 4);
if (racl->group_obj == NO_ENTRY)
racl->group_obj = (mode >> 3) & 7;
racl->mask_obj = IVAL(buf, 8);
racl->other_obj = IVAL(buf, 12);
if (racl->other_obj == NO_ENTRY)
racl->other_obj = mode & 7;
if (cnt) {
char *bp = buf + 4*4;
id_access *ida;
if (!(ida = racl->names.idas = new_array(id_access, cnt)))
out_of_memory("get_rsync_acl");
id_access *ida = racl->names.idas = new_array(id_access, cnt);
racl->names.count = cnt;
for ( ; cnt--; ida++, bp += 4+4) {
ida->id = IVAL(bp, 0);
@@ -512,11 +636,113 @@ static int get_rsync_acl(const char *fname, rsync_acl *racl,
}
#endif
#ifdef HAVE_SOLARIS_ACLS
/* Solaris has no libacl *_at; read the ACL through the held fd via facl(2)
* when we have one. With no held fd this branch is skipped and the path-based
* call below reads the ACL (acceptable: a read can't redirect a write out of
* the tree). */
if (fd >= 0) {
if ((sacl = sys_acl_get_fd_type(fd, type)) != 0) {
BOOL ok = unpack_smb_acl(sacl, racl);
sys_acl_free_acl(sacl);
if (!ok) {
rsyserr(FERROR_XFER, errno, "get_acl: unpack_smb_acl(%s)", fname);
return -1;
}
return 0;
}
if (no_acl_syscall_error(errno)) {
if (type == SMB_ACL_TYPE_ACCESS)
rsync_acl_fake_perms(racl, mode);
return 0;
}
rsyserr(FERROR_XFER, errno, "get_acl: sys_acl_get_fd_type(%s, %s)",
fname, str_acl_type(type));
return -1;
}
#endif
#ifdef SUPPORT_ACL_FD
#ifdef HAVE_LIBACL_AT
/* Read the ACL via the new libacl *_at calls; fd<0 && dirfd<0
* (e.g. a synthetic dir) falls through to the path-based call below. */
if (fd >= 0 || dirfd >= 0) {
if (fd >= 0)
sacl = sys_acl_get_file_at(fd, "", AT_EMPTY_PATH, type);
else
sacl = sys_acl_get_file_at(dirfd, leaf, AT_SYMLINK_NOFOLLOW, type);
if (sacl != 0) {
BOOL ok = unpack_smb_acl(sacl, racl);
sys_acl_free_acl(sacl);
if (!ok) {
rsyserr(FERROR_XFER, errno, "get_acl: unpack_smb_acl(%s)", fname);
return -1;
}
return 0;
}
if (no_acl_syscall_error(errno)) {
if (type == SMB_ACL_TYPE_ACCESS)
rsync_acl_fake_perms(racl, mode);
return 0;
}
rsyserr(FERROR_XFER, errno, "get_acl: acl_get_file_at(%s, %s)",
fname, str_acl_type(type));
return -1;
}
#else
/* Race-safe path: read the ACL through the held O_NOFOLLOW fd, or via
* setxattrat(AT_SYMLINK_NOFOLLOW) on dirfd+leaf, instead of re-resolving
* fname. Only for real-root ACLs (am_root >= 0; the fake-super branch
* above already returned). */
if (fd >= 0 || (dirfd >= 0 && xacl_at_available())) {
int is_def = type == SMB_ACL_TYPE_DEFAULT;
rsync_acl_ent *ents = NULL;
int n = 0, rc;
if (fd >= 0)
rc = xacl_get_fd(fd, is_def, &ents, &n);
else
rc = xacl_get_at(dirfd, leaf, is_def, &ents, &n);
if (rc < 0) {
if (no_acl_syscall_error(errno)) {
if (type == SMB_ACL_TYPE_ACCESS)
rsync_acl_fake_perms(racl, mode);
return 0;
}
rsyserr(FERROR_XFER, errno, "get_acl: xacl_get(%s, %s)",
fname, str_acl_type(type));
return -1;
}
if (n == 0) {
/* No explicit ACL: mirror libacl's mode-derived access ACL
* (an absent default ACL stays empty). */
if (type == SMB_ACL_TYPE_ACCESS)
rsync_acl_fake_perms(racl, mode);
} else if (!unpack_acl_entries(ents, n, racl)) {
if (ents)
free(ents);
rsyserr(FERROR_XFER, errno, "get_acl: unpack_acl_entries(%s)", fname);
return -1;
}
if (ents)
free(ents);
return 0;
}
/* Neither a held fd nor a usable dirfd path (xacl_at_available() covers the
* *xattrat syscalls AND the pre-6.13 /proc/self/fd compat, so this is the
* BSDs / a /proc-less namespace / an un-pinnable entry): read the real
* destination ACL via the path-based call rather than a mode-only fake, so
* --acls stays functional where the race-safe primitive is unavailable. */
#endif /* HAVE_LIBACL_AT */
#endif
if ((sacl = sys_acl_get_file(fname, type)) != 0) {
BOOL ok = unpack_smb_acl(sacl, 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)) {
@@ -532,11 +758,31 @@ static int get_rsync_acl(const char *fname, rsync_acl *racl,
return 0;
}
/* Return the Access Control List for the given filename. */
int get_acl(const char *fname, stat_x *sxp)
/* Return the Access Control List for the given filename. When a held
* O_NOFOLLOW fd (or a dirfd+leaf) is available, the ACL is read race-safely
* through it; otherwise (fd < 0 && dirfd < 0) the path-based fallback is used. */
int get_acl_fdat(int fd, int dirfd, const char *leaf, const char *fname, stat_x *sxp)
{
sxp->acc_acl = create_racl();
if (get_rsync_acl(fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS,
if (S_ISREG(sxp->st.st_mode) || S_ISDIR(sxp->st.st_mode)) {
/* Everyone supports this. */
} else if (S_ISLNK(sxp->st.st_mode)) {
return 0;
} else if (IS_SPECIAL(sxp->st.st_mode)) {
#ifndef NO_SPECIAL_ACLS
if (!preserve_specials)
#endif
return 0;
} else if (IS_DEVICE(sxp->st.st_mode)) {
#ifndef NO_DEVICE_ACLS
if (!preserve_devices)
#endif
return 0;
} else if (IS_MISSING_FILE(sxp->st))
return 0;
if (get_rsync_acl(fd, dirfd, leaf, fname, sxp->acc_acl, SMB_ACL_TYPE_ACCESS,
sxp->st.st_mode) < 0) {
free_acl(sxp);
return -1;
@@ -544,7 +790,7 @@ int get_acl(const char *fname, stat_x *sxp)
if (S_ISDIR(sxp->st.st_mode)) {
sxp->def_acl = create_racl();
if (get_rsync_acl(fname, sxp->def_acl, SMB_ACL_TYPE_DEFAULT,
if (get_rsync_acl(fd, dirfd, leaf, fname, sxp->def_acl, SMB_ACL_TYPE_DEFAULT,
sxp->st.st_mode) < 0) {
free_acl(sxp);
return -1;
@@ -554,10 +800,15 @@ int get_acl(const char *fname, stat_x *sxp)
return 0;
}
int get_acl(const char *fname, stat_x *sxp)
{
return get_acl_fdat(-1, -1, NULL, fname, sxp);
}
/* === Send functions === */
/* Send the ida list over the file descriptor. */
static void send_ida_entries(const ida_entries *idal, int f)
static void send_ida_entries(int f, const ida_entries *idal)
{
id_access *ida;
size_t count = idal->count;
@@ -569,9 +820,9 @@ static void send_ida_entries(const ida_entries *idal, int f)
const char *name;
if (ida->access & NAME_IS_USER) {
xbits |= XFLAG_NAME_IS_USER;
name = add_uid(ida->id);
name = numeric_ids ? NULL : add_uid(ida->id);
} else
name = add_gid(ida->id);
name = numeric_ids ? NULL : add_gid(ida->id);
write_varint(f, ida->id);
if (inc_recurse && name) {
int len = strlen(name);
@@ -583,8 +834,8 @@ static void send_ida_entries(const ida_entries *idal, int f)
}
}
static void send_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type,
item_list *racl_list, int f)
static void send_rsync_acl(int f, rsync_acl *racl, SMB_ACL_TYPE_T type,
item_list *racl_list)
{
int ndx = find_matching_rsync_acl(racl, type, racl_list);
@@ -617,7 +868,7 @@ static void send_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type,
if (flags & XMIT_OTHER_OBJ)
write_varint(f, racl->other_obj);
if (flags & XMIT_NAME_LIST)
send_ida_entries(&racl->names, f);
send_ida_entries(f, &racl->names);
/* Give the allocated data to the new list object. */
*new_racl = *racl;
@@ -627,28 +878,28 @@ static void send_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type,
/* Send the ACL from the stat_x structure down the indicated file descriptor.
* This also frees the ACL data. */
void send_acl(stat_x *sxp, int f)
void send_acl(int f, stat_x *sxp)
{
if (!sxp->acc_acl) {
sxp->acc_acl = create_racl();
rsync_acl_fake_perms(sxp->acc_acl, sxp->st.st_mode);
}
/* Avoid sending values that can be inferred from other data. */
rsync_acl_strip_perms(sxp->acc_acl);
rsync_acl_strip_perms(sxp);
send_rsync_acl(sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list, f);
send_rsync_acl(f, sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);
if (S_ISDIR(sxp->st.st_mode)) {
if (!sxp->def_acl)
sxp->def_acl = create_racl();
send_rsync_acl(sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list, f);
send_rsync_acl(f, sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list);
}
}
/* === Receive functions === */
static uint32 recv_acl_access(uchar *name_follows_ptr, int f)
static uint32 recv_acl_access(int f, uchar *name_follows_ptr)
{
uint32 access = read_varint(f);
@@ -673,23 +924,18 @@ static uint32 recv_acl_access(uchar *name_follows_ptr, int f)
return access;
}
static uchar recv_ida_entries(ida_entries *ent, int f)
static uchar recv_ida_entries(int f, ida_entries *ent)
{
uchar computed_mask_bits = 0;
int i, count = read_varint(f);
if (count) {
if (!(ent->idas = new_array(id_access, count)))
out_of_memory("recv_ida_entries");
} else
ent->idas = NULL;
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;
for (i = 0; i < count; i++) {
uchar has_name;
id_t id = read_varint(f);
uint32 access = recv_acl_access(&has_name, f);
uint32 access = recv_acl_access(f, &has_name);
if (has_name) {
if (access & NAME_IS_USER)
@@ -697,7 +943,7 @@ static uchar recv_ida_entries(ida_entries *ent, int f)
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))
@@ -712,7 +958,7 @@ static uchar recv_ida_entries(ida_entries *ent, int f)
return computed_mask_bits & ~NO_ENTRY;
}
static int recv_rsync_acl(item_list *racl_list, SMB_ACL_TYPE_T type, int f)
static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode_t mode)
{
uchar computed_mask_bits = 0;
acl_duo *duo_item;
@@ -727,7 +973,7 @@ static int recv_rsync_acl(item_list *racl_list, SMB_ACL_TYPE_T type, int f)
if (ndx != 0)
return ndx - 1;
ndx = racl_list->count;
duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000);
duo_item->racl = empty_rsync_acl;
@@ -735,29 +981,30 @@ static int recv_rsync_acl(item_list *racl_list, SMB_ACL_TYPE_T type, int f)
flags = read_byte(f);
if (flags & XMIT_USER_OBJ)
duo_item->racl.user_obj = recv_acl_access(NULL, f);
duo_item->racl.user_obj = recv_acl_access(f, NULL);
if (flags & XMIT_GROUP_OBJ)
duo_item->racl.group_obj = recv_acl_access(NULL, f);
duo_item->racl.group_obj = recv_acl_access(f, NULL);
if (flags & XMIT_MASK_OBJ)
duo_item->racl.mask_obj = recv_acl_access(NULL, f);
duo_item->racl.mask_obj = recv_acl_access(f, NULL);
if (flags & XMIT_OTHER_OBJ)
duo_item->racl.other_obj = recv_acl_access(NULL, f);
duo_item->racl.other_obj = recv_acl_access(f, NULL);
if (flags & XMIT_NAME_LIST)
computed_mask_bits |= recv_ida_entries(&duo_item->racl.names, f);
computed_mask_bits |= recv_ida_entries(f, &duo_item->racl.names);
#ifdef HAVE_OSX_ACLS
/* If we received a superfluous mask, throw it away. */
duo_item->racl.mask_obj = NO_ENTRY;
(void)mode;
(void)computed_mask_bits;
#else
if (!duo_item->racl.names.count) {
/* If we received a superfluous mask, throw it away. */
if (duo_item->racl.mask_obj != NO_ENTRY) {
/* Mask off the group perms with it first. */
duo_item->racl.group_obj &= duo_item->racl.mask_obj | NO_ENTRY;
duo_item->racl.mask_obj = NO_ENTRY;
}
} else if (duo_item->racl.mask_obj == NO_ENTRY) /* Must be non-empty with lists. */
duo_item->racl.mask_obj = (computed_mask_bits | duo_item->racl.group_obj) & ~NO_ENTRY;
if (duo_item->racl.names.count && duo_item->racl.mask_obj == NO_ENTRY) {
/* Mask must be non-empty with lists. */
if (type == SMB_ACL_TYPE_ACCESS)
computed_mask_bits = (mode >> 3) & 7;
else
computed_mask_bits |= duo_item->racl.group_obj & ~NO_ENTRY;
duo_item->racl.mask_obj = computed_mask_bits;
}
#endif
duo_item->sacl = NULL;
@@ -766,12 +1013,12 @@ static int recv_rsync_acl(item_list *racl_list, SMB_ACL_TYPE_T type, int f)
}
/* Receive the ACL info the sender has included for this file-list entry. */
void receive_acl(struct file_struct *file, int f)
void receive_acl(int f, struct file_struct *file)
{
F_ACL(file) = recv_rsync_acl(&access_acl_list, SMB_ACL_TYPE_ACCESS, f);
F_ACL(file) = recv_rsync_acl(f, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode);
if (S_ISDIR(file->mode))
F_DIR_DEFACL(file) = recv_rsync_acl(&default_acl_list, SMB_ACL_TYPE_DEFAULT, f);
F_DIR_DEFACL(file) = recv_rsync_acl(f, &default_acl_list, SMB_ACL_TYPE_DEFAULT, 0);
}
static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type, item_list *racl_list)
@@ -794,14 +1041,45 @@ static int cache_rsync_acl(rsync_acl *racl, SMB_ACL_TYPE_T type, item_list *racl
/* Turn the ACL data in stat_x into cached ACL data, setting the index
* values in the file struct. */
void cache_acl(struct file_struct *file, stat_x *sxp)
void cache_tmp_acl(struct file_struct *file, stat_x *sxp)
{
F_ACL(file) = cache_rsync_acl(sxp->acc_acl,
SMB_ACL_TYPE_ACCESS, &access_acl_list);
if (prior_access_count == (size_t)-1)
prior_access_count = access_acl_list.count;
F_ACL(file) = cache_rsync_acl(sxp->acc_acl, SMB_ACL_TYPE_ACCESS, &access_acl_list);
if (S_ISDIR(sxp->st.st_mode)) {
F_DIR_DEFACL(file) = cache_rsync_acl(sxp->def_acl,
SMB_ACL_TYPE_DEFAULT, &default_acl_list);
if (prior_default_count == (size_t)-1)
prior_default_count = default_acl_list.count;
F_DIR_DEFACL(file) = cache_rsync_acl(sxp->def_acl, SMB_ACL_TYPE_DEFAULT, &default_acl_list);
}
}
static void uncache_duo_acls(item_list *duo_list, size_t start)
{
acl_duo *duo_item = duo_list->items;
acl_duo *duo_start = duo_item + start;
duo_item += duo_list->count;
duo_list->count = start;
while (duo_item-- > duo_start) {
rsync_acl_free(&duo_item->racl);
if (duo_item->sacl)
sys_acl_free_acl(duo_item->sacl);
}
}
void uncache_tmp_acls(void)
{
if (prior_access_count != (size_t)-1) {
uncache_duo_acls(&access_acl_list, prior_access_count);
prior_access_count = (size_t)-1;
}
if (prior_default_count != (size_t)-1) {
uncache_duo_acls(&default_acl_list, prior_default_count);
prior_default_count = (size_t)-1;
}
}
@@ -850,12 +1128,14 @@ static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode
COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
break;
case SMB_ACL_MASK:
#ifndef HAVE_SOLARIS_ACLS
#ifndef ACLS_NEED_MASK
/* mask is only empty when we don't need it. */
if (racl->mask_obj == NO_ENTRY)
break;
#endif
COE2( store_access_in_entry,((mode >> 3) & 7, entry) );
#endif
break;
case SMB_ACL_OTHER:
COE2( store_access_in_entry,(mode & 7, entry) );
@@ -868,7 +1148,7 @@ static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode
rsyserr(FERROR_XFER, errno, "change_sacl_perms: %s()",
errfun);
}
return (mode_t)~0;
return (mode_t)-1;
}
#ifdef SMB_ACL_LOSES_SPECIAL_MODE_BITS
@@ -883,17 +1163,60 @@ static mode_t change_sacl_perms(SMB_ACL_T sacl, rsync_acl *racl, mode_t old_mode
}
#endif
static int set_rsync_acl(const char *fname, acl_duo *duo_item,
SMB_ACL_TYPE_T type, stat_x *sxp, mode_t mode)
static int set_rsync_acl(int fd, int dirfd, const char *leaf, const char *fname,
acl_duo *duo_item, SMB_ACL_TYPE_T type, stat_x *sxp, mode_t mode)
{
#ifndef SUPPORT_ACL_FD
#ifndef HAVE_SOLARIS_ACLS
(void)fd; /* Solaris drives the ACL via facl(2) on fd but has no SUPPORT_ACL_FD. */
#endif
(void)dirfd;
(void)leaf;
#endif
if (type == SMB_ACL_TYPE_DEFAULT
&& duo_item->racl.user_obj == NO_ENTRY) {
int rc;
#ifdef SUPPORT_XATTRS
/* --fake-super support: delete default ACL from xattrs. */
if (am_root < 0)
rc = del_def_xattr_acl(fname);
rc = del_def_xattr_acl(fd, fname);
else
#endif
#ifdef SUPPORT_ACL_FD
#ifdef HAVE_LIBACL_AT
/* Race-safe default-ACL delete via the new libacl *_at
* calls (held fd via AT_EMPTY_PATH, dirfd+leaf via AT_SYMLINK_NOFOLLOW)
* -- race-safe on every Linux kernel. fd<0 && dirfd<0 falls to path. */
if (fd >= 0)
rc = sys_acl_delete_def_file_at(fd, "", AT_EMPTY_PATH);
else if (dirfd >= 0)
rc = sys_acl_delete_def_file_at(dirfd, leaf, AT_SYMLINK_NOFOLLOW);
else
#else
/* Race-safe default-ACL delete via the held fd or dirfd+leaf. Where
* neither is available (xacl_at_available() is false -- the BSDs, a
* /proc-less namespace, an un-pinnable entry; every Linux with procfs
* takes the dirfd path via *xattrat or the /proc/self/fd compat) -- fall
* back to the path-based call, preferring the documented --acls behaviour
* over refusing it where the race-safe primitive is unavailable. */
if (fd >= 0)
rc = xacl_del_default_fd(fd);
else if (dirfd >= 0 && xacl_at_available())
rc = xacl_del_default_at(dirfd, leaf);
else
#endif /* HAVE_LIBACL_AT */
#endif
#ifdef HAVE_SOLARIS_ACLS
/* Solaris: delete the default ACL through the held fd via facl(2). For a
* root receiver a missing held fd means the leaf was raced, so refuse rather
* than let the path-based delete follow it; a plain non-root receiver keeps
* the legacy path fallback (op_pin am_root != 0 rule). */
if (fd >= 0)
rc = sys_acl_delete_def_fd(fd);
else if (vfs_relpath_active() && am_root) {
errno = ELOOP;
rc = -1;
} else
#endif
rc = sys_acl_delete_def_file(fname);
if (rc < 0) {
@@ -922,7 +1245,7 @@ static int set_rsync_acl(const char *fname, acl_duo *duo_item,
SIVAL(bp, 4, ida->access);
}
}
rc = set_xattr_acl(fname, type == SMB_ACL_TYPE_ACCESS, buf, len);
rc = set_xattr_acl(fd, fname, type == SMB_ACL_TYPE_ACCESS, buf, len);
free(buf);
return rc;
#endif
@@ -932,14 +1255,99 @@ 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);
if (cur_mode == (mode_t)~0)
cur_mode = change_sacl_perms(duo_item->sacl, &duo_item->racl, cur_mode, mode);
if (cur_mode == (mode_t)-1)
return 0;
}
#endif
#ifdef SUPPORT_ACL_FD
#ifdef HAVE_LIBACL_AT
/* Apply the packed/perm-reconciled ACL (duo_item->sacl)
* through the new libacl *_at calls -- held fd via AT_EMPTY_PATH,
* dirfd+leaf via AT_SYMLINK_NOFOLLOW -- race-safe on every Linux kernel,
* and byte-identical to the path-based sys_acl_set_file() below. */
if (fd >= 0 || dirfd >= 0) {
int rc;
if (fd >= 0)
rc = sys_acl_set_file_at(fd, "", AT_EMPTY_PATH, type, duo_item->sacl);
else
rc = sys_acl_set_file_at(dirfd, leaf, AT_SYMLINK_NOFOLLOW, type, duo_item->sacl);
if (rc < 0) {
rsyserr(FERROR_XFER, errno, "set_acl: acl_set_file_at(%s, %s)",
fname, str_acl_type(type));
return -1;
}
if (type == SMB_ACL_TYPE_ACCESS)
sxp->st.st_mode = cur_mode;
return 0;
}
#else
/* Race-safe write: serialize the packed (and perm-reconciled)
* system ACL to the kernel xattr format and apply it through the
* held fd or dirfd+leaf -- never re-resolving fname. This matches
* exactly what sys_acl_set_file() would have written. */
if (fd >= 0 || (dirfd >= 0 && xacl_at_available())) {
int is_def = type == SMB_ACL_TYPE_DEFAULT;
rsync_acl_ent *ents;
int n = sacl_to_entries(duo_item->sacl, &ents);
int rc;
if (n < 0)
return -1;
if (fd >= 0)
rc = xacl_set_fd(fd, is_def, ents, n);
else
rc = xacl_set_at(dirfd, leaf, is_def, ents, n);
free(ents);
if (rc < 0) {
rsyserr(FERROR_XFER, errno, "set_acl: xacl_set(%s, %s)",
fname, str_acl_type(type));
return -1;
}
if (type == SMB_ACL_TYPE_ACCESS)
sxp->st.st_mode = cur_mode;
return 0;
}
/* No held fd and no usable dirfd path (xacl_at_available() is false --
* the BSDs, a /proc-less namespace, an un-pinnable entry; every Linux
* with procfs took xacl_set_at() above via *xattrat or the /proc/self/fd
* compat): prefer the documented --acls behaviour over refusing it and
* fall back to the path-based set. This re-resolves fname, so it still
* carries the parent-symlink-race exposure on those remaining platforms;
* it is the only way to honour --acls where no race-safe primitive
* exists. */
#endif /* HAVE_LIBACL_AT */
#endif
#ifdef HAVE_SOLARIS_ACLS
/* Solaris: apply the ACL through the held fd via facl(2). */
if (fd >= 0) {
if (sys_acl_set_fd_type(fd, type, duo_item->sacl) < 0) {
rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_set_fd_type(%s, %s)",
fname, str_acl_type(type));
return -1;
}
if (type == SMB_ACL_TYPE_ACCESS)
sxp->st.st_mode = cur_mode;
return 0;
}
if (vfs_relpath_active() && am_root) {
/* Real root always can open its own freshly-staged reg/dir/fifo leaf,
* so a missing held fd on a confined receiver means the leaf was raced
* to a symlink; sys_acl_set_file() follows the leaf, so refuse rather
* than write the attacker-supplied ACL onto a redirected inode (covers
* the top-level no-slash entry the caller's slashed-path xattr_refuse
* gate misses). A plain non-root receiver keeps the path-based fallback
* for a legitimately un-pinnable owned leaf (e.g. a 0300 dir), matching
* the operator-path op_pin rule (am_root != 0). */
errno = ELOOP;
rsyserr(FERROR_XFER, errno, "set_acl: refusing path-based ACL on %s (no held fd)",
fname);
return -1;
}
#endif
if (sys_acl_set_file(fname, type, duo_item->sacl) < 0) {
rsyserr(FERROR_XFER, errno, "set_acl: sys_acl_set_file(%s, %s)",
@@ -953,17 +1361,22 @@ static int set_rsync_acl(const char *fname, acl_duo *duo_item,
return 0;
}
/* Set ACL on indicated filename.
/* Given a fname, this sets extended access ACL entries, the default ACL (for a
* dir), and the regular mode bits on the file. Call this with fname set to
* NULL to just check if the ACL is different.
*
* This sets extended access ACL entries and default ACL. If convenient,
* it sets permission bits along with the access ACL and signals having
* done so by modifying sxp->st.st_mode.
* When a held O_NOFOLLOW fd (or a dirfd+leaf) is supplied, the ACL is applied
* race-safely through it; otherwise (fd < 0 && dirfd < 0) the path-based
* fallback is used.
*
* Returns 1 for unchanged, 0 for changed, -1 for failed. Call this
* with fname set to NULL to just check if the ACL is unchanged. */
int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp)
* If the ACL operation has a side-effect of changing the file's mode, the
* sxp->st.st_mode value will be changed to match.
*
* Returns 0 for an unchanged ACL, 1 for changed, -1 for failed. */
int set_acl_fdat(int fd, int dirfd, const char *leaf, const char *fname,
const struct file_struct *file, stat_x *sxp, mode_t new_mode)
{
int unchanged = 1;
int changed = 0;
int32 ndx;
BOOL eq;
@@ -977,18 +1390,18 @@ int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp)
acl_duo *duo_item = access_acl_list.items;
duo_item += ndx;
eq = sxp->acc_acl
&& rsync_acl_equal_enough(sxp->acc_acl, &duo_item->racl, file->mode);
&& rsync_acl_equal_enough(sxp->acc_acl, &duo_item->racl, new_mode);
if (!eq) {
unchanged = 0;
changed = 1;
if (!dry_run && fname
&& set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_ACCESS,
sxp, file->mode) < 0)
unchanged = -1;
&& set_rsync_acl(fd, dirfd, leaf, fname, duo_item, SMB_ACL_TYPE_ACCESS,
sxp, new_mode) < 0)
return -1;
}
}
if (!S_ISDIR(sxp->st.st_mode))
return unchanged;
if (!S_ISDIR(new_mode))
return changed;
ndx = F_DIR_DEFACL(file);
if (ndx >= 0 && (size_t)ndx < default_acl_list.count) {
@@ -996,16 +1409,20 @@ int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp)
duo_item += ndx;
eq = sxp->def_acl && rsync_acl_equal(sxp->def_acl, &duo_item->racl);
if (!eq) {
if (unchanged > 0)
unchanged = 0;
changed = 1;
if (!dry_run && fname
&& set_rsync_acl(fname, duo_item, SMB_ACL_TYPE_DEFAULT,
sxp, file->mode) < 0)
unchanged = -1;
&& set_rsync_acl(fd, dirfd, leaf, fname, duo_item, SMB_ACL_TYPE_DEFAULT,
sxp, new_mode) < 0)
return -1;
}
}
return unchanged;
return changed;
}
int set_acl(const char *fname, const struct file_struct *file, stat_x *sxp, mode_t new_mode)
{
return set_acl_fdat(-1, -1, NULL, fname, file, sxp, new_mode);
}
/* Non-incremental recursion needs to convert all the received IDs.
@@ -1048,20 +1465,21 @@ int default_perms_for_dir(const char *dir)
if (sacl == NULL) {
/* Couldn't get an ACL. Darn. */
switch (errno) {
case EINVAL:
/* If SMB_ACL_TYPE_DEFAULT isn't valid, then the ACLs must be non-POSIX. */
break;
#ifdef ENOTSUP
case ENOTSUP:
#endif
case ENOSYS:
/* No ACLs are available. */
break;
case ENOENT:
if (dry_run) {
default:
if (dry_run && errno == ENOENT) {
/* We're doing a dry run, so the containing directory
* wasn't actually created. Don't worry about it. */
break;
}
/* Otherwise fall through. */
default:
rprintf(FWARNING,
"default_perms_for_dir: sys_acl_get_file(%s, %s): %s, falling back on umask\n",
dir, str_acl_type(SMB_ACL_TYPE_DEFAULT), strerror(errno));
@@ -1081,7 +1499,7 @@ int default_perms_for_dir(const char *dir)
/* Apply the permission-bit entries of the default ACL, if any. */
if (racl.user_obj != NO_ENTRY) {
perms = rsync_acl_get_perms(&racl);
if (verbose > 2)
if (DEBUG_GTE(ACL, 1))
rprintf(FINFO, "got ACL-based default perms %o for directory %s\n", perms, dir);
}
+323 -164
View File
@@ -2,7 +2,7 @@
* Support rsync daemon authentication.
*
* Copyright (C) 1998-2000 Andrew Tridgell
* Copyright (C) 2002-2008 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
@@ -19,8 +19,19 @@
*/
#include "rsync.h"
#include "itypes.h"
#include "ifuncs.h"
/* O_CLOEXEC is absent on some still-supported targets. The random-source fd
* is read and closed synchronously, so the established zero-value fallback is
* sufficient without adding a configure dependency. */
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
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
@@ -53,10 +64,31 @@ void base64_encode(const char *buf, int len, char *out, int pad)
out[i] = '\0';
}
/* Fill buf with len bytes from the kernel CSPRNG. Returns 1 on success.
* We read /dev/urandom directly rather than depending on getrandom()/
* arc4random_buf() availability so this works on every platform rsync
* targets without new configure probes. */
static int get_random_bytes(char *buf, int len)
{
int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
int got = 0;
if (fd < 0)
return 0;
while (got < len) {
int n = read(fd, buf + got, len - got);
if (n <= 0)
break;
got += n;
}
close(fd);
return got == len;
}
/* Generate a challenge buffer and return it base64-encoded. */
static void gen_challenge(const char *addr, char *challenge)
{
char input[32];
char rnd[32];
char digest[MAX_DIGEST_LEN];
struct timeval tv;
int len;
@@ -69,136 +101,23 @@ static void gen_challenge(const char *addr, char *challenge)
SIVAL(input, 20, tv.tv_usec);
SIVAL(input, 24, getpid());
sum_init(0);
len = sum_init(valid_auth_checksums.negotiated_nni, 0);
/* The challenge must be unpredictable to a network observer; addr+time
* +pid alone is ~35 bits and lets an attacker enumerate the preimage
* offline. Hash 32 bytes from the kernel RNG first so the digest
* carries full entropy, keeping the legacy inputs as a mix-in so a
* urandom failure degrades to (never below) the old behaviour. */
if (get_random_bytes(rnd, sizeof rnd))
sum_update(rnd, sizeof rnd);
else
rprintf(FWARNING, "gen_challenge: /dev/urandom unavailable, "
"falling back to time-based challenge\n");
sum_update(input, sizeof input);
len = sum_end(digest);
sum_end(digest);
base64_encode(digest, len, challenge, 0);
}
/* Return the secret for a user from the secret file, null terminated.
* Maximum length is len (not counting the null). */
static int get_secret(int module, const char *user, char *secret, int len)
{
const char *fname = lp_secrets_file(module);
STRUCT_STAT st;
int fd, ok = 1;
const char *p;
char ch, *s;
if (!fname || !*fname)
return 0;
if ((fd = open(fname, O_RDONLY)) < 0)
return 0;
if (do_stat(fname, &st) == -1) {
rsyserr(FLOG, errno, "stat(%s)", fname);
ok = 0;
} else if (lp_strict_modes(module)) {
if ((st.st_mode & 06) != 0) {
rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
ok = 0;
} else if (MY_UID() == 0 && st.st_uid != 0) {
rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
ok = 0;
}
}
if (!ok) {
rprintf(FLOG, "continuing without secrets file\n");
close(fd);
return 0;
}
if (*user == '#') {
/* Reject attempt to match a comment. */
close(fd);
return 0;
}
/* Try to find a line that starts with the user name and a ':'. */
p = user;
while (1) {
if (read(fd, &ch, 1) != 1) {
close(fd);
return 0;
}
if (ch == '\n')
p = user;
else if (p) {
if (*p == ch)
p++;
else if (!*p && ch == ':')
break;
else
p = NULL;
}
}
/* Slurp the secret into the "secret" buffer. */
s = secret;
while (len > 0) {
if (read(fd, s, 1) != 1 || *s == '\n')
break;
if (*s == '\r')
continue;
s++;
len--;
}
*s = '\0';
close(fd);
return 1;
}
static const char *getpassf(const char *filename)
{
STRUCT_STAT st;
char buffer[512], *p;
int fd, n, ok = 1;
const char *envpw = getenv("RSYNC_PASSWORD");
if (!filename)
return NULL;
if ((fd = open(filename,O_RDONLY)) < 0) {
rsyserr(FWARNING, errno, "could not open password file \"%s\"",
filename);
if (envpw)
rprintf(FINFO, "falling back to RSYNC_PASSWORD environment variable.\n");
return NULL;
}
if (do_stat(filename, &st) == -1) {
rsyserr(FWARNING, errno, "stat(%s)", filename);
ok = 0;
} else if ((st.st_mode & 06) != 0) {
rprintf(FWARNING, "password file must not be other-accessible\n");
ok = 0;
} else if (MY_UID() == 0 && st.st_uid != 0) {
rprintf(FWARNING, "password file must be owned by root when running as root\n");
ok = 0;
}
if (!ok) {
close(fd);
rprintf(FWARNING, "continuing without password file\n");
if (envpw)
rprintf(FINFO, "falling back to RSYNC_PASSWORD environment variable.\n");
return NULL;
}
n = read(fd, buffer, sizeof buffer - 1);
close(fd);
if (n > 0) {
buffer[n] = '\0';
if ((p = strtok(buffer, "\n\r")) != NULL)
return strdup(p);
}
return NULL;
}
/* Generate an MD4 hash created from the combination of the password
* and the challenge string and return it base64-encoded. */
static void generate_hash(const char *in, const char *challenge, char *out)
@@ -206,14 +125,160 @@ static void generate_hash(const char *in, const char *challenge, char *out)
char buf[MAX_DIGEST_LEN];
int len;
sum_init(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);
}
/* Return the secret for a user from the secret file, null terminated.
* Maximum length is len (not counting the null). */
static const char *check_secret(int module, const char *user, const char *group,
const char *challenge, const char *pass)
{
char line[1024];
char pass2[MAX_DIGEST_LEN*2];
const char *fname = lp_secrets_file(module);
STRUCT_STAT st;
int ok = 1;
int user_len = strlen(user);
int group_len = group ? strlen(group) : 0;
char *err;
FILE *fh;
/* Daemon 'secrets file = PATH' open. A planted symlink would be
* followed and the strict-modes fstat() check below runs on the target
* inode, so a symlink to /etc/shadow (0640 root:shadow) would pass and
* the daemon would auth against shadow hashes. Refuse symlinks not
* owned by uid 0 or our euid. */
if (!fname || !*fname)
return "no secrets file";
{
int fd = vfs_open_owner_walk(fname, O_RDONLY, 0, 0);
if (fd < 0)
return "no secrets file";
fh = fdopen(fd, "r");
if (!fh) {
close(fd);
return "no secrets file";
}
}
if (vfs_fstat(fileno(fh), &st) == -1) {
rsyserr(FLOG, errno, "fstat(%s)", fname);
ok = 0;
} else if (lp_strict_modes(module)) {
if ((st.st_mode & 06) != 0) {
rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
ok = 0;
} else if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
ok = 0;
}
}
if (!ok) {
fclose(fh);
return "ignoring secrets file";
}
if (*user == '#') {
/* Reject attempt to match a comment. */
fclose(fh);
return "invalid username";
}
/* Try to find a line that starts with the user (or @group) name and a ':'. */
err = "secret not found";
while ((user || group) && fgets(line, sizeof line, fh) != NULL) {
const char **ptr, *s = strtok(line, "\n\r");
int len;
if (!s)
continue;
if (*s == '@') {
ptr = &group;
len = group_len;
s++;
} else {
ptr = &user;
len = user_len;
}
if (!*ptr || strncmp(s, *ptr, len) != 0 || s[len] != ':')
continue;
generate_hash(s+len+1, challenge, pass2);
if (strcmp(pass, pass2) == 0) {
err = NULL;
break;
}
err = "password mismatch";
*ptr = NULL; /* Don't look for name again. */
}
fclose(fh);
force_memzero(line, sizeof line);
force_memzero(pass2, sizeof pass2);
return err;
}
static const char *getpassf(const char *filename)
{
STRUCT_STAT st;
char buffer[512], *p;
int n;
if (!filename)
return NULL;
if (strcmp(filename, "-") == 0) {
n = fgets(buffer, sizeof buffer, stdin) == NULL ? -1 : (int)strlen(buffer);
} else {
int fd;
/* --password-file=PATH client open. Its first line is sent as the
* auth response, so a planted symlink leaks the target's content
* (e.g. shadow hashes) to a malicious daemon; the vfs_stat()
* other-access check runs on the target mode and passes 0640
* root:shadow. Refuse symlinks not owned by uid 0 or our euid. */
if ((fd = vfs_open_owner_walk(filename, O_RDONLY, 0, 0)) < 0) {
rsyserr(FERROR, errno, "could not open password file %s", filename);
exit_cleanup(RERR_SYNTAX);
}
/* fstat the opened fd, not the pathname: a same-object check
* (matching check_secret() above) so an attacker who swaps the
* path between open and check can't make the owner/mode test
* validate a different inode than the one we read the password
* from. */
if (vfs_fstat(fd, &st) == -1) {
rsyserr(FERROR, errno, "fstat(%s)", filename);
exit_cleanup(RERR_SYNTAX);
}
if ((st.st_mode & 06) != 0) {
rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
exit_cleanup(RERR_SYNTAX);
}
if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
exit_cleanup(RERR_SYNTAX);
}
n = read(fd, buffer, sizeof buffer - 1);
close(fd);
}
if (n > 0) {
buffer[n] = '\0';
if ((p = strtok(buffer, "\n\r")) != NULL)
return strdup(p);
}
rprintf(FERROR, "ERROR: failed to read a password from %s\n", filename);
exit_cleanup(RERR_SYNTAX);
}
/* Possibly negotiate authentication with the client. Use "leader" to
* start off the auth if necessary.
*
@@ -226,19 +291,52 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
char *users = lp_auth_users(module);
char challenge[MAX_DIGEST_LEN*2];
char line[BIGPATHBUFLEN];
char secret[512];
char pass2[MAX_DIGEST_LEN*2];
const char **auth_uid_groups = NULL;
int auth_uid_groups_cnt = -1;
const char *err = NULL;
int group_match = -1;
char *tok, *pass;
char opt_ch = '\0';
/* if no auth list then allow anyone in! */
if (!users || !*users)
return "";
negotiate_daemon_auth(f_out, 0);
/* Enforce a configured minimum auth digest (default: none). This refuses
* a peer that negotiated -- or, via an omitted digest list / old protocol,
* fell back to -- a digest weaker than the operator-required floor, e.g. a
* client downgraded to md5/md4. Lower rank == stronger (the auth list is
* ordered strongest-first), so a higher rank than the floor is too weak. */
{
const char *min_digest = lp_auth_digest(module);
if (min_digest && *min_digest) {
int floor_rank = auth_digest_rank(min_digest);
int got_rank = auth_digest_rank(valid_auth_checksums.negotiated_nni->name);
if (floor_rank < 0) {
rprintf(FLOG, "auth failed on module %s from %s (%s): the "
"configured 'auth digest = %s' is not a supported digest "
"on this build\n",
lp_name(module), host, addr, min_digest);
return NULL;
}
if (got_rank < 0 || got_rank > floor_rank) {
rprintf(FLOG, "auth failed on module %s from %s (%s): negotiated "
"auth digest %s is weaker than the required "
"'auth digest = %s'\n",
lp_name(module), host, addr,
valid_auth_checksums.negotiated_nni->name, min_digest);
return NULL;
}
}
}
gen_challenge(addr, challenge);
io_printf(f_out, "%s%s\n", leader, challenge);
if (!read_line_old(f_in, line, sizeof line)
if (!read_line_old(f_in, line, sizeof line, 0)
|| (pass = strchr(line, ' ')) == NULL) {
rprintf(FLOG, "auth failed on module %s from %s (%s): "
"invalid challenge response\n",
@@ -247,40 +345,100 @@ char *auth_server(int f_in, int f_out, int module, const char *host,
}
*pass++ = '\0';
if (!(users = strdup(users)))
out_of_memory("auth_server");
users = strdup(users);
for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
if (wildmatch(tok, line))
break;
/* conf_strtok() honours the documented leading-comma form: a value that
* starts with a comma splits on commas ALONE, so an entry may contain
* spaces -- which is how a group name with a space is written. Splitting
* on whitespace here tore such an entry apart, so the rule the admin wrote
* never matched and a rule they never wrote appeared from its tail. The
* daemon's gid field already uses this parser (clientserver.c). */
for (tok = conf_strtok(users); tok; tok = conf_strtok(NULL)) {
char *opts;
/* See if the user appended :deny, :ro, or :rw. */
if ((opts = strchr(tok, ':')) != NULL) {
*opts++ = '\0';
opt_ch = isUpper(opts) ? toLower(opts) : *opts;
if (opt_ch == 'r') { /* handle ro and rw */
opt_ch = isUpper(opts+1) ? toLower(opts+1) : opts[1];
if (opt_ch == 'o')
opt_ch = 'r';
else if (opt_ch != 'w')
opt_ch = '\0';
} else if (opt_ch != 'd') /* if it's not deny, ignore it */
opt_ch = '\0';
} else
opt_ch = '\0';
if (*tok != '@') {
/* Match the username */
if (wildmatch(tok, line))
break;
} else {
#ifdef HAVE_GETGROUPLIST
int j;
/* See if authorizing user is a real user, and if so, see
* if it is in a group that matches tok+1 wildmat. */
if (auth_uid_groups_cnt < 0) {
item_list gid_list = EMPTY_ITEM_LIST;
uid_t auth_uid;
if (!user_to_uid(line, &auth_uid, False)
|| getallgroups(auth_uid, &gid_list) != NULL)
auth_uid_groups_cnt = 0;
else {
gid_t *gid_array = gid_list.items;
auth_uid_groups_cnt = gid_list.count;
auth_uid_groups = new_array(const char *, auth_uid_groups_cnt);
for (j = 0; j < auth_uid_groups_cnt; j++)
auth_uid_groups[j] = gid_to_group(gid_array[j]);
}
}
for (j = 0; j < auth_uid_groups_cnt; j++) {
if (auth_uid_groups[j] && wildmatch(tok+1, auth_uid_groups[j])) {
group_match = j;
break;
}
}
if (group_match >= 0)
break;
#else
rprintf(FLOG, "your computer doesn't support getgrouplist(), so no @group authorization is possible.\n");
#endif
}
}
free(users);
if (!tok) {
rprintf(FLOG, "auth failed on module %s from %s (%s): "
"unauthorized user\n",
lp_name(module), host, addr);
if (!tok)
err = "no matching rule";
else if (opt_ch == 'd')
err = "denied by rule";
else {
const char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
err = check_secret(module, line, group, challenge, pass);
}
force_memzero(challenge, sizeof challenge);
force_memzero(pass, strlen(pass));
if (auth_uid_groups) {
int j;
for (j = 0; j < auth_uid_groups_cnt; j++) {
if (auth_uid_groups[j])
free((char*)auth_uid_groups[j]);
}
free(auth_uid_groups);
}
if (err) {
rprintf(FLOG, "auth failed on module %s from %s (%s) for %s: %s\n",
lp_name(module), host, addr, line, err);
return NULL;
}
memset(secret, 0, sizeof secret);
if (!get_secret(module, line, secret, sizeof secret - 1)) {
memset(secret, 0, sizeof secret);
rprintf(FLOG, "auth failed on module %s from %s (%s): "
"missing secret for user \"%s\"\n",
lp_name(module), host, addr, line);
return NULL;
}
generate_hash(secret, challenge, pass2);
memset(secret, 0, sizeof secret);
if (strcmp(pass, pass2) != 0) {
rprintf(FLOG, "auth failed on module %s from %s (%s): "
"password mismatch\n",
lp_name(module), host, addr);
return NULL;
}
if (opt_ch == 'r')
read_only = 1;
else if (opt_ch == 'w')
read_only = 0;
return strdup(line);
}
@@ -292,18 +450,19 @@ 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"))) {
/* XXX: cyeoh says that getpass is deprecated, because
* it may return a truncated password on some systems,
* and it is not in the LSB.
*
* Andrew Klein says that getpassphrase() is present
* on Solaris and reads up to 256 characters.
*
* OpenBSD has a readpassphrase() that might be more suitable.
*/
*
* Andrew Klein says that getpassphrase() is present
* on Solaris and reads up to 256 characters.
*
* OpenBSD has a readpassphrase() that might be more suitable.
*/
pass = getpass("Password: ");
}
+358 -274
View File
@@ -2,7 +2,7 @@
* Backup handling code.
*
* Copyright (C) 1999 Andrew Tridgell
* Copyright (C) 2003-2008 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@
*/
#include "rsync.h"
#include "ifuncs.h"
extern int verbose;
extern int am_root;
extern int preserve_acls;
extern int preserve_xattrs;
@@ -34,329 +34,413 @@ extern char backup_dir_buf[MAXPATHLEN];
extern char *backup_suffix;
extern char *backup_dir;
/* make a complete pathname for backup file */
/* Pin a backup SOURCE leaf with a confined O_NOFOLLOW fd (via the operator
* owner-walk resolver, like set_file_attrs's op_leaf_fd) so the ACL/xattr the
* backup caches off it are read through the held fd -- a parent-symlink race
* can't redirect the read out of the module. Returns -1 for a non-hardened
* receiver (caller path-reads) or for a raced/absent leaf on a hardened one
* (caller skips the cache rather than read through a flippable path; use
* backup_metadata_hardened() to tell the two -1 cases apart). */
int backup_metadata_hardened(void)
{
return vfs_relpath_active() && !vfs_symlink_optout_allowed();
}
int backup_source_fd(const char *path)
{
#if defined AT_FDCWD && defined O_NOFOLLOW
if (backup_metadata_hardened() && path && *path)
return vfs_open_at(path, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_CLOEXEC, 0, VFS_OPERATOR_PATH);
#endif
return -1;
}
/* Returns -1 on error, 0 on missing dir, and 1 on present dir. */
static int validate_backup_dir(void)
{
STRUCT_STAT st;
if (vfs_lstat(VFS_AT_FDCWD, backup_dir_buf, &st, VFS_OPERATOR_PATH) < 0) {
if (errno == ENOENT)
return 0;
rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
return -1;
}
if (!S_ISDIR(st.st_mode)) {
int flags = get_del_for_flag(st.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
if (delete_item(backup_dir_buf, st.st_mode, flags) == 0)
return 0;
return -1;
}
return 1;
}
/* Create a backup path from the given fname, putting the result into
* backup_dir_buf. Any new directories (compared to the prior backup
* path) are ensured to exist as directories, replacing anything else
* that may be in the way (e.g. a symlink). */
static BOOL copy_valid_path(const char *fname)
{
const char *f;
int val;
BOOL ret = True;
stat_x sx;
char *b, *rel = backup_dir_buf + backup_dir_len, *name = rel;
for (f = fname, b = rel; *f && *f == *b; f++, b++) {
if (*b == '/')
name = b + 1;
}
if (stringjoin(rel, backup_dir_remainder, fname, backup_suffix, NULL) >= backup_dir_remainder) {
rprintf(FERROR, "backup filename too long\n");
*name = '\0';
return False;
}
for ( ; ; name = b + 1) {
if ((b = strchr(name, '/')) == NULL)
return True;
*b = '\0';
val = validate_backup_dir();
if (val == 0)
break;
if (val < 0) {
*name = '\0';
return False;
}
*b = '/';
}
init_stat_x(&sx);
for ( ; b; name = b + 1, b = strchr(name, '/')) {
*b = '\0';
while (vfs_mkdir(VFS_AT_FDCWD, backup_dir_buf, ACCESSPERMS, VFS_OPERATOR_PATH) < 0) {
if (errno == EEXIST) {
val = validate_backup_dir();
if (val > 0)
break;
if (val == 0)
continue;
} else
rsyserr(FERROR, errno, "backup mkdir %s failed", backup_dir_buf);
*name = '\0';
ret = False;
goto cleanup;
}
/* Try to transfer the directory settings of the actual dir
* that the files are coming from. */
if (x_stat(rel, &sx.st, NULL, VFS_OPERATOR_PATH) < 0)
rsyserr(FERROR, errno, "backup stat %s failed", full_fname(rel));
else {
struct file_struct *file;
if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
continue;
#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
{ /* read the source dir's ACL/xattr through a confined fd */
int bfd = backup_source_fd(rel);
if (!backup_metadata_hardened() || bfd >= 0) {
# ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
get_acl_fdat(bfd, -1, NULL, rel, &sx);
cache_tmp_acl(file, &sx);
free_acl(&sx);
}
# endif
# ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
get_xattr(rel, bfd, &sx);
cache_tmp_xattr(file, &sx);
free_xattr(&sx);
}
# endif
}
if (bfd >= 0)
close(bfd);
}
#endif
set_file_attrs(backup_dir_buf, file, NULL, NULL, ATTRS_OPERATOR_PATH);
unmake_file(file);
}
*b = '/';
}
cleanup:
#ifdef SUPPORT_ACLS
uncache_tmp_acls();
#endif
#ifdef SUPPORT_XATTRS
uncache_tmp_xattrs();
#endif
return ret;
}
/* Make a complete pathname for backup file and verify any new path elements. */
char *get_backup_name(const char *fname)
{
if (backup_dir) {
if (stringjoin(backup_dir_buf + backup_dir_len, backup_dir_remainder,
fname, backup_suffix, NULL) < backup_dir_remainder)
return backup_dir_buf;
} else {
if (stringjoin(backup_dir_buf, MAXPATHLEN,
fname, backup_suffix, NULL) < MAXPATHLEN)
static int initialized = 0;
if (!initialized) {
char dirbuf[MAXPATHLEN];
int ret;
if (strlcpy(dirbuf, backup_dir_buf, sizeof dirbuf) >= sizeof dirbuf) {
errno = ENAMETOOLONG;
return NULL;
}
if (backup_dir_len > 1)
dirbuf[backup_dir_len-1] = '\0';
ret = vfs_make_path(dirbuf, 0, VFS_OPERATOR_PATH);
if (ret < 0)
return NULL;
initialized = 1;
}
/* copy fname into backup_dir_buf while validating the dirs. */
if (copy_valid_path(fname))
return backup_dir_buf;
/* copy_valid_path() has printed an error message. */
return NULL;
}
if (stringjoin(backup_dir_buf, MAXPATHLEN, fname, backup_suffix, NULL) < MAXPATHLEN)
return backup_dir_buf;
rprintf(FERROR, "backup filename too long\n");
return NULL;
}
/* simple backup creates a backup with a suffix in the same directory */
static int make_simple_backup(const char *fname)
/* Has same return codes as make_backup(). */
static inline int link_or_rename(const char *from, const char *to,
BOOL prefer_rename, STRUCT_STAT *stp)
{
int rename_errno;
const char *fnamebak = get_backup_name(fname);
if (!fnamebak)
return 0;
while (1) {
if (do_rename(fname, fnamebak) == 0) {
if (verbose > 1) {
rprintf(FINFO, "backed up %s to %s\n",
fname, fnamebak);
}
break;
}
/* cygwin (at least version b19) reports EINVAL */
if (errno == ENOENT || errno == EINVAL)
break;
rename_errno = errno;
if (errno == EISDIR && do_rmdir(fnamebak) == 0)
continue;
if (errno == ENOTDIR && do_unlink(fnamebak) == 0)
continue;
rsyserr(FERROR, rename_errno, "rename %s to backup %s",
fname, fnamebak);
errno = rename_errno;
return 0;
}
return 1;
}
/****************************************************************************
Create a directory given an absolute path, perms based upon another directory
path
****************************************************************************/
int make_bak_dir(const char *fullpath)
{
char fbuf[MAXPATHLEN], *rel, *end, *p;
struct file_struct *file;
int len = backup_dir_len;
stat_x sx;
while (*fullpath == '.' && fullpath[1] == '/') {
fullpath += 2;
len -= 2;
}
if (strlcpy(fbuf, fullpath, sizeof fbuf) >= sizeof fbuf)
return -1;
rel = fbuf + len;
end = p = rel + strlen(rel);
/* Try to find an existing dir, starting from the deepest dir. */
while (1) {
if (--p == fbuf)
return -1;
if (*p == '/') {
*p = '\0';
if (mkdir_defmode(fbuf) == 0)
break;
if (errno != ENOENT) {
rsyserr(FERROR, errno,
"make_bak_dir mkdir %s failed",
full_fname(fbuf));
return -1;
}
}
}
/* Make all the dirs that we didn't find on the way here. */
while (1) {
if (p >= rel) {
/* Try to transfer the directory settings of the
* actual dir that the files are coming from. */
if (x_stat(rel, &sx.st, NULL) < 0) {
rsyserr(FERROR, errno,
"make_bak_dir stat %s failed",
full_fname(rel));
} else {
#ifdef SUPPORT_ACLS
sx.acc_acl = sx.def_acl = NULL;
#ifdef SUPPORT_HARD_LINKS
if (!prefer_rename) {
#ifndef CAN_HARDLINK_SYMLINK
if (S_ISLNK(stp->st_mode))
return 0; /* Use copy code. */
#endif
#ifdef SUPPORT_XATTRS
sx.xattr = NULL;
#ifndef CAN_HARDLINK_SPECIAL
if (IS_SPECIAL(stp->st_mode) || IS_DEVICE(stp->st_mode))
return 0; /* Use copy code. */
#endif
if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
continue;
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
get_acl(rel, &sx);
cache_acl(file, &sx);
free_acl(&sx);
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
get_xattr(rel, &sx);
cache_xattr(file, &sx);
free_xattr(&sx);
}
#endif
set_file_attrs(fbuf, file, NULL, NULL, 0);
unmake_file(file);
}
}
*p = '/';
p += strlen(p);
if (p == end)
break;
if (mkdir_defmode(fbuf) < 0) {
rsyserr(FERROR, errno, "make_bak_dir mkdir %s failed",
full_fname(fbuf));
return -1;
/* from = the live dest file being backed up (a transfer path); to = the
* --backup-dir path (operator). Per-operand policy keeps the transfer
* source under the secure receiver resolve and only owner-walks the
* operator backup parent. */
if (vfs_link_at(from, to, 0, VFS_OPERATOR_PATH) == 0) {
if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: HLINK %s successful.\n", from);
return 2;
}
/* We prefer to rename a regular file rather than copy it. */
if (!S_ISREG(stp->st_mode) || errno == EEXIST || errno == EISDIR)
return 0;
}
return 0;
}
/* robustly move a file, creating new directory structures if necessary */
static int robust_move(const char *src, char *dst)
{
if (robust_rename(src, dst, NULL, 0755) < 0) {
int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
if (errno == ENOENT && make_bak_dir(dst) == 0) {
if (robust_rename(src, dst, NULL, 0755) < 0)
save_errno = errno ? errno : save_errno;
else
save_errno = 0;
#endif
if (vfs_rename_at(from, to, 0, VFS_OPERATOR_PATH) == 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! from is the
* transfer-side source, so unlink it under the secure resolve (0). */
robust_unlink(from, 0); /* Just in case... */
}
if (save_errno) {
errno = save_errno;
return -1;
}
}
return 0;
}
/* If we have a --backup-dir, then we get here from make_backup().
* We will move the file to be deleted into a parallel directory tree. */
static int keep_backup(const char *fname)
{
stat_x sx;
struct file_struct *file;
char *buf;
int save_preserve_xattrs = preserve_xattrs;
int kept = 0;
int ret_code;
/* return if no file to keep */
if (x_lstat(fname, &sx.st, NULL) < 0)
if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: RENAME %s successful.\n", from);
return 1;
#ifdef SUPPORT_ACLS
sx.acc_acl = sx.def_acl = NULL;
#endif
#ifdef SUPPORT_XATTRS
sx.xattr = NULL;
#endif
}
return 0;
}
if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
return 1; /* the file could have disappeared */
/* Hard-link, rename, or copy an item to the backup name. Returns 0 for
* failure, 1 if item was moved, 2 if item was duplicated or hard linked
* into backup area, or 3 if item doesn't exist or isn't a regular file. */
static int make_backup_inner(const char *fname, BOOL prefer_rename)
{
stat_x sx;
struct file_struct *file;
int save_preserve_xattrs;
char *buf;
int ret = 0;
if (!(buf = get_backup_name(fname))) {
unmake_file(file);
init_stat_x(&sx);
/* Return success if no file to keep. */
if (x_lstat(fname, &sx.st, NULL, VFS_OPERATOR_PATH) < 0)
return 3;
if (!(buf = get_backup_name(fname)))
return 0;
}
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
get_acl(fname, &sx);
cache_acl(file, &sx);
free_acl(&sx);
#ifdef SUPPORT_LINKS
/* Honor --safe-links BEFORE the hard-link / rename fast path. When
* CAN_HARDLINK_SYMLINK is defined, link_or_rename() would otherwise
* hard-link an escaping symlink (e.g. ../../etc/passwd) into the backup
* area and "goto success", skipping the safe_symlinks check in the
* copy-fallback path below -- silently preserving an unsafe link that
* --safe-links was meant to drop. Match the copy path: don't back up an
* unsafe symlink. */
if (preserve_links && S_ISLNK(sx.st.st_mode) && safe_symlinks) {
char lnkbuf[MAXPATHLEN];
int llen = vfs_readlink(fname, lnkbuf, MAXPATHLEN - 1);
/* A failed readlink means we can't verify the target, so fail
* closed: skip the backup rather than let the hard-link fast path
* preserve a possibly-unsafe symlink unchecked. */
if (llen <= 0) {
if (INFO_GTE(SYMSAFE, 1))
rprintf(FINFO, "not backing up symlink with unreadable target \"%s\"\n", fname);
ret = 2;
goto success;
}
lnkbuf[llen] = '\0';
if (unsafe_symlink(lnkbuf, fname)) {
if (INFO_GTE(SYMSAFE, 1)) {
rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n",
fname, lnkbuf);
}
ret = 2;
goto success;
}
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
get_xattr(fname, &sx);
cache_xattr(file, &sx);
free_xattr(&sx);
/* Try a hard-link or a rename first. Using rename is not atomic, but
* is more efficient than forcing a copy for larger files when no hard-
* linking is possible. */
if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
goto success;
if (errno == EEXIST || errno == EISDIR) {
STRUCT_STAT bakst;
if (vfs_lstat(VFS_AT_FDCWD, buf, &bakst, VFS_OPERATOR_PATH) == 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;
}
if ((ret = link_or_rename(fname, buf, prefer_rename, &sx.st)) != 0)
goto success;
}
/* Fall back to making a copy. */
if (!(file = make_file(fname, NULL, &sx.st, 0, NO_FILTERS)))
return 3; /* the file could have disappeared */
#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
{ /* read the source file's ACL/xattr through a confined fd */
int bfd = backup_source_fd(fname);
if (!backup_metadata_hardened() || bfd >= 0) {
# ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
get_acl_fdat(bfd, -1, NULL, fname, &sx);
cache_tmp_acl(file, &sx);
free_acl(&sx);
}
# endif
# ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
get_xattr(fname, bfd, &sx);
cache_tmp_xattr(file, &sx);
free_xattr(&sx);
}
# endif
}
if (bfd >= 0)
close(bfd);
}
#endif
/* 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))) {
int save_errno;
do_unlink(buf);
if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0) {
save_errno = errno ? errno : EINVAL; /* 0 paranoia */
if (errno == ENOENT && make_bak_dir(buf) == 0) {
if (do_mknod(buf, file->mode, sx.st.st_rdev) < 0)
save_errno = errno ? errno : save_errno;
else
save_errno = 0;
}
if (save_errno) {
rsyserr(FERROR, save_errno, "mknod %s failed",
full_fname(buf));
}
} else
save_errno = 0;
if (verbose > 2 && save_errno == 0) {
rprintf(FINFO, "make_backup: DEVICE %s successful.\n",
fname);
}
kept = 1;
do_unlink(fname);
}
if (!kept && S_ISDIR(file->mode)) {
/* make an empty directory */
if (do_mkdir(buf, file->mode) < 0) {
int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
if (errno == ENOENT && make_bak_dir(buf) == 0) {
if (do_mkdir(buf, file->mode) < 0)
save_errno = errno ? errno : save_errno;
else
save_errno = 0;
}
if (save_errno) {
rsyserr(FINFO, save_errno, "mkdir %s failed",
full_fname(buf));
}
}
ret_code = do_rmdir(fname);
if (verbose > 2) {
rprintf(FINFO, "make_backup: RMDIR %s returns %i\n",
full_fname(fname), ret_code);
}
kept = 1;
if (vfs_mknod(VFS_AT_FDCWD, buf, file->mode, sx.st.st_rdev, VFS_OPERATOR_PATH) < 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);
ret = 2;
}
#ifdef SUPPORT_LINKS
if (!kept && preserve_links && S_ISLNK(file->mode)) {
if (!ret && preserve_links && S_ISLNK(file->mode)) {
const char *sl = F_SYMLINK(file);
if (safe_symlinks && unsafe_symlink(sl, buf)) {
if (verbose) {
rprintf(FINFO, "ignoring unsafe symlink %s -> %s\n",
full_fname(buf), sl);
if (safe_symlinks && unsafe_symlink(sl, fname)) {
if (INFO_GTE(SYMSAFE, 1)) {
rprintf(FINFO, "not backing up unsafe symlink \"%s\" -> \"%s\"\n",
fname, sl);
}
kept = 1;
ret = 2;
} else {
do_unlink(buf);
if (do_symlink(sl, buf) < 0) {
int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
if (errno == ENOENT && make_bak_dir(buf) == 0) {
if (do_symlink(sl, buf) < 0)
save_errno = errno ? errno : save_errno;
else
save_errno = 0;
}
if (save_errno) {
rsyserr(FERROR, save_errno, "link %s -> \"%s\"",
full_fname(buf), sl);
}
}
do_unlink(fname);
kept = 1;
if (vfs_symlink(sl, VFS_AT_FDCWD, buf, VFS_OPERATOR_PATH) < 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);
ret = 2;
}
}
#endif
if (!kept && !S_ISREG(file->mode)) {
rprintf(FINFO, "make_bak: skipping non-regular file %s\n",
fname);
if (!ret && !S_ISREG(file->mode)) {
if (INFO_GTE(NONREG, 1))
rprintf(FINFO, "make_bak: skipping non-regular file %s\n", fname);
unmake_file(file);
return 1;
#ifdef SUPPORT_ACLS
uncache_tmp_acls();
#endif
#ifdef SUPPORT_XATTRS
uncache_tmp_xattrs();
#endif
return 3;
}
/* move to keep tree if a file */
if (!kept) {
if (robust_move(fname, buf) != 0) {
/* Copy to backup tree if a file. */
if (!ret) {
if (copy_file(fname, buf, -1, file->mode, VFS_OPERATOR_PATH) < 0) {
rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
full_fname(fname), buf);
} else if (sx.st.st_nlink > 1) {
/* If someone has hard-linked the file into the backup
* dir, rename() might return success but do nothing! */
robust_unlink(fname); /* Just in case... */
unmake_file(file);
#ifdef SUPPORT_ACLS
uncache_tmp_acls();
#endif
#ifdef SUPPORT_XATTRS
uncache_tmp_xattrs();
#endif
return 0;
}
if (DEBUG_GTE(BACKUP, 1))
rprintf(FINFO, "make_backup: COPY %s successful.\n", fname);
ret = 2;
}
save_preserve_xattrs = preserve_xattrs;
preserve_xattrs = 0;
set_file_attrs(buf, file, NULL, fname, 0);
set_file_attrs(buf, file, NULL, fname, ATTRS_OPERATOR_PATH | ATTRS_ACCURATE_TIME);
preserve_xattrs = save_preserve_xattrs;
unmake_file(file);
#ifdef SUPPORT_ACLS
uncache_tmp_acls();
#endif
#ifdef SUPPORT_XATTRS
uncache_tmp_xattrs();
#endif
if (verbose > 1) {
rprintf(FINFO, "backed up %s to %s\n",
fname, buf);
}
return 1;
success:
if (INFO_GTE(BACKUP, 1))
rprintf(FINFO, "backed up %s to %s\n", fname, buf);
return ret;
}
/* main backup switch routine */
int make_backup(const char *fname)
int make_backup(const char *fname, BOOL prefer_rename)
{
if (backup_dir)
return keep_backup(fname);
return make_simple_backup(fname);
int ret;
/* The --backup-dir is an operator-supplied path: resolve it (and the
* tail/rename beneath it) with the ownership walk so a foreign-owned
* symlink component is refused while the operator's own is followed --
* absolute and relative alike. --insecure-links / "insecure links ="
* restores legacy following. */
ret = make_backup_inner(fname, prefer_rename);
return ret;
}
+144 -62
View File
@@ -3,7 +3,7 @@
*
* Copyright (C) 1999 Weiss
* Copyright (C) 2004 Chris Shoemaker
* Copyright (C) 2004-2008 Wayne Davison
* Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
*/
#include "rsync.h"
#include "zlib/zlib.h"
#include <zlib.h>
#include <time.h>
extern int eol_nulls;
@@ -37,14 +37,19 @@ extern int always_checksum;
extern int do_compression;
extern int inplace;
extern int append_mode;
extern int write_batch;
extern int protocol_version;
extern int raw_argc, cooked_argc;
extern char **raw_argv, **cooked_argv;
extern char *batch_name;
#ifdef ICONV_OPTION
extern char *iconv_opt;
#endif
extern struct filter_list_struct filter_list;
extern filter_rule_list filter_list;
int batch_fd = -1;
int batch_sh_fd = -1;
int batch_stream_flags;
static int tweaked_append;
@@ -70,7 +75,7 @@ static int *flag_ptr[] = {
NULL
};
static char *flag_name[] = {
static const char *const flag_name[] = {
"--recurse (-r)",
"--owner (-o)",
"--group (-g)",
@@ -135,7 +140,7 @@ void check_batch_flags(void)
set ? "Please" : "Do not");
exit_cleanup(RERR_SYNTAX);
}
if (verbose) {
if (INFO_GTE(MISC, 1)) {
rprintf(FINFO,
"%sing the %s option to match the batchfile.\n",
set ? "Sett" : "Clear", flag_name[i]);
@@ -156,40 +161,76 @@ void check_batch_flags(void)
append_mode = 2;
}
static void write_arg(int fd, char *arg)
static int write_arg(const char *arg)
{
char *x, *s;
const char *x, *s;
int len, err = 0;
/* Emit a "--opt=" prefix unquoted only when it is a plain option token;
* a metacharacter before '=' (an attacker-shaped arg) must be quoted
* along with the rest, or it would run raw in the replay script. */
if (*arg == '-' && (x = strchr(arg, '=')) != NULL) {
write(fd, arg, x - arg + 1);
arg += x - arg + 1;
}
if (strpbrk(arg, " \"'&;|[]()$#!*?^\\") != NULL) {
write(fd, "'", 1);
for (s = arg; (x = strchr(s, '\'')) != NULL; s = x + 1) {
write(fd, s, x - s + 1);
write(fd, "'", 1);
const char *p = arg;
while (p < x && (*p == '-' || *p == '_'
|| (*p >= '0' && *p <= '9')
|| (*p >= 'A' && *p <= 'Z')
|| (*p >= 'a' && *p <= 'z')))
p++;
if (p == x) {
err |= write(batch_sh_fd, arg, x - arg + 1) != x - arg + 1;
arg += x - arg + 1;
}
write(fd, s, strlen(s));
write(fd, "'", 1);
return;
}
write(fd, arg, strlen(arg));
/* Single-quote unconditionally so every shell metacharacter (backtick,
* newline, redirection, ...) stays literal in the replay script. An
* embedded ' is emitted as the '\'' close/escape/reopen sequence. */
err |= write(batch_sh_fd, "'", 1) != 1;
for (s = arg; (x = strchr(s, '\'')) != NULL; s = x + 1) {
err |= write(batch_sh_fd, s, x - s) != x - s;
err |= write(batch_sh_fd, "'\\''", 4) != 4;
}
len = strlen(s);
err |= write(batch_sh_fd, s, len) != len;
err |= write(batch_sh_fd, "'", 1) != 1;
return err;
}
/* Writes out a space and then an option (or other string) with an optional "=" + arg suffix. */
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;
if (arg) {
err |= write(batch_sh_fd, "=", 1) != 1;
err |= write_arg(arg);
}
return err;
}
static void write_filter_rules(int fd)
{
struct filter_struct *ent;
filter_rule *ent;
write_sbuf(fd, " <<'#E#'\n");
for (ent = filter_list.head; ent; ent = ent->next) {
unsigned int plen;
char *p = get_rule_prefix(ent->match_flags, "- ", 0, &plen);
char *p = get_rule_prefix(ent, "- ", 0, &plen);
/* A filter pattern is one here-doc line; an embedded newline would let
* a crafted pattern (e.g. from a dir-merge/--exclude-from file in an
* untrusted tree) forge the "#E#" terminator on its own line and inject
* shell commands into the generated replay script. Such a pattern also
* can't round-trip the line-delimited here-doc, so refuse it fail-closed
* rather than emit an injectable script. */
if (ent->pattern && strchr(ent->pattern, '\n')) {
rprintf(FERROR, "cannot write a filter rule containing a newline to the batch replay script\n");
exit_cleanup(RERR_SYNTAX);
}
write_buf(fd, p, plen);
write_sbuf(fd, ent->pattern);
if (ent->match_flags & MATCHFLG_DIRECTORY)
if (ent->rflags & FILTRULE_DIRECTORY)
write_byte(fd, '/');
write_byte(fd, eol_nulls ? 0 : '\n');
}
@@ -198,40 +239,84 @@ static void write_filter_rules(int fd)
write_sbuf(fd, "#E#");
}
/* This sets batch_fd and (for --write-batch) batch_sh_fd. */
void open_batch_files(void)
{
/* --write-batch/--read-batch are operator-supplied; a planted symlink
* could truncate+overwrite an arbitrary file (write side) or stream
* attacker bytes into the protocol parser (read side). Refuse symlinks
* not owned by uid 0 or our euid anywhere in the path. */
if (write_batch) {
char filename[MAXPATHLEN];
stringjoin(filename, sizeof filename, batch_name, ".sh", NULL);
batch_sh_fd = vfs_open_owner_walk(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR | S_IXUSR, 0);
if (batch_sh_fd < 0) {
rsyserr(FERROR, errno, "Batch file %s open error", full_fname(filename));
exit_cleanup(RERR_FILESELECT);
}
/* O_BINARY: the batch stream is binary protocol data; without it
* Cygwin et al apply CRLF translation and corrupt it. Unlike
* vfs_open(), vfs_open_owner_walk passes flags verbatim. */
batch_fd = vfs_open_owner_walk(batch_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR, 0);
} else if (strcmp(batch_name, "-") == 0)
batch_fd = STDIN_FILENO;
else
batch_fd = vfs_open_owner_walk(batch_name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR, 0);
if (batch_fd < 0) {
rsyserr(FERROR, errno, "Batch file %s open error", full_fname(batch_name));
exit_cleanup(RERR_FILEIO);
}
/* --read-batch: the file's bytes drive the protocol parser, so refuse
* non-regular files (FIFO, device, socket) at the batch path. */
if (!write_batch && batch_fd != STDIN_FILENO) {
STRUCT_STAT st;
if (vfs_fstat(batch_fd, &st) == 0 && !S_ISREG(st.st_mode)) {
rprintf(FERROR, "Batch file %s is not a regular file\n",
full_fname(batch_name));
exit_cleanup(RERR_FILEIO);
}
}
}
/* This routine tries to write out an equivalent --read-batch command
* given the user's --write-batch args. However, it doesn't really
* understand most of the options, so it uses some overly simple
* heuristics to munge the command line into something that will
* (hopefully) work. */
void write_batch_shell_file(int argc, char *argv[], int file_arg_cnt)
void write_batch_shell_file(void)
{
int fd, i, len;
char *p, filename[MAXPATHLEN];
stringjoin(filename, sizeof filename,
batch_name, ".sh", NULL);
fd = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IEXEC);
if (fd < 0) {
rsyserr(FERROR, errno, "Batch file %s open error",
filename);
exit_cleanup(RERR_FILESELECT);
}
int i, j, len, err = 0;
char *p, *p2;
/* Write argvs info to BATCH.sh file */
write_arg(fd, argv[0]);
err |= write_arg(raw_argv[0]);
if (filter_list.head) {
if (protocol_version >= 29)
write_sbuf(fd, " --filter=._-");
err |= write_opt("--filter", "._-");
else
write_sbuf(fd, " --exclude-from=-");
err |= write_opt("--exclude-from", "-");
}
for (i = 1; i < argc - file_arg_cnt; i++) {
p = argv[i];
/* Elide the filename args from the option list, but scan for them in reverse. */
for (i = raw_argc-1, j = cooked_argc-1; i > 0 && j >= 0; i--) {
if (strcmp(raw_argv[i], cooked_argv[j]) == 0) {
raw_argv[i] = NULL;
j--;
}
}
for (i = 1; i < raw_argc; i++) {
if (!(p = raw_argv[i]))
continue;
if (strncmp(p, "--files-from", 12) == 0
|| strncmp(p, "--filter", 8) == 0
|| strncmp(p, "--include", 9) == 0
|| strncmp(p, "--exclude", 9) == 0) {
|| strncmp(p, "--filter", 8) == 0
|| strncmp(p, "--include", 9) == 0
|| strncmp(p, "--exclude", 9) == 0) {
if (strchr(p, '=') == NULL)
i++;
continue;
@@ -240,27 +325,24 @@ void write_batch_shell_file(int argc, char *argv[], int file_arg_cnt)
i++;
continue;
}
write(fd, " ", 1);
if (strncmp(p, "--write-batch", len = 13) == 0
|| strncmp(p, "--only-write-batch", len = 18) == 0) {
write(fd, "--read-batch", 12);
if (p[len] == '=') {
write(fd, "=", 1);
write_arg(fd, p + len + 1);
}
} else
write_arg(fd, p);
|| strncmp(p, "--only-write-batch", len = 18) == 0)
err |= write_opt("--read-batch", p[len] == '=' ? p + len + 1 : NULL);
else {
err |= write(batch_sh_fd, " ", 1) != 1;
err |= write_arg(p);
}
}
if (!(p = check_for_hostspec(argv[argc - 1], &p, &i)))
p = argv[argc - 1];
write(fd, " ${1:-", 6);
write_arg(fd, p);
write_byte(fd, '}');
if (!(p = check_for_hostspec(cooked_argv[cooked_argc - 1], &p2, &i)))
p = cooked_argv[cooked_argc - 1];
err |= write_opt("${1:-", NULL);
err |= write_arg(p);
err |= write(batch_sh_fd, "}", 1) != 1;
if (filter_list.head)
write_filter_rules(fd);
if (write(fd, "\n", 1) != 1 || close(fd) < 0) {
rsyserr(FERROR, errno, "Batch file %s write error",
filename);
write_filter_rules(batch_sh_fd);
if (write(batch_sh_fd, "\n", 1) != 1 || close(batch_sh_fd) < 0 || err) {
rsyserr(FERROR, errno, "Batch file %s.sh write error", batch_name);
exit_cleanup(RERR_FILEIO);
}
batch_sh_fd = -1;
}
+126 -16
View File
@@ -2,7 +2,7 @@
* Simple byteorder handling.
*
* Copyright (C) 1992-1995 Andrew Tridgell
* Copyright (C) 2007-2008 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
@@ -22,7 +22,7 @@
/* We know that the x86 can handle misalignment and has the same
* byte order (LSB-first) as the 32-bit numbers we transmit. */
#ifdef __i386__
#if defined __i386__ || defined __i486__ || defined __i586__ || defined __i686__ || __amd64
#define CAREFUL_ALIGNMENT 0
#endif
@@ -32,21 +32,131 @@
#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
#define UVAL(buf,pos) ((uint32)CVAL(buf,pos))
#define SCVAL(buf,pos,val) (CVAL(buf,pos) = (val))
#if CAREFUL_ALIGNMENT
#define PVAL(buf,pos) (UVAL(buf,pos)|UVAL(buf,(pos)+1)<<8)
#define IVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+2)<<16)
#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
#else
/* this handles things for architectures like the 386 that can handle
alignment errors */
/*
WARNING: This section is dependent on the length of int32
being correct. set CAREFUL_ALIGNMENT if it is not.
static inline uint32
IVALu(const uchar *buf, int pos)
{
return UVAL(buf, pos)
| UVAL(buf, pos + 1) << 8
| UVAL(buf, pos + 2) << 16
| UVAL(buf, pos + 3) << 24;
}
static inline void
SIVALu(uchar *buf, int pos, uint32 val)
{
CVAL(buf, pos) = val;
CVAL(buf, pos + 1) = val >> 8;
CVAL(buf, pos + 2) = val >> 16;
CVAL(buf, pos + 3) = val >> 24;
}
static inline int64
IVAL64(const char *buf, int pos)
{
return IVALu((uchar*)buf, pos) | (int64)IVALu((uchar*)buf, pos + 4) << 32;
}
static inline void
SIVAL64(char *buf, int pos, int64 val)
{
SIVALu((uchar*)buf, pos, val);
SIVALu((uchar*)buf, pos + 4, val >> 32);
}
#else /* !CAREFUL_ALIGNMENT */
/* We don't want false positives about alignment from UBSAN, see:
https://github.com/WayneD/rsync/issues/427#issuecomment-1375132291
*/
#define IVAL(buf,pos) (*(uint32 *)((char *)(buf) + (pos)))
#define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32)(val))
/* From https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html */
#ifndef GCC_VERSION
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#endif
/* This handles things for architectures like the 386 that can handle alignment errors.
* WARNING: This section is dependent on the length of an int32 (and thus a uint32)
* being correct (4 bytes)! Set CAREFUL_ALIGNMENT if it is not. */
#ifdef __clang__
__attribute__((no_sanitize("undefined")))
#elif GCC_VERSION >= 409
__attribute__((no_sanitize_undefined))
#endif
static inline uint32
IVALu(const uchar *buf, int pos)
{
union {
const uchar *b;
const uint32 *num;
} u;
u.b = buf + pos;
return *u.num;
}
#ifdef __clang__
__attribute__((no_sanitize("undefined")))
#elif GCC_VERSION >= 409
__attribute__((no_sanitize_undefined))
#endif
static inline void
SIVALu(uchar *buf, int pos, uint32 val)
{
union {
uchar *b;
uint32 *num;
} u;
u.b = buf + pos;
*u.num = val;
}
#ifdef __clang__
__attribute__((no_sanitize("undefined")))
#elif GCC_VERSION >= 409
__attribute__((no_sanitize_undefined))
#endif
static inline int64
IVAL64(const char *buf, int pos)
{
union {
const char *b;
const int64 *num;
} u;
u.b = buf + pos;
return *u.num;
}
#ifdef __clang__
__attribute__((no_sanitize("undefined")))
#elif GCC_VERSION >= 409
__attribute__((no_sanitize_undefined))
#endif
static inline void
SIVAL64(char *buf, int pos, int64 val)
{
union {
char *b;
int64 *num;
} u;
u.b = buf + pos;
*u.num = val;
}
#endif /* !CAREFUL_ALIGNMENT */
static inline uint32
IVAL(const char *buf, int pos)
{
return IVALu((uchar*)buf, pos);
}
static inline void
SIVAL(char *buf, int pos, uint32 val)
{
SIVALu((uchar*)buf, pos, val);
}
+54 -41
View File
@@ -1,7 +1,7 @@
/*
* End-of-run cleanup helper code used by cleanup.c.
* Allow an arbitrary sequence of case labels.
*
* Copyright (C) 2006-2008 Wayne Davison
* Copyright (C) 2006-2020 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
@@ -17,63 +17,76 @@
* with this program; if not, visit the http://fsf.org website.
*/
/* This is included by cleanup.c multiple times, once for every segement in
* the _exit_cleanup() code. This produces the next "case N:" statement in
* sequence and increments the cleanup_step variable by 1. This ensures that
* our case statements never get out of whack due to added/removed steps. */
/* This is included multiple times, once for every segment in a switch statement.
* This produces the next "case N:" statement in sequence. */
#if !defined EXIT_CLEANUP_CASE_0
#define EXIT_CLEANUP_CASE_0
#if !defined CASE_N_STATE_0
#define CASE_N_STATE_0
case 0:
#elif !defined EXIT_CLEANUP_CASE_1
#define EXIT_CLEANUP_CASE_1
#elif !defined CASE_N_STATE_1
#define CASE_N_STATE_1
/* FALLTHROUGH */
case 1:
#elif !defined EXIT_CLEANUP_CASE_2
#define EXIT_CLEANUP_CASE_2
#elif !defined CASE_N_STATE_2
#define CASE_N_STATE_2
/* FALLTHROUGH */
case 2:
#elif !defined EXIT_CLEANUP_CASE_3
#define EXIT_CLEANUP_CASE_3
#elif !defined CASE_N_STATE_3
#define CASE_N_STATE_3
/* FALLTHROUGH */
case 3:
#elif !defined EXIT_CLEANUP_CASE_4
#define EXIT_CLEANUP_CASE_4
#elif !defined CASE_N_STATE_4
#define CASE_N_STATE_4
/* FALLTHROUGH */
case 4:
#elif !defined EXIT_CLEANUP_CASE_5
#define EXIT_CLEANUP_CASE_5
#elif !defined CASE_N_STATE_5
#define CASE_N_STATE_5
/* FALLTHROUGH */
case 5:
#elif !defined EXIT_CLEANUP_CASE_6
#define EXIT_CLEANUP_CASE_6
#elif !defined CASE_N_STATE_6
#define CASE_N_STATE_6
/* FALLTHROUGH */
case 6:
#elif !defined EXIT_CLEANUP_CASE_7
#define EXIT_CLEANUP_CASE_7
#elif !defined CASE_N_STATE_7
#define CASE_N_STATE_7
/* FALLTHROUGH */
case 7:
#elif !defined EXIT_CLEANUP_CASE_8
#define EXIT_CLEANUP_CASE_8
#elif !defined CASE_N_STATE_8
#define CASE_N_STATE_8
/* FALLTHROUGH */
case 8:
#elif !defined EXIT_CLEANUP_CASE_9
#define EXIT_CLEANUP_CASE_9
#elif !defined CASE_N_STATE_9
#define CASE_N_STATE_9
/* FALLTHROUGH */
case 9:
#elif !defined EXIT_CLEANUP_CASE_10
#define EXIT_CLEANUP_CASE_10
#elif !defined CASE_N_STATE_10
#define CASE_N_STATE_10
/* FALLTHROUGH */
case 10:
#elif !defined EXIT_CLEANUP_CASE_11
#define EXIT_CLEANUP_CASE_11
#elif !defined CASE_N_STATE_11
#define CASE_N_STATE_11
/* FALLTHROUGH */
case 11:
#elif !defined EXIT_CLEANUP_CASE_12
#define EXIT_CLEANUP_CASE_12
#elif !defined CASE_N_STATE_12
#define CASE_N_STATE_12
/* FALLTHROUGH */
case 12:
#elif !defined EXIT_CLEANUP_CASE_13
#define EXIT_CLEANUP_CASE_13
#elif !defined CASE_N_STATE_13
#define CASE_N_STATE_13
/* FALLTHROUGH */
case 13:
#elif !defined EXIT_CLEANUP_CASE_14
#define EXIT_CLEANUP_CASE_14
#elif !defined CASE_N_STATE_14
#define CASE_N_STATE_14
/* FALLTHROUGH */
case 14:
#elif !defined EXIT_CLEANUP_CASE_15
#define EXIT_CLEANUP_CASE_15
#elif !defined CASE_N_STATE_15
#define CASE_N_STATE_15
/* FALLTHROUGH */
case 15:
#elif !defined EXIT_CLEANUP_CASE_16
#define EXIT_CLEANUP_CASE_16
#elif !defined CASE_N_STATE_16
#define CASE_N_STATE_16
/* FALLTHROUGH */
case 16:
#else
#error Need to add more case statements!
#endif
cleanup_step++;
+699 -103
View File
@@ -3,13 +3,20 @@
*
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2004-2008 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to dynamically link rsync with the OpenSSL and xxhash
* libraries when those libraries are being distributed in compliance
* with their license terms, and to distribute a dynamically linked
* combination of rsync and these libraries. This is also considered
* to be covered under the GPL's System Libraries exception.
*
* 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
@@ -21,61 +28,366 @@
#include "rsync.h"
#ifdef SUPPORT_XXHASH
#include <xxhash.h>
# if XXH_VERSION_NUMBER >= 800
# define SUPPORT_XXH3 1
# endif
#endif
extern int am_server;
extern int whole_file;
extern int checksum_seed;
extern int protocol_version;
extern int proper_seed_order;
extern const char *checksum_choice;
int csum_length = SHORT_SUM_LENGTH; /* initial value */
#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, 0, "xxh128", NULL },
{ CSUM_XXH3_64, 0, "xxh3", NULL },
#endif
#ifdef SUPPORT_XXHASH
{ CSUM_XXH64, 0, "xxh64", NULL },
{ CSUM_XXH64, 0, "xxhash", NULL },
#endif
{ 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 }
};
struct name_num_obj valid_checksums = {
"checksum", NULL, 0, 0, valid_checksums_items
};
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
};
/* Return the strength rank (0 = strongest) of a daemon-auth digest by name in
* valid_auth_checksums_items[], which is listed strongest-first; -1 if the name
* is not a supported auth digest on this build. Used by the daemon's
* "auth digest" floor to compare the negotiated digest against the minimum. */
int auth_digest_rank(const char *name)
{
struct name_num_item *nni;
int rank = 0;
if (!name || !*name)
return -1;
for (nni = valid_auth_checksums_items; nni->name; nni++, rank++) {
if (strcasecmp(nni->name, name) == 0)
return rank;
}
return -1;
}
/* 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) {
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);
if (!nni) {
rprintf(FERROR, "unknown checksum name: %s\n", name);
exit_cleanup(RERR_UNSUPPORTED);
}
return nni;
}
#ifdef USE_OPENSSL
static const EVP_MD *csum_evp_md(struct name_num_item *nni)
{
const EVP_MD *emd;
if (!(nni->flags & NNI_EVP))
return NULL;
#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_nni)
xfer_sum_nni = file_sum_nni = valid_checksums.negotiated_nni;
else {
const char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
if (cp) {
xfer_sum_nni = parse_csum_name(checksum_choice, cp - checksum_choice);
file_sum_nni = parse_csum_name(cp+1, -1);
} else
xfer_sum_nni = file_sum_nni = parse_csum_name(checksum_choice, -1);
if (am_server && checksum_choice)
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
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_nni)
checksum_choice = valid_checksums.negotiated_nni->name;
else if (checksum_choice == NULL)
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_nni ? " negotiated" : "",
checksum_choice);
}
}
int csum_len_for_type(int cst, BOOL flist_csum)
{
switch (cst) {
case CSUM_NONE:
return 1;
case CSUM_MD4_ARCHAIC:
/* The oldest checksum code is rather weird: the file-list code only sent
* 2-byte checksums, but all other checksums were full MD4 length. */
return flist_csum ? 2 : MD4_DIGEST_LEN;
case CSUM_MD4:
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
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;
case CSUM_XXH3_128:
return 128/8;
default: /* paranoia to prevent missing case values */
exit_cleanup(RERR_UNSUPPORTED);
}
return 0;
}
/* Returns 0 if the checksum is not canonical (i.e. it includes a seed value).
* Returns 1 if the public sum order matches our internal sum order.
* Returns -1 if the public sum order is the reverse of our internal sum order.
*/
int canonical_checksum(int csum_type)
{
switch (csum_type) {
case CSUM_NONE:
case CSUM_MD4_ARCHAIC:
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
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:
case CSUM_XXH3_128:
return 1;
default: /* paranoia to prevent missing case values */
exit_cleanup(RERR_UNSUPPORTED);
}
return 0;
}
#ifndef USE_ROLL_SIMD /* See simd-checksum-*.cpp. */
/*
a simple 32 bit checksum that can be upadted from either end
a simple 32 bit checksum that can be updated from either end
(inspired by Mark Adler's Adler-32 checksum)
*/
uint32 get_checksum1(char *buf1, int32 len)
{
int32 i;
uint32 s1, s2;
schar *buf = (schar *)buf1;
int32 i;
uint32 s1, s2;
schar *buf = (schar *)buf1;
s1 = s2 = 0;
for (i = 0; i < (len-4); i+=4) {
s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] +
10*CHAR_OFFSET;
s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
}
for (; i < len; i++) {
s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
}
return (s1 & 0xffff) + (s2 << 16);
s1 = s2 = 0;
for (i = 0; i < (len-4); i+=4) {
s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + 10*CHAR_OFFSET;
s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
}
for (; i < len; i++) {
s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
}
return (s1 & 0xffff) + (s2 << 16);
}
#endif
/* The "sum" buffer must be at least MAX_DIGEST_LEN bytes! */
void get_checksum2(char *buf, int32 len, char *sum)
{
md_context m;
if (protocol_version >= 30) {
#ifdef USE_OPENSSL
if (xfer_sum_evp_md) {
static EVP_MD_CTX *evp = NULL;
uchar seedbuf[4];
md5_begin(&m);
md5_update(&m, (uchar *)buf, len);
if (!evp && !(evp = EVP_MD_CTX_create()))
out_of_memory("get_checksum2");
EVP_DigestInit_ex(evp, xfer_sum_evp_md, NULL);
if (checksum_seed) {
SIVAL(seedbuf, 0, checksum_seed);
md5_update(&m, seedbuf, 4);
SIVALu(seedbuf, 0, checksum_seed);
EVP_DigestUpdate(evp, seedbuf, 4);
}
md5_result(&m, (uchar *)sum);
} else {
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));
break;
#endif
#ifdef SUPPORT_XXH3
case CSUM_XXH3_64:
SIVAL64(sum, 0, XXH3_64bits_withSeed(buf, len, checksum_seed));
break;
case CSUM_XXH3_128: {
XXH128_hash_t digest = XXH3_128bits_withSeed(buf, len, checksum_seed);
SIVAL64(sum, 0, digest.low64);
SIVAL64(sum, 8, digest.high64);
break;
}
#endif
case CSUM_MD5: {
md_context m5;
uchar seedbuf[4];
md5_begin(&m5);
if (proper_seed_order) {
if (checksum_seed) {
SIVALu(seedbuf, 0, checksum_seed);
md5_update(&m5, seedbuf, 4);
}
md5_update(&m5, (uchar *)buf, len);
} else {
md5_update(&m5, (uchar *)buf, len);
if (checksum_seed) {
SIVALu(seedbuf, 0, checksum_seed);
md5_update(&m5, seedbuf, 4);
}
}
md5_result(&m5, (uchar *)sum);
break;
}
case CSUM_MD4:
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC: {
md_context m;
int32 i;
static char *buf1;
static int32 len1;
mdfour_begin(&m);
if (len > len1) {
if (buf1)
free(buf1);
if (len > len1 || !buf1) {
free(buf1);
buf1 = new_array(char, len+4);
len1 = len;
if (!buf1)
out_of_memory("get_checksum2");
}
memcpy(buf1, buf, len);
@@ -93,59 +405,150 @@ 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 || protocol_version >= 27)
if (len - i > 0 || xfer_sum_nni->num > CSUM_MD4_BUSTED)
mdfour_update(&m, (uchar *)(buf1+i), len-i);
mdfour_result(&m, (uchar *)sum);
break;
}
default: /* paranoia to prevent missing case values */
exit_cleanup(RERR_UNSUPPORTED);
}
}
void file_checksum(char *fname, char *sum, OFF_T size)
void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
{
struct map_struct *buf;
OFF_T i, len = size;
md_context m;
OFF_T i, len = st_p->st_size;
int32 remainder;
int fd;
memset(sum, 0, MAX_DIGEST_LEN);
fd = do_open(fname, O_RDONLY, 0);
if (fd == -1)
fd = vfs_open_checklinks(fname);
if (fd == -1) {
memset(sum, 0, file_sum_len);
return;
}
buf = map_file(fd, size, MAX_MAP_SIZE, CSUM_CHUNK);
buf = map_file(fd, len, MAX_MAP_SIZE, CHUNK_SIZE);
if (protocol_version >= 30) {
md5_begin(&m);
#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");
for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
md5_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
CSUM_CHUNK);
}
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)
md5_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
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;
if (!state && !(state = XXH64_createState()))
out_of_memory("file_checksum");
XXH64_reset(state, 0);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
XXH64_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
remainder = (int32)(len - i);
if (remainder > 0)
XXH64_update(state, (uchar *)map_ptr(buf, i, remainder), remainder);
SIVAL64(sum, 0, XXH64_digest(state));
break;
}
#endif
#ifdef SUPPORT_XXH3
case CSUM_XXH3_64: {
static XXH3_state_t* state = NULL;
if (!state && !(state = XXH3_createState()))
out_of_memory("file_checksum");
XXH3_64bits_reset(state);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
XXH3_64bits_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
remainder = (int32)(len - i);
if (remainder > 0)
XXH3_64bits_update(state, (uchar *)map_ptr(buf, i, remainder), remainder);
SIVAL64(sum, 0, XXH3_64bits_digest(state));
break;
}
case CSUM_XXH3_128: {
XXH128_hash_t digest;
static XXH3_state_t* state = NULL;
if (!state && !(state = XXH3_createState()))
out_of_memory("file_checksum");
XXH3_128bits_reset(state);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
XXH3_128bits_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
remainder = (int32)(len - i);
if (remainder > 0)
XXH3_128bits_update(state, (uchar *)map_ptr(buf, i, remainder), remainder);
digest = XXH3_128bits_digest(state);
SIVAL64(sum, 0, digest.low64);
SIVAL64(sum, 8, digest.high64);
break;
}
#endif
case CSUM_MD5: {
md_context m5;
md5_begin(&m5);
for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
md5_update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
remainder = (int32)(len - i);
if (remainder > 0)
md5_update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder);
md5_result(&m5, (uchar *)sum);
break;
}
case CSUM_MD4:
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC: {
md_context m;
md5_result(&m, (uchar *)sum);
} else {
mdfour_begin(&m);
for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) {
mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK),
CSUM_CHUNK);
}
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 || protocol_version >= 27)
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);
break;
}
default:
rprintf(FERROR, "Invalid checksum-choice for --checksum: %s (%d)\n",
file_sum_nni->name, file_sum_nni->num);
exit_cleanup(RERR_UNSUPPORTED);
}
close(fd);
@@ -153,73 +556,266 @@ void file_checksum(char *fname, char *sum, OFF_T size)
}
static int32 sumresidue;
static md_context md;
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 struct name_num_item *cur_sum_nni;
int cur_sum_len;
void sum_init(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 (protocol_version >= 30)
md5_begin(&md);
else {
mdfour_begin(&md);
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
#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()))
out_of_memory("sum_init");
XXH64_reset(xxh64_state, 0);
break;
#endif
#ifdef SUPPORT_XXH3
case CSUM_XXH3_64:
if (!xxh3_state && !(xxh3_state = XXH3_createState()))
out_of_memory("sum_init");
XXH3_64bits_reset(xxh3_state);
break;
case CSUM_XXH3_128:
if (!xxh3_state && !(xxh3_state = XXH3_createState()))
out_of_memory("sum_init");
XXH3_128bits_reset(xxh3_state);
break;
#endif
case CSUM_MD5:
md5_begin(&ctx_md);
break;
case CSUM_MD4:
mdfour_begin(&ctx_md);
sumresidue = 0;
break;
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC:
mdfour_begin(&ctx_md);
sumresidue = 0;
SIVAL(s, 0, seed);
sum_update(s, 4);
break;
case CSUM_NONE:
break;
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)
{
if (protocol_version >= 30) {
md5_update(&md, (uchar *)p, len);
return;
}
#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);
break;
#endif
#ifdef SUPPORT_XXH3
case CSUM_XXH3_64:
XXH3_64bits_update(xxh3_state, p, len);
break;
case CSUM_XXH3_128:
XXH3_128bits_update(xxh3_state, p, len);
break;
#endif
case CSUM_MD5:
md5_update(&ctx_md, (uchar *)p, len);
break;
case CSUM_MD4:
case CSUM_MD4_OLD:
case CSUM_MD4_BUSTED:
case CSUM_MD4_ARCHAIC:
if (len + sumresidue < CSUM_CHUNK) {
memcpy(ctx_md.buffer + sumresidue, p, len);
sumresidue += len;
break;
}
if (len + sumresidue < CSUM_CHUNK) {
memcpy(md.buffer + sumresidue, p, len);
sumresidue += len;
return;
}
if (sumresidue) {
int32 i = CSUM_CHUNK - sumresidue;
memcpy(ctx_md.buffer + sumresidue, p, i);
mdfour_update(&ctx_md, (uchar *)ctx_md.buffer, CSUM_CHUNK);
len -= i;
p += i;
}
if (sumresidue) {
int32 i = CSUM_CHUNK - sumresidue;
memcpy(md.buffer + sumresidue, p, i);
mdfour_update(&md, (uchar *)md.buffer, CSUM_CHUNK);
len -= i;
p += i;
}
while (len >= CSUM_CHUNK) {
mdfour_update(&ctx_md, (uchar *)p, CSUM_CHUNK);
len -= CSUM_CHUNK;
p += CSUM_CHUNK;
}
while (len >= CSUM_CHUNK) {
mdfour_update(&md, (uchar *)p, CSUM_CHUNK);
len -= CSUM_CHUNK;
p += CSUM_CHUNK;
sumresidue = len;
if (sumresidue)
memcpy(ctx_md.buffer, p, sumresidue);
break;
case CSUM_NONE:
break;
default: /* paranoia to prevent missing case values */
exit_cleanup(RERR_UNSUPPORTED);
}
sumresidue = len;
if (sumresidue)
memcpy(md.buffer, p, sumresidue);
}
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)
{
if (protocol_version >= 30) {
md5_result(&md, (uchar *)sum);
return MD5_DIGEST_LEN;
#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));
break;
#endif
#ifdef SUPPORT_XXH3
case CSUM_XXH3_64:
SIVAL64(sum, 0, XXH3_64bits_digest(xxh3_state));
break;
case CSUM_XXH3_128: {
XXH128_hash_t digest = XXH3_128bits_digest(xxh3_state);
SIVAL64(sum, 0, digest.low64);
SIVAL64(sum, 8, digest.high64);
break;
}
#endif
case CSUM_MD5:
md5_result(&ctx_md, (uchar *)sum);
break;
case CSUM_MD4:
case CSUM_MD4_OLD:
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);
break;
case CSUM_NONE:
*sum = '\0';
break;
default: /* paranoia to prevent missing case values */
exit_cleanup(RERR_UNSUPPORTED);
}
if (sumresidue || protocol_version >= 27)
mdfour_update(&md, (uchar *)md.buffer, sumresidue);
mdfour_result(&md, (uchar *)sum);
return MD4_DIGEST_LEN;
}
#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;
}
+118 -12
View File
@@ -2,7 +2,7 @@
* Implement the core of the --chmod option.
*
* Copyright (C) 2002 Scott Howard
* Copyright (C) 2005-2008 Wayne Davison
* Copyright (C) 2005-2020 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
@@ -19,6 +19,7 @@
*/
#include "rsync.h"
#include "itypes.h"
extern mode_t orig_umask;
@@ -28,32 +29,48 @@ extern mode_t orig_umask;
struct chmod_mode_struct {
struct chmod_mode_struct *next;
int ModeAND, ModeOR;
int ModeAND, ModeOR, ModeCOPY_SRC, ModeCOPY_DST, ModeCOPY_AND, ModeOP;
char flags;
};
#define CHMOD_ADD 1
#define CHMOD_SUB 2
#define CHMOD_EQ 3
#define CHMOD_SET 4
#define STATE_ERROR 0
#define STATE_1ST_HALF 1
#define STATE_2ND_HALF 2
#define STATE_OCTAL_NUM 3
static int mode_dest_special_bits(int where)
{
int bits = 0;
if (where & 0100)
bits |= S_ISUID;
if (where & 0010)
bits |= S_ISGID;
if (where & 0001)
bits |= S_ISVTX;
return bits;
}
/* Parse a chmod-style argument, and break it down into one or more AND/OR
* pairs in a linked list. We return a pointer to new items on succcess
* pairs in a linked list. We return a pointer to new items on success
* (appending the items to the specified list), or NULL on error. */
struct chmod_mode_struct *parse_chmod(const char *modestr,
struct chmod_mode_struct **root_mode_ptr)
{
int state = STATE_1ST_HALF;
int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0, copybits = 0;
struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
*prev_mode = NULL;
while (state != STATE_ERROR) {
if (!*modestr || *modestr == ',') {
int bits;
int bits, where_specified;
if (!op) {
state = STATE_ERROR;
@@ -67,9 +84,10 @@ struct chmod_mode_struct *parse_chmod(const char *modestr,
first_mode = curr_mode;
curr_mode->next = NULL;
if (where)
where_specified = where;
if (where) {
bits = where * what;
else {
} else {
where = 0111;
bits = (where * what) & ~orig_umask;
}
@@ -78,14 +96,35 @@ struct chmod_mode_struct *parse_chmod(const char *modestr,
case CHMOD_ADD:
curr_mode->ModeAND = CHMOD_BITS;
curr_mode->ModeOR = bits + topoct;
curr_mode->ModeCOPY_SRC = copybits;
curr_mode->ModeCOPY_DST = where;
curr_mode->ModeCOPY_AND = where_specified ? CHMOD_BITS : ~orig_umask;
curr_mode->ModeOP = op;
break;
case CHMOD_SUB:
curr_mode->ModeAND = CHMOD_BITS - bits - topoct;
curr_mode->ModeOR = 0;
curr_mode->ModeCOPY_SRC = copybits;
curr_mode->ModeCOPY_DST = where;
curr_mode->ModeCOPY_AND = where_specified ? CHMOD_BITS : ~orig_umask;
curr_mode->ModeOP = op;
break;
case CHMOD_EQ:
curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0);
curr_mode->ModeAND = CHMOD_BITS - (where * 7) - (topoct ? topbits : 0)
- (copybits ? mode_dest_special_bits(where) : 0);
curr_mode->ModeOR = bits + topoct;
curr_mode->ModeCOPY_SRC = copybits;
curr_mode->ModeCOPY_DST = where;
curr_mode->ModeCOPY_AND = where_specified ? CHMOD_BITS : ~orig_umask;
curr_mode->ModeOP = op;
break;
case CHMOD_SET:
curr_mode->ModeAND = 0;
curr_mode->ModeOR = bits;
curr_mode->ModeCOPY_SRC = 0;
curr_mode->ModeCOPY_DST = 0;
curr_mode->ModeCOPY_AND = CHMOD_BITS;
curr_mode->ModeOP = op;
break;
}
@@ -96,10 +135,11 @@ struct chmod_mode_struct *parse_chmod(const char *modestr,
modestr++;
state = STATE_1ST_HALF;
where = what = op = topoct = topbits = flags = 0;
where = what = op = topoct = topbits = flags = copybits = 0;
}
if (state != STATE_2ND_HALF) {
switch (state) {
case STATE_1ST_HALF:
switch (*modestr) {
case 'D':
if (flags & FLAG_FILES_ONLY)
@@ -124,6 +164,7 @@ struct chmod_mode_struct *parse_chmod(const char *modestr,
break;
case 'a':
where |= 0111;
topbits |= 06000; /* a+s sets BOTH setuid and setgid (like chmod(1)) */
break;
case '+':
op = CHMOD_ADD;
@@ -138,36 +179,79 @@ struct chmod_mode_struct *parse_chmod(const char *modestr,
state = STATE_2ND_HALF;
break;
default:
state = STATE_ERROR;
if (isDigit(modestr) && *modestr < '8' && !where) {
op = CHMOD_SET;
state = STATE_OCTAL_NUM;
where = 1;
what = *modestr - '0';
} else
state = STATE_ERROR;
break;
}
} else {
break;
case STATE_2ND_HALF:
switch (*modestr) {
case 'r':
if (copybits)
state = STATE_ERROR;
what |= 4;
break;
case 'w':
if (copybits)
state = STATE_ERROR;
what |= 2;
break;
case 'X':
if (copybits)
state = STATE_ERROR;
flags |= FLAG_X_KEEP;
/* FALL THROUGH */
case 'x':
if (copybits)
state = STATE_ERROR;
what |= 1;
break;
case 's':
if (copybits)
state = STATE_ERROR;
if (topbits)
topoct |= topbits;
else
topoct = 04000;
break;
case 't':
if (copybits)
state = STATE_ERROR;
topoct |= 01000;
break;
case 'u':
if (what || topoct || copybits)
state = STATE_ERROR;
copybits = 0100;
break;
case 'g':
if (what || topoct || copybits)
state = STATE_ERROR;
copybits = 0010;
break;
case 'o':
if (what || topoct || copybits)
state = STATE_ERROR;
copybits = 0001;
break;
default:
state = STATE_ERROR;
break;
}
break;
case STATE_OCTAL_NUM:
if (isDigit(modestr) && *modestr < '8') {
what = what*8 + *modestr - '0';
if (what > CHMOD_BITS)
state = STATE_ERROR;
} else
state = STATE_ERROR;
break;
}
modestr++;
}
@@ -188,6 +272,20 @@ struct chmod_mode_struct *parse_chmod(const char *modestr,
return first_mode;
}
static int mode_copy_bits(int mode, int copy_src, int copy_dst, int copy_and)
{
int copy_bits = 0;
if (copy_src & 0100)
copy_bits |= (mode >> 6) & 7;
if (copy_src & 0010)
copy_bits |= (mode >> 3) & 7;
if (copy_src & 0001)
copy_bits |= mode & 7;
return (copy_dst * copy_bits) & copy_and;
}
/* Takes an existing file permission and a list of AND/OR changes, and
* create a new permissions. */
@@ -195,17 +293,25 @@ int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
{
int IsX = mode & 0111;
int NonPerm = mode & ~CHMOD_BITS;
int copy_bits;
for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
continue;
if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
continue;
copy_bits = mode_copy_bits(mode, chmod_modes->ModeCOPY_SRC,
chmod_modes->ModeCOPY_DST,
chmod_modes->ModeCOPY_AND);
mode &= chmod_modes->ModeAND;
if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
mode |= chmod_modes->ModeOR & ~0111;
else
mode |= chmod_modes->ModeOR;
if (chmod_modes->ModeOP == CHMOD_SUB)
mode &= CHMOD_BITS - copy_bits;
else
mode |= copy_bits;
}
return mode | NonPerm;
+123 -46
View File
@@ -4,7 +4,7 @@
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool
* Copyright (C) 2003-2008 Wayne Davison
* Copyright (C) 2003-2020 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
@@ -22,14 +22,23 @@
#include "rsync.h"
extern int dry_run;
extern int am_server;
extern int am_daemon;
extern int am_receiver;
extern int am_sender;
extern int io_error;
extern int keep_partial;
extern int got_xfer_error;
extern int protocol_version;
extern int output_needs_newline;
extern char *partial_dir;
extern char *logfile_name;
int called_from_signal_handler = 0;
BOOL shutting_down = False;
BOOL flush_ok_after_signal = False;
#ifdef HAVE_SIGACTION
static struct sigaction sigact;
#endif
@@ -49,7 +58,7 @@ void close_all(void)
max_fd = sysconf(_SC_OPEN_MAX) - 1;
for (fd = max_fd; fd >= 0; fd--) {
if ((ret = do_fstat(fd, &st)) == 0) {
if ((ret = vfs_fstat(fd, &st)) == 0) {
if (is_a_socket(fd))
ret = shutdown(fd, 2);
ret = close(fd);
@@ -81,7 +90,7 @@ int cleanup_got_literal = 0;
static const char *cleanup_fname;
static const char *cleanup_new_fname;
static struct file_struct *cleanup_file;
static int cleanup_fd_r, cleanup_fd_w;
static int cleanup_fd_r = -1, cleanup_fd_w = -1;
static pid_t cleanup_pid = 0;
pid_t cleanup_child_pid = -1;
@@ -93,75 +102,104 @@ pid_t cleanup_child_pid = -1;
**/
NORETURN void _exit_cleanup(int code, const char *file, int line)
{
static int cleanup_step = 0;
static int exit_code = 0;
static int unmodified_code = 0;
static int switch_step = 0;
static int exit_code = 0, exit_line = 0;
static const char *exit_file = NULL;
static int first_code = 0;
SIGACTION(SIGUSR1, SIG_IGN);
SIGACTION(SIGUSR2, SIG_IGN);
if (exit_code) /* Preserve first error code when recursing. */
code = exit_code;
if (!exit_code) { /* Preserve first error exit info when recursing. */
exit_code = code;
exit_file = file;
exit_line = line < 0 ? -line : line;
}
/* If this is the exit at the end of the run, the server side
* should not attempt to output a message (see log.c). */
* should not attempt to output a message (see log_exit()). */
if (am_server && code == 0)
am_server = 2;
/* Some of our actions might cause a recursive call back here, so we
* keep track of where we are in the cleanup and never repeat a step. */
switch (cleanup_step) {
#include "case_N.h" /* case 0: cleanup_step++; */
switch (switch_step) {
#include "case_N.h" /* case 0: */
switch_step++;
exit_code = unmodified_code = code;
first_code = code;
if (verbose > 3) {
rprintf(FINFO,
"_exit_cleanup(code=%d, file=%s, line=%d): entered\n",
code, file, line);
if (output_needs_newline) {
fputc('\n', stdout);
output_needs_newline = 0;
}
if (DEBUG_GTE(EXIT, 2)) {
rprintf(FINFO,
"[%s] _exit_cleanup(code=%d, file=%s, line=%d): entered\n",
who_am_i(), code, src_file(file), line);
}
/* FALLTHROUGH */
#include "case_N.h"
switch_step++;
if (cleanup_child_pid != -1) {
int status;
int pid = wait_process(cleanup_child_pid, &status, WNOHANG);
if (pid == cleanup_child_pid) {
status = WEXITSTATUS(status);
if (status > code)
code = exit_code = status;
if (status > exit_code)
exit_code = status;
}
}
/* FALLTHROUGH */
#include "case_N.h"
switch_step++;
if (cleanup_got_literal && cleanup_fname && cleanup_new_fname
&& keep_partial && handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) {
const char *fname = cleanup_fname;
cleanup_fname = NULL;
if (cleanup_fd_r != -1)
if (cleanup_got_literal && (cleanup_fname || cleanup_fd_w != -1)) {
if (cleanup_fd_r != -1) {
close(cleanup_fd_r);
cleanup_fd_r = -1;
}
if (cleanup_fd_w != -1) {
flush_write_file(cleanup_fd_w);
close(cleanup_fd_w);
cleanup_fd_w = -1;
}
if (cleanup_fname && cleanup_new_fname && keep_partial
&& handle_partial_dir(cleanup_new_fname, PDIR_CREATE)) {
int tweak_modtime = 0;
const char *fname = cleanup_fname;
cleanup_fname = NULL;
if (!partial_dir) {
/* We don't want to leave a partial file with a modern time or it
* could be skipped via --update. Setting the time to something
* really old also helps it to stand out as unfinished in an ls. */
tweak_modtime = 1;
cleanup_file->modtime = 0;
}
finish_transfer(cleanup_new_fname, fname, NULL, NULL,
cleanup_file, tweak_modtime, !partial_dir);
}
finish_transfer(cleanup_new_fname, fname, NULL, NULL,
cleanup_file, 0, !partial_dir);
}
/* FALLTHROUGH */
#include "case_N.h"
switch_step++;
io_flush(FULL_FLUSH);
if (flush_ok_after_signal) {
flush_ok_after_signal = False;
if (code == RERR_SIGNAL)
io_flush(FULL_FLUSH);
}
if (!exit_code && !code)
io_flush(FULL_FLUSH);
/* FALLTHROUGH */
#include "case_N.h"
switch_step++;
if (cleanup_fname)
do_unlink(cleanup_fname);
if (code)
vfs_unlink(VFS_AT_FDCWD, cleanup_fname, 0);
if (exit_code)
kill_all(SIGUSR1);
if (cleanup_pid && cleanup_pid == getpid()) {
char *pidf = lp_pid_file();
@@ -169,32 +207,60 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
unlink(lp_pid_file());
}
if (code == 0) {
if (exit_code == 0) {
if (code)
exit_code = code;
if (io_error & IOERR_DEL_LIMIT)
code = exit_code = RERR_DEL_LIMIT;
exit_code = RERR_DEL_LIMIT;
if (io_error & IOERR_VANISHED)
code = exit_code = RERR_VANISHED;
exit_code = RERR_VANISHED;
if (io_error & IOERR_GENERAL || got_xfer_error)
code = exit_code = RERR_PARTIAL;
exit_code = RERR_PARTIAL;
}
if (code || am_daemon || (logfile_name && (am_server || !verbose)))
log_exit(code, file, line);
/* If line < 0, this exit is after a MSG_ERROR_EXIT event, so
* we don't want to output a duplicate error. */
if ((exit_code && line > 0)
|| am_daemon || (logfile_name && (am_server || !INFO_GTE(STATS, 1)))) {
log_exit(exit_code, exit_file, exit_line);
}
/* FALLTHROUGH */
#include "case_N.h"
switch_step++;
if (verbose > 2) {
if (DEBUG_GTE(EXIT, 1)) {
rprintf(FINFO,
"_exit_cleanup(code=%d, file=%s, line=%d): "
"about to call exit(%d)\n",
unmodified_code, file, line, code);
"[%s] _exit_cleanup(code=%d, file=%s, line=%d): "
"about to call exit(%d)%s\n",
who_am_i(), first_code, exit_file, exit_line, exit_code,
dry_run ? " (DRY RUN)" : "");
}
/* FALLTHROUGH */
#include "case_N.h"
switch_step++;
if (am_server && code)
if (exit_code && exit_code != RERR_SOCKETIO && exit_code != RERR_STREAMIO && exit_code != RERR_SIGNAL1
&& exit_code != RERR_TIMEOUT && !shutting_down) {
if (protocol_version >= 31 || am_receiver) {
if (line > 0) {
if (DEBUG_GTE(EXIT, 3)) {
rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT with exit_code %d\n",
who_am_i(), exit_code);
}
send_msg_int(MSG_ERROR_EXIT, exit_code);
}
if (!am_sender)
io_flush(MSG_FLUSH); /* Be sure to send all messages */
noop_io_until_death();
}
else if (!am_sender)
io_flush(MSG_FLUSH); /* Be sure to send all messages */
}
#include "case_N.h"
switch_step++;
if (am_server && exit_code)
msleep(100);
close_all();
@@ -203,12 +269,23 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
break;
}
exit(code);
if (called_from_signal_handler) {
#ifdef GCOV_COVERAGE
/* _exit() bypasses the gcov atexit flush; rsync's generator (and
* other processes) normally finish via the signal handler, so
* without this they would write no .gcda. Harmless otherwise. */
extern void __gcov_dump(void);
__gcov_dump();
#endif
_exit(exit_code);
}
exit(exit_code);
}
void cleanup_disable(void)
{
cleanup_fname = cleanup_new_fname = NULL;
cleanup_fd_r = cleanup_fd_w = -1;
cleanup_got_literal = 0;
}
+314 -119
View File
@@ -3,7 +3,7 @@
*
* Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2002-2008 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
@@ -27,50 +27,60 @@
*/
#include "rsync.h"
#include "itypes.h"
extern int am_daemon;
static const char default_name[] = "UNKNOWN";
extern int am_server;
static const char proxyv2sig[] = "\r\n\r\n\0\r\nQUIT\n";
static char ipaddr_buf[100];
/**
* Return the IP addr of the client as a string
**/
#define PROXY_V2_SIG_SIZE ((int)sizeof proxyv2sig - 1)
#define PROXY_V2_HEADER_SIZE (PROXY_V2_SIG_SIZE + 1 + 1 + 2)
#define CMD_LOCAL 0
#define CMD_PROXY 1
#define PROXY_FAM_TCPv4 0x11
#define PROXY_FAM_TCPv6 0x21
#define GET_SOCKADDR_FAMILY(ss) ((struct sockaddr*)ss)->sa_family
static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len);
static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size);
static int valid_ipaddr(const char *s, int allow_scope);
/* Return the IP addr of the client as a string. */
char *client_addr(int fd)
{
static char addr_buf[100];
static int initialised;
struct sockaddr_storage ss;
socklen_t length = sizeof ss;
char *ssh_info, *p;
if (initialised)
return addr_buf;
if (*ipaddr_buf)
return ipaddr_buf;
initialised = 1;
if (am_server) { /* daemon over --rsh mode */
strlcpy(addr_buf, "0.0.0.0", sizeof addr_buf);
if ((ssh_info = getenv("SSH_CONNECTION")) != NULL
|| (ssh_info = getenv("SSH_CLIENT")) != NULL
|| (ssh_info = getenv("SSH2_CLIENT")) != NULL) {
strlcpy(addr_buf, ssh_info, sizeof addr_buf);
if (am_daemon < 0) { /* daemon over --rsh mode */
char *env_str;
strlcpy(ipaddr_buf, "0.0.0.0", sizeof ipaddr_buf);
if ((env_str = getenv("REMOTE_HOST")) != NULL
|| (env_str = getenv("SSH_CONNECTION")) != NULL
|| (env_str = getenv("SSH_CLIENT")) != NULL
|| (env_str = getenv("SSH2_CLIENT")) != NULL) {
char *p;
strlcpy(ipaddr_buf, env_str, sizeof ipaddr_buf);
/* Truncate the value to just the IP address. */
if ((p = strchr(addr_buf, ' ')) != NULL)
if ((p = strchr(ipaddr_buf, ' ')) != NULL)
*p = '\0';
}
} else {
client_sockaddr(fd, &ss, &length);
getnameinfo((struct sockaddr *)&ss, length,
addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST);
if (valid_ipaddr(ipaddr_buf, True))
return ipaddr_buf;
}
return addr_buf;
}
client_sockaddr(fd, &ss, &length);
getnameinfo((struct sockaddr *)&ss, length, ipaddr_buf, sizeof ipaddr_buf, NULL, 0, NI_NUMERICHOST);
static int get_sockaddr_family(const struct sockaddr_storage *ss)
{
return ((struct sockaddr *) ss)->sa_family;
return ipaddr_buf;
}
@@ -87,68 +97,218 @@ static int get_sockaddr_family(const struct sockaddr_storage *ss)
* After translation from sockaddr to name we do a forward lookup to
* make sure nobody is spoofing PTR records.
**/
char *client_name(int fd)
char *client_name(const char *ipaddr)
{
static char name_buf[100];
static char port_buf[100];
static int initialised;
char port_buf[100];
struct sockaddr_storage ss;
socklen_t ss_len;
struct addrinfo hint, *answer;
int err;
if (initialised)
if (*name_buf)
return name_buf;
strlcpy(name_buf, default_name, sizeof name_buf);
initialised = 1;
if (strcmp(ipaddr, "0.0.0.0") == 0)
return name_buf;
memset(&ss, 0, sizeof ss);
if (am_server) { /* daemon over --rsh mode */
char *addr = client_addr(fd);
struct addrinfo hint, *answer;
int err;
memset(&hint, 0, sizeof hint);
memset(&hint, 0, sizeof hint);
#ifdef AI_NUMERICHOST
hint.ai_flags = AI_NUMERICHOST;
hint.ai_flags = AI_NUMERICHOST;
#endif
hint.ai_socktype = SOCK_STREAM;
hint.ai_socktype = SOCK_STREAM;
if ((err = getaddrinfo(addr, NULL, &hint, &answer)) != 0) {
rprintf(FLOG, "malformed address %s: %s\n",
addr, gai_strerror(err));
return name_buf;
}
switch (answer->ai_family) {
case AF_INET:
ss_len = sizeof (struct sockaddr_in);
memcpy(&ss, answer->ai_addr, ss_len);
break;
#ifdef INET6
case AF_INET6:
ss_len = sizeof (struct sockaddr_in6);
memcpy(&ss, answer->ai_addr, ss_len);
break;
#endif
default:
exit_cleanup(RERR_SOCKETIO);
}
freeaddrinfo(answer);
} else {
ss_len = sizeof ss;
client_sockaddr(fd, &ss, &ss_len);
if ((err = getaddrinfo(ipaddr, NULL, &hint, &answer)) != 0) {
rprintf(FLOG, "malformed address %s: %s\n", ipaddr, gai_strerror(err));
return name_buf;
}
if (lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf,
port_buf, sizeof port_buf) == 0)
check_name(fd, &ss, name_buf, sizeof name_buf);
switch (answer->ai_family) {
case AF_INET:
ss_len = sizeof (struct sockaddr_in);
memcpy(&ss, answer->ai_addr, ss_len);
break;
#ifdef INET6
case AF_INET6:
ss_len = sizeof (struct sockaddr_in6);
memcpy(&ss, answer->ai_addr, ss_len);
break;
#endif
default:
NOISY_DEATH("Unknown ai_family value");
}
freeaddrinfo(answer);
/* reverse lookup */
err = getnameinfo((struct sockaddr*)&ss, ss_len, name_buf, sizeof name_buf,
port_buf, sizeof port_buf, NI_NAMEREQD | NI_NUMERICSERV);
if (err) {
strlcpy(name_buf, default_name, sizeof name_buf);
rprintf(FLOG, "name lookup failed for %s: %s\n", ipaddr, gai_strerror(err));
} else
check_name(ipaddr, &ss, name_buf, sizeof name_buf);
return name_buf;
}
/* Try to read a proxy protocol header (V1 or V2). Returns 1 on success or 0 on failure. */
int read_proxy_protocol_header(int fd)
{
union {
struct {
char line[108];
} v1;
struct {
char sig[PROXY_V2_SIG_SIZE];
char ver_cmd;
char fam;
unsigned char len[2];
union {
struct {
char src_addr[4];
char dst_addr[4];
char src_port[2];
char dst_port[2];
} ip4;
struct {
char src_addr[16];
char dst_addr[16];
char src_port[2];
char dst_port[2];
} ip6;
struct {
char src_addr[108];
char dst_addr[108];
} unx;
} addr;
} v2;
} hdr;
read_buf(fd, (char*)&hdr, PROXY_V2_SIG_SIZE);
if (memcmp(hdr.v2.sig, proxyv2sig, PROXY_V2_SIG_SIZE) == 0) { /* Proxy V2 */
int ver, cmd, size;
read_buf(fd, (char*)&hdr + PROXY_V2_SIG_SIZE, PROXY_V2_HEADER_SIZE - PROXY_V2_SIG_SIZE);
ver = (hdr.v2.ver_cmd & 0xf0) >> 4;
cmd = (hdr.v2.ver_cmd & 0x0f);
size = (hdr.v2.len[0] << 8) + hdr.v2.len[1];
if (ver != 2 || size + PROXY_V2_HEADER_SIZE > (int)sizeof hdr)
return 0;
/* Grab all the remaining data in the binary request. */
read_buf(fd, (char*)&hdr + PROXY_V2_HEADER_SIZE, size);
switch (cmd) {
case CMD_PROXY:
switch (hdr.v2.fam) {
case PROXY_FAM_TCPv4:
if (size != sizeof hdr.v2.addr.ip4)
return 0;
inet_ntop(AF_INET, hdr.v2.addr.ip4.src_addr, ipaddr_buf, sizeof ipaddr_buf);
return valid_ipaddr(ipaddr_buf, False);
#ifdef INET6
case PROXY_FAM_TCPv6:
if (size != sizeof hdr.v2.addr.ip6)
return 0;
inet_ntop(AF_INET6, hdr.v2.addr.ip6.src_addr, ipaddr_buf, sizeof ipaddr_buf);
return valid_ipaddr(ipaddr_buf, False);
#endif
default:
break;
}
/* For an unsupported protocol we'll ignore the proxy data (leaving ipaddr_buf unset)
* and accept the connection, which will get handled as a normal socket addr. */
return 1;
case CMD_LOCAL:
return 1;
default:
break;
}
return 0;
}
if (memcmp(hdr.v1.line, "PROXY", 5) == 0) { /* Proxy V1 */
char *endc, *sp, *p = hdr.v1.line + PROXY_V2_SIG_SIZE;
int port_chk;
*p = '\0';
if (!strchr(hdr.v1.line, '\n')) {
while (1) {
read_buf(fd, p, 1);
if (*p++ == '\n')
break;
if (p - hdr.v1.line >= (int)sizeof hdr.v1.line - 1)
return 0;
}
*p = '\0';
}
endc = strchr(hdr.v1.line, '\r');
if (!endc || endc[1] != '\n' || endc[2])
return 0;
*endc = '\0';
p = hdr.v1.line + 5;
if (!isSpace(p++))
return 0;
if (strncmp(p, "TCP4", 4) == 0)
p += 4;
else if (strncmp(p, "TCP6", 4) == 0)
p += 4;
else if (strncmp(p, "UNKNOWN", 7) == 0)
return 1;
else
return 0;
if (!isSpace(p++))
return 0;
if ((sp = strchr(p, ' ')) == NULL)
return 0;
*sp = '\0';
if (!valid_ipaddr(p, False))
return 0;
strlcpy(ipaddr_buf, p, sizeof ipaddr_buf); /* It will always fit when valid. */
p = sp + 1;
if ((sp = strchr(p, ' ')) == NULL)
return 0;
*sp = '\0';
if (!valid_ipaddr(p, False))
return 0;
/* Ignore destination address. */
p = sp + 1;
if ((sp = strchr(p, ' ')) == NULL)
return 0;
*sp = '\0';
port_chk = strtol(p, &endc, 10);
if (*endc || port_chk == 0)
return 0;
/* Ignore source port. */
p = sp + 1;
port_chk = strtol(p, &endc, 10);
if (*endc || port_chk == 0)
return 0;
/* Ignore destination port. */
return 1;
}
return 0;
}
/**
* Get the sockaddr for the client.
@@ -156,9 +316,7 @@ char *client_name(int fd)
* If it comes in as an ipv4 address mapped into IPv6 format then we
* convert it back to a regular IPv4.
**/
void client_sockaddr(int fd,
struct sockaddr_storage *ss,
socklen_t *ss_len)
static void client_sockaddr(int fd, struct sockaddr_storage *ss, socklen_t *ss_len)
{
memset(ss, 0, sizeof *ss);
@@ -169,8 +327,8 @@ void client_sockaddr(int fd,
}
#ifdef INET6
if (get_sockaddr_family(ss) == AF_INET6 &&
IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
if (GET_SOCKADDR_FAMILY(ss) == AF_INET6
&& IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
/* OK, so ss is in the IPv6 family, but it is really
* an IPv4 address: something like
* "::ffff:10.130.1.2". If we use it as-is, then the
@@ -193,51 +351,20 @@ void client_sockaddr(int fd,
/* There is a macro to extract the mapped part
* (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem
* to be present in the Linux headers. */
memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
sizeof sin->sin_addr);
memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12], sizeof sin->sin_addr);
}
#endif
}
/**
* Look up a name from @p ss into @p name_buf.
*
* @param fd file descriptor for client socket.
**/
int lookup_name(int fd, const struct sockaddr_storage *ss,
socklen_t ss_len,
char *name_buf, size_t name_buf_size,
char *port_buf, size_t port_buf_size)
{
int name_err;
/* reverse lookup */
name_err = getnameinfo((struct sockaddr *) ss, ss_len,
name_buf, name_buf_size,
port_buf, port_buf_size,
NI_NAMEREQD | NI_NUMERICSERV);
if (name_err != 0) {
strlcpy(name_buf, default_name, name_buf_size);
rprintf(FLOG, "name lookup failed for %s: %s\n",
client_addr(fd), gai_strerror(name_err));
return name_err;
}
return 0;
}
/**
* Compare an addrinfo from the resolver to a sockinfo.
*
* Like strcmp, returns 0 for identical.
**/
int compare_addrinfo_sockaddr(const struct addrinfo *ai,
const struct sockaddr_storage *ss)
static int compare_addrinfo_sockaddr(const struct addrinfo *ai, const struct sockaddr_storage *ss)
{
int ss_family = get_sockaddr_family(ss);
int ss_family = GET_SOCKADDR_FAMILY(ss);
const char fn[] = "compare_addrinfo_sockaddr";
if (ai->ai_family != ss_family) {
@@ -253,8 +380,7 @@ int compare_addrinfo_sockaddr(const struct addrinfo *ai,
sin1 = (const struct sockaddr_in *) ss;
sin2 = (const struct sockaddr_in *) ai->ai_addr;
return memcmp(&sin1->sin_addr, &sin2->sin_addr,
sizeof sin1->sin_addr);
return memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof sin1->sin_addr);
}
#ifdef INET6
@@ -264,14 +390,13 @@ int compare_addrinfo_sockaddr(const struct addrinfo *ai,
sin1 = (const struct sockaddr_in6 *) ss;
sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
if (ai->ai_addrlen < sizeof (struct sockaddr_in6)) {
if (ai->ai_addrlen < (int)sizeof (struct sockaddr_in6)) {
rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",
fn, (int)ai->ai_addrlen);
return 1;
}
if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
sizeof sin1->sin6_addr))
if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof sin1->sin6_addr))
return 1;
#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
@@ -297,13 +422,11 @@ int compare_addrinfo_sockaddr(const struct addrinfo *ai,
* because it doesn't seem that it could be spoofed in any way, and
* getaddrinfo on random service names seems to cause problems on AIX.
**/
int check_name(int fd,
const struct sockaddr_storage *ss,
char *name_buf, size_t name_buf_size)
static int check_name(const char *ipaddr, const struct sockaddr_storage *ss, char *name_buf, size_t name_buf_size)
{
struct addrinfo hints, *res, *res0;
int error;
int ss_family = get_sockaddr_family(ss);
int ss_family = GET_SOCKADDR_FAMILY(ss);
memset(&hints, 0, sizeof hints);
hints.ai_family = ss_family;
@@ -334,10 +457,82 @@ int check_name(int fd,
/* We hit the end of the list without finding an
* address that was the same as ss. */
rprintf(FLOG, "%s is not a known address for \"%s\": "
"spoofed address?\n", client_addr(fd), name_buf);
"spoofed address?\n", ipaddr, name_buf);
strlcpy(name_buf, default_name, name_buf_size);
}
freeaddrinfo(res0);
return 0;
}
/* Returns 1 for a valid IPv4 or IPv6 addr, or 0 for a bad one. */
static int valid_ipaddr(const char *s, int allow_scope)
{
int i;
if (strchr(s, ':') != NULL) { /* Only IPv6 has a colon. */
int count, saw_double_colon = 0;
int ipv4_at_end = 0;
if (*s == ':') { /* A colon at the start must be a :: */
if (*++s != ':')
return 0;
saw_double_colon = 1;
s++;
}
for (count = 0; count < 8; count++) {
if (!*s)
return saw_double_colon;
if (allow_scope && *s == '%') {
if (saw_double_colon)
break;
return 0;
}
if (strchr(s, ':') == NULL && strchr(s, '.') != NULL) {
if ((!saw_double_colon && count != 6) || (saw_double_colon && count > 6))
return 0;
ipv4_at_end = 1;
break;
}
if (!isHexDigit(s++)) /* Need 1-4 hex digits */
return 0;
if (isHexDigit(s) && isHexDigit(++s) && isHexDigit(++s) && isHexDigit(++s))
return 0;
if (*s == ':') {
if (!*++s)
return 0;
if (*s == ':') {
if (saw_double_colon)
return 0;
saw_double_colon = 1;
s++;
}
}
}
if (!ipv4_at_end) {
if (allow_scope && *s == '%')
for (s++; isAlNum(s); s++) { }
return !*s && s[-1] != '%';
}
}
/* IPv4 */
for (i = 0; i < 4; i++) {
long n;
char *end;
if (i && *s++ != '.')
return 0;
n = strtol(s, &end, 10);
if (n > 255 || n < 0 || end <= s || end > s+3)
return 0;
s = end;
}
return !*s;
}
+1036 -317
View File
File diff suppressed because it is too large. Load diff
Executable
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
srcdir=`dirname $0`
opt="$1"
shift
echo "$*"
if ! "${@}"; then
echo "If you can't fix the issue, re-run $srcdir/configure with --$opt."
exit 1
fi
+658 -62
View File
@@ -3,7 +3,7 @@
*
* Copyright (C) Andrew Tridgell 1996
* Copyright (C) Paul Mackerras 1996
* Copyright (C) 2004-2008 Wayne Davison
* Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,12 +20,9 @@
*/
#include "rsync.h"
#include "itypes.h"
#include "ifuncs.h"
int remote_protocol = 0;
int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
int inc_recurse = 0;
extern int verbose;
extern int am_server;
extern int am_sender;
extern int local_server;
@@ -33,9 +30,11 @@ extern int inplace;
extern int recurse;
extern int use_qsort;
extern int allow_inc_recurse;
extern int preallocate_files;
extern int append_mode;
extern int fuzzy_basis;
extern int read_batch;
extern int write_batch;
extern int delay_updates;
extern int checksum_seed;
extern int basis_dir_cnt;
@@ -44,32 +43,87 @@ extern int protocol_version;
extern int protect_args;
extern int preserve_uid;
extern int preserve_gid;
extern int preserve_atimes;
extern int preserve_crtimes;
extern int preserve_acls;
extern int preserve_xattrs;
extern int xfer_flags_as_varint;
extern int need_messages_from_generator;
extern int delete_mode, delete_before, delete_during, delete_after;
extern int do_compression;
extern int do_compression_level;
extern int do_compression_threads;
extern int saw_stderr_opt;
extern int msgs2stderr;
extern char *shell_cmd;
extern char *partial_dir;
extern char *dest_option;
extern char *files_from;
extern char *filesfrom_host;
extern struct filter_list_struct filter_list;
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, 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 */
int inc_recurse = 0;
int compat_flags = 0;
int use_safe_inc_flist = 0;
int want_xattr_optim = 0;
int proper_seed_order = 0;
int inplace_partial = 0;
int do_negotiated_strings = 0;
int xmit_id0_names = 0;
struct name_num_item *xattr_sum_nni;
int xattr_sum_len = 0;
/* These index values are for the file-list's extra-attribute array. */
int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
int sender_symlink_iconv = 0; /* sender should convert symlink content */
#ifdef ICONV_OPTION
int filesfrom_convert = 0;
#endif
#define MAX_NSTR_STRLEN 256
struct name_num_item valid_compressions_items[] = {
#ifdef SUPPORT_ZSTD
{ CPRES_ZSTD, 0, "zstd", NULL },
#endif
#ifdef SUPPORT_LZ4
{ CPRES_LZ4, 0, "lz4", NULL },
#endif
{ 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)
#define CF_SYMLINK_TIMES (1<<1)
#define CF_SYMLINK_ICONV (1<<2)
#define CF_SAFE_FLIST (1<<3)
#define CF_AVOID_XATTR_OPTIM (1<<4)
#define CF_CHKSUM_SEED_FIX (1<<5)
#define CF_INPLACE_PARTIAL_DIR (1<<6)
#define CF_VARINT_FLIST_FLAGS (1<<7)
#define CF_ID0_NAMES (1<<8)
static const char *client_info;
@@ -78,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))
@@ -111,7 +161,13 @@ static void check_sub_protocol(void)
void set_allow_inc_recurse(void)
{
client_info = shell_cmd ? shell_cmd : "";
if (!local_server)
client_info = shell_cmd ? shell_cmd : "";
else if (am_server) {
char buf[64];
maybe_add_e_option(buf, sizeof buf);
client_info = *buf ? strdup(buf+1) : ""; /* The +1 skips the leading "e". */
}
if (!recurse || use_qsort)
allow_inc_recurse = 0;
@@ -119,25 +175,426 @@ void set_allow_inc_recurse(void)
&& (delete_before || delete_after
|| delay_updates || prune_empty_dirs))
allow_inc_recurse = 0;
else if (am_server && !local_server
&& (strchr(client_info, 'i') == NULL))
else if (am_server && strchr(client_info, 'i') == NULL)
allow_inc_recurse = 0;
}
void parse_compress_choice(int final_call)
{
if (valid_compressions.negotiated_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) {
rprintf(FERROR, "unknown compress name: %s\n", compress_choice);
exit_cleanup(RERR_UNSUPPORTED);
}
do_compression = nni->num;
if (am_server)
validate_choice_vs_env(NSTR_COMPRESS, do_compression, -1);
} else if (do_compression)
do_compression = CPRES_ZLIB;
else
do_compression = CPRES_NONE;
if (do_compression != CPRES_NONE && final_call)
init_compression_level(); /* There's a chance this might turn compression off! */
if (do_compression == CPRES_NONE)
compress_choice = NULL;
/* Snag the compression name for both write_batch's option output & the following debug output. */
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";
}
if (final_call && DEBUG_GTE(NSTR, am_server ? 3 : 1)
&& (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_nni ? " negotiated" : "",
compress_choice, do_compression_level);
}
}
struct name_num_item *get_nni_by_name(struct name_num_obj *nno, const char *name, int len)
{
struct name_num_item *nni;
if (len < 0)
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;
}
return NULL;
}
struct name_num_item *get_nni_by_num(struct name_num_obj *nno, int num)
{
struct name_num_item *nni;
for (nni = nno->list; nni->name; nni++) {
if (num == nni->num)
return nni;
}
return NULL;
}
static void init_nno_saw(struct name_num_obj *nno, int val)
{
struct name_num_item *nni;
int cnt;
if (!nno->saw_len) {
for (nni = nno->list; nni->name; nni++) {
if (nni->num >= nno->saw_len)
nno->saw_len = nni->num + 1;
}
}
if (!nno->saw) {
nno->saw = new_array0(uchar, nno->saw_len);
/* 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_nni = &nno->list[nno->saw[nni->num]-1];
else
nno->saw[nni->num] = cnt;
}
}
memset(nno->saw, val, nno->saw_len);
}
/* Simplify the user-provided string so that it contains valid names without any duplicates.
* It also sets the "saw" flags to a 1-relative count of which name was seen first. */
static int parse_nni_str(struct name_num_obj *nno, const char *from, char *tobuf, int tobuf_len)
{
char *to = tobuf, *tok = NULL;
int saw_tok = 0, cnt = 0;
while (1) {
int at_space = isSpace(from);
char ch = *from++;
if (ch == '&')
ch = '\0';
if (!ch || at_space) {
if (tok) {
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_nni) {
to = tok + strlcpy(tok, nni->main_nni->name, tobuf_len - (tok - tobuf));
if (to - tobuf >= tobuf_len) {
to = tok - 1;
break;
}
}
} else
to = tok - (tok != tobuf);
saw_tok = 1;
tok = NULL;
}
if (!ch)
break;
continue;
}
if (!tok) {
if (to != tobuf)
*to++ = ' ';
tok = to;
}
if (to - tobuf >= tobuf_len - 1) {
to = tok - (tok != tobuf);
break;
}
*to++ = ch;
}
*to = '\0';
if (saw_tok && to == tobuf)
return strlcpy(tobuf, "INVALID", MAX_NSTR_STRLEN);
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) /* Can't improve on our own #1 preference */
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)
{
if (len < 0)
len = read_vstring(f_in, tmpbuf, MAX_NSTR_STRLEN);
if (DEBUG_GTE(NSTR, am_server ? 3 : 2)) {
if (am_server)
rprintf(FINFO, "Client %s list (on server): %s\n", nno->type, tmpbuf);
else
rprintf(FINFO, "Server %s list (on client): %s\n", nno->type, tmpbuf);
}
if (len > 0 && parse_negotiate_str(nno, tmpbuf))
return;
if (!am_server || !do_negotiated_strings) {
char *cp = tmpbuf;
int j;
rprintf(FERROR, "Failed to negotiate a %s choice.\n", nno->type);
rprintf(FERROR, "%s list: %s\n", am_server ? "Client" : "Server", tmpbuf);
/* Recreate our original list from the saw values. This can't overflow our huge
* buffer because we don't have enough valid entries to get anywhere close. */
for (j = 1, *cp = '\0'; j <= nno->saw_len; j++) {
struct name_num_item *nni;
for (nni = nno->list; nni->name; nni++) {
if (nno->saw[nni->num] == j) {
*cp++ = ' ';
cp += strlcpy(cp, nni->name, MAX_NSTR_STRLEN - (cp - tmpbuf));
break;
}
}
}
if (!*tmpbuf)
strlcpy(cp, " INVALID", MAX_NSTR_STRLEN);
rprintf(FERROR, "%s list:%s\n", am_server ? "Server" : "Client", tmpbuf);
}
exit_cleanup(RERR_UNSUPPORTED);
}
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)
env_str = ntype == NSTR_COMPRESS ? "zlib" : protocol_version >= 30 ? "md5" : "md4";
if (am_server && env_str) {
const char *cp = strchr(env_str, '&');
if (cp)
env_str = cp + 1;
}
return env_str;
}
void validate_choice_vs_env(int ntype, int num1, int num2)
{
struct name_num_obj *nno = ntype == NSTR_COMPRESS ? &valid_compressions : &valid_checksums;
const char *list_str = getenv_nstr(ntype);
char tmpbuf[MAX_NSTR_STRLEN];
if (!list_str)
return;
while (isSpace(list_str)) list_str++;
if (!*list_str)
return;
init_nno_saw(nno, 0);
parse_nni_str(nno, list_str, tmpbuf, MAX_NSTR_STRLEN);
if (ntype == NSTR_CHECKSUM) /* If "md4" is in the env list, all the old MD4 choices are OK too. */
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",
ntype == NSTR_COMPRESS ? "compress" : "checksum",
ntype == NSTR_COMPRESS ? compress_choice : checksum_choice);
exit_cleanup(RERR_UNSUPPORTED);
}
free(nno->saw);
nno->saw = NULL;
}
/* The saw buffer is initialized and used to store ordinal values from 1 to N
* for the order of the args in the array. If dup_markup == '\0', duplicates
* are removed otherwise the char is prefixed to the duplicate term and, if it
* is an opening paren/bracket/brace, the matching closing char is suffixed.
* "none" is removed on the client side unless dup_markup != '\0'. */
int get_default_nno_list(struct name_num_obj *nno, char *to_buf, int to_buf_len, char dup_markup)
{
struct name_num_item *nni;
int len = 0, cnt = 0;
char delim = '\0', post_delim;
switch (dup_markup) {
case '(': post_delim = ')'; break;
case '[': post_delim = ']'; break;
case '{': post_delim = '}'; break;
default: post_delim = '\0'; break;
}
init_nno_saw(nno, 0);
for (nni = nno->list, len = 0; nni->name; nni++) {
if (nni->num == CSUM_gone)
continue;
if (nni->main_nni) {
if (!dup_markup || nni->main_nni->num == CSUM_gone)
continue;
delim = dup_markup;
}
if (nni->num == 0 && !am_server && !dup_markup)
continue;
if (len)
to_buf[len++]= ' ';
if (delim) {
to_buf[len++]= delim;
delim = post_delim;
}
len += strlcpy(to_buf+len, nni->name, to_buf_len - len);
if (len >= to_buf_len - 3)
exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE... */
if (delim) {
to_buf[len++]= delim;
delim = '\0';
}
nno->saw[nni->num] = ++cnt;
}
return len;
}
static void send_negotiate_str(int f_out, struct name_num_obj *nno, int ntype)
{
char tmpbuf[MAX_NSTR_STRLEN];
const char *list_str = getenv_nstr(ntype);
int len;
if (list_str && *list_str) {
init_nno_saw(nno, 0);
len = parse_nni_str(nno, list_str, tmpbuf, MAX_NSTR_STRLEN);
list_str = tmpbuf;
} else
list_str = NULL;
if (!list_str || !*list_str)
len = get_default_nno_list(nno, tmpbuf, MAX_NSTR_STRLEN, '\0');
if (DEBUG_GTE(NSTR, am_server ? 3 : 2)) {
if (am_server)
rprintf(FINFO, "Server %s list (on server): %s\n", nno->type, tmpbuf);
else
rprintf(FINFO, "Client %s list (on client): %s\n", nno->type, tmpbuf);
}
/* Each side sends their list of valid names to the other side and then each
* side picks its own most-preferred name that also appears in the peer's
* list. Honest peers emit their list in table (strongest-first) order via
* get_default_nno_list(), so both sides converge on the strongest mutual
* choice; a peer that front-loads a weaker name only desyncs itself. */
if (do_negotiated_strings)
write_vstring(f_out, tmpbuf, len);
}
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);
if (do_compression && !compress_choice)
send_negotiate_str(f_out, &valid_compressions, NSTR_COMPRESS);
if (valid_checksums.saw) {
char tmpbuf[MAX_NSTR_STRLEN];
int len;
if (do_negotiated_strings)
len = -1;
else
len = strlcpy(tmpbuf, protocol_version >= 30 ? "md5" : "md4", MAX_NSTR_STRLEN);
recv_negotiate_str(f_in, &valid_checksums, tmpbuf, len);
}
if (valid_compressions.saw) {
char tmpbuf[MAX_NSTR_STRLEN];
int len;
if (do_negotiated_strings)
len = -1;
else
len = strlcpy(tmpbuf, "zlib", MAX_NSTR_STRLEN);
recv_negotiate_str(f_in, &valid_compressions, tmpbuf, len);
}
/* 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_nni = valid_compressions.negotiated_nni = NULL;
}
void setup_protocol(int f_out,int f_in)
{
if (am_sender)
file_extra_cnt += PTR_EXTRA_CNT;
assert(file_extra_cnt == 0);
assert(EXTRA64_CNT == 2 || EXTRA64_CNT == 1);
/* All int64 values must be set first so that they are guaranteed to be
* aligned for direct int64-pointer memory access. */
if (preserve_atimes)
atimes_ndx = (file_extra_cnt += EXTRA64_CNT);
if (preserve_crtimes)
crtimes_ndx = (file_extra_cnt += EXTRA64_CNT);
if (am_sender) /* This is most likely in the file_extras64 union as well. */
pathname_ndx = (file_extra_cnt += PTR_EXTRA_CNT);
else
file_extra_cnt++;
if (preserve_uid)
uid_ndx = ++file_extra_cnt;
if (preserve_gid)
gid_ndx = ++file_extra_cnt;
if (preserve_acls && !am_sender)
acls_ndx = ++file_extra_cnt;
if (preserve_xattrs)
xattrs_ndx = ++file_extra_cnt;
depth_ndx = ++file_extra_cnt;
/* uid_ndx/gid_ndx/acls_ndx/xattrs_ndx are assigned AFTER
* check_batch_flags() below: a batch file's stream-flags can flip
* preserve_uid/gid/acls/xattrs on, and computing the *_ndx slots
* before that leaves e.g. preserve_xattrs=1 with xattrs_ndx=0 -- so
* F_XATTR(file) (= REQ_EXTRA(file, 0)) writes at offset 0 of every
* file_struct, clobbering file->dirname. Nothing between here and
* check_batch_flags() reads file_extra_cnt or the *_ndx values. */
if (am_server)
set_allow_inc_recurse();
@@ -157,14 +614,14 @@ void setup_protocol(int f_out,int f_in)
exit_cleanup(RERR_PROTOCOL);
}
if (verbose > 3) {
if (DEBUG_GTE(PROTO, 1)) {
rprintf(FINFO, "(%s) Protocol versions: remote=%d, negotiated=%d\n",
am_server? "Server" : "Client", remote_protocol, protocol_version);
}
if (remote_protocol < MIN_PROTOCOL_VERSION
|| remote_protocol > MAX_PROTOCOL_VERSION) {
rprintf(FERROR,"protocol version mismatch -- is your shell clean?\n");
rprintf(FERROR,"(see the rsync man page for an explanation)\n");
rprintf(FERROR,"(see the rsync manpage for an explanation)\n");
exit_cleanup(RERR_PROTOCOL);
}
if (remote_protocol < OLD_PROTOCOL_VERSION) {
@@ -184,21 +641,41 @@ void setup_protocol(int f_out,int f_in)
if (read_batch)
check_batch_flags();
if (preserve_uid)
uid_ndx = ++file_extra_cnt;
if (preserve_gid)
gid_ndx = ++file_extra_cnt;
if (preserve_acls && !am_sender)
acls_ndx = ++file_extra_cnt;
if (preserve_xattrs)
xattrs_ndx = ++file_extra_cnt;
if (!saw_stderr_opt && protocol_version <= 28 && am_server)
msgs2stderr = 0; /* The client side may not have stderr setup for us. */
#ifndef SUPPORT_PREALLOCATION
if (preallocate_files && !am_sender) {
rprintf(FERROR, "preallocation is not supported on this %s\n",
am_server ? "Server" : "Client");
exit_cleanup(RERR_SYNTAX);
}
#endif
if (protocol_version < 30) {
if (append_mode == 1)
append_mode = 2;
if (preserve_acls && !local_server) {
rprintf(FERROR,
"--acls requires protocol 30 or higher"
" (negotiated %d).\n",
protocol_version);
"--acls requires protocol 30 or higher"
" (negotiated %d).\n",
protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
if (preserve_xattrs && !local_server) {
rprintf(FERROR,
"--xattrs requires protocol 30 or higher"
" (negotiated %d).\n",
protocol_version);
"--xattrs requires protocol 30 or higher"
" (negotiated %d).\n",
protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
}
@@ -213,78 +690,121 @@ void setup_protocol(int f_out,int f_in)
if (protocol_version < 29) {
if (fuzzy_basis) {
rprintf(FERROR,
"--fuzzy requires protocol 29 or higher"
" (negotiated %d).\n",
protocol_version);
"--fuzzy requires protocol 29 or higher"
" (negotiated %d).\n",
protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
if (basis_dir_cnt && inplace) {
rprintf(FERROR,
"%s with --inplace requires protocol 29 or higher"
" (negotiated %d).\n",
dest_option, protocol_version);
"%s with --inplace requires protocol 29 or higher"
" (negotiated %d).\n",
alt_dest_opt(0), protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
if (basis_dir_cnt > 1) {
rprintf(FERROR,
"Using more than one %s option requires protocol"
" 29 or higher (negotiated %d).\n",
dest_option, protocol_version);
"Using more than one %s option requires protocol"
" 29 or higher (negotiated %d).\n",
alt_dest_opt(0), protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
if (prune_empty_dirs) {
rprintf(FERROR,
"--prune-empty-dirs requires protocol 29 or higher"
" (negotiated %d).\n",
protocol_version);
"--prune-empty-dirs requires protocol 29 or higher"
" (negotiated %d).\n",
protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
} else if (protocol_version >= 30) {
int compat_flags;
if (am_server) {
compat_flags = allow_inc_recurse ? CF_INC_RECURSE : 0;
#if defined HAVE_LUTIMES && defined HAVE_UTIMES
#ifdef CAN_SET_SYMLINK_TIMES
compat_flags |= CF_SYMLINK_TIMES;
#endif
write_byte(f_out, compat_flags);
} else
compat_flags = read_byte(f_in);
#ifdef ICONV_OPTION
compat_flags |= CF_SYMLINK_ICONV;
#endif
if (strchr(client_info, 'f') != NULL)
compat_flags |= CF_SAFE_FLIST;
if (strchr(client_info, 'x') != NULL)
compat_flags |= CF_AVOID_XATTR_OPTIM;
if (strchr(client_info, 'C') != NULL)
compat_flags |= CF_CHKSUM_SEED_FIX;
if (strchr(client_info, 'I') != NULL)
compat_flags |= CF_INPLACE_PARTIAL_DIR;
if (strchr(client_info, 'u') != NULL)
compat_flags |= CF_ID0_NAMES;
if (strchr(client_info, 'v') != NULL) {
do_negotiated_strings = 1;
compat_flags |= CF_VARINT_FLIST_FLAGS;
}
if (strchr(client_info, 'V') != NULL) { /* Support a pre-release 'V' that got superseded */
if (!write_batch)
compat_flags |= CF_VARINT_FLIST_FLAGS;
write_byte(f_out, compat_flags);
} else
write_varint(f_out, compat_flags);
} else { /* read_varint() is compatible with the older write_byte() when the 0x80 bit isn't on. */
compat_flags = read_varint(f_in);
if (compat_flags & CF_VARINT_FLIST_FLAGS)
do_negotiated_strings = 1;
}
/* The inc_recurse var MUST be set to 0 or 1. */
inc_recurse = compat_flags & CF_INC_RECURSE ? 1 : 0;
want_xattr_optim = protocol_version >= 31 && !(compat_flags & CF_AVOID_XATTR_OPTIM);
proper_seed_order = compat_flags & CF_CHKSUM_SEED_FIX ? 1 : 0;
xfer_flags_as_varint = compat_flags & CF_VARINT_FLIST_FLAGS ? 1 : 0;
xmit_id0_names = compat_flags & CF_ID0_NAMES ? 1 : 0;
if (!xfer_flags_as_varint && preserve_crtimes) {
fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --crtimes.\n");
exit_cleanup(RERR_PROTOCOL);
}
if (am_sender) {
receiver_symlink_times = am_server
? strchr(client_info, 'L') != NULL
: !!(compat_flags & CF_SYMLINK_TIMES);
}
#if defined HAVE_LUTIMES && defined HAVE_UTIMES
#ifdef CAN_SET_SYMLINK_TIMES
else
receiver_symlink_times = 1;
#endif
#ifdef ICONV_OPTION
sender_symlink_iconv = iconv_opt && (am_server
? strchr(client_info, 's') != NULL
: !!(compat_flags & CF_SYMLINK_ICONV));
#endif
if (inc_recurse && !allow_inc_recurse) {
/* This should only be able to happen in a batch. */
fprintf(stderr,
"Incompatible options specified for inc-recursive %s.\n",
read_batch ? "batch file" : "connection");
"Incompatible options specified for inc-recursive %s.\n",
read_batch ? "batch file" : "connection");
exit_cleanup(RERR_SYNTAX);
}
use_safe_inc_flist = (compat_flags & CF_SAFE_FLIST) || protocol_version >= 31;
need_messages_from_generator = 1;
#if defined HAVE_LUTIMES && defined HAVE_UTIMES
if (compat_flags & CF_INPLACE_PARTIAL_DIR)
inplace_partial = 1;
#ifdef CAN_SET_SYMLINK_TIMES
} else if (!am_sender) {
receiver_symlink_times = 1;
#endif
}
if (read_batch)
do_negotiated_strings = 0;
if (need_unsorted_flist && (!am_sender || inc_recurse))
unsort_ndx = ++file_extra_cnt;
if (partial_dir && *partial_dir != '/' && (!am_server || local_server)) {
int flags = MATCHFLG_NO_PREFIXES | MATCHFLG_DIRECTORY;
int rflags = FILTRULE_NO_PREFIXES | FILTRULE_DIRECTORY;
if (!am_sender || protocol_version >= 30)
flags |= MATCHFLG_PERISHABLE;
parse_rule(&filter_list, partial_dir, flags, 0);
rflags |= FILTRULE_PERISHABLE;
parse_filter_str(&filter_list, partial_dir, rule_template(rflags), 0);
}
@@ -297,11 +817,87 @@ void setup_protocol(int f_out,int f_in)
}
#endif
negotiate_the_strings(f_in, f_out);
if (am_server) {
if (!checksum_seed)
checksum_seed = time(NULL);
checksum_seed = time(NULL) ^ (getpid() << 6);
write_int(f_out, checksum_seed);
} else {
checksum_seed = read_int(f_in);
}
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
}
Vendored Executable → Regular
+1115 -841
View File
File diff suppressed because it is too large. Load diff
Vendored Executable → Regular
+1576 -1277
View File
File diff suppressed because it is too large. Load diff
Vendored
+14 -12
View File
@@ -4,22 +4,24 @@
# then transfer control to the configure.sh script to do the real work.
dir=`dirname $0`
realconfigure="$dir/configure.sh"
if test x"$dir" = x; then
dir=.
fi
if test ! -f "$realconfigure"; then
if test -f "$HOME/build_farm/build_test.fns"; then
# Allow the build farm to grab latest files via rsync.
actions='build fetch'
else
actions='build'
if test "$dir" = '.'; then
branch=`packaging/prep-auto-dir` || exit 1
if test x"$branch" != x; then
cd build || exit 1
dir=..
fi
if "$dir/prepare-source" $actions; then
:
else
fi
if test ! -f configure.sh; then
if ! "$dir/prepare-source" build; then
echo 'Failed to build configure.sh and/or config.h.in -- giving up.' >&2
rm -f "$realconfigure"
rm -f configure.sh
exit 1
fi
fi
exec "$realconfigure" "${@}"
exec ./configure.sh --srcdir="$dir" "${@}"
+1558
View File
File diff suppressed because it is too large. Load diff
-983
View File
@@ -1,983 +0,0 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT()
AC_CONFIG_SRCDIR([byteorder.h])
AC_CONFIG_HEADER(config.h)
AC_PREREQ(2.59)
RSYNC_VERSION=3.0.3
AC_SUBST(RSYNC_VERSION)
AC_MSG_NOTICE([Configuring rsync $RSYNC_VERSION])
AC_DEFINE_UNQUOTED(RSYNC_VERSION, ["$RSYNC_VERSION"], [rsync release version])
LDFLAGS=${LDFLAGS-""}
AC_CANONICAL_TARGET([])
dnl Checks for programs.
AC_PROG_CC
AC_PROG_CPP
AC_PROG_EGREP
AC_PROG_INSTALL
AC_PROG_CC_STDC
AC_SUBST(SHELL)
AC_DEFINE([_GNU_SOURCE], 1,
[Define _GNU_SOURCE so that we get all necessary prototypes])
if test x"$ac_cv_prog_cc_stdc" = x"no"; then
AC_MSG_WARN([rsync requires an ANSI C compiler and you don't seem to have one])
fi
# We must decide this before testing the compiler.
# Please allow this to default to yes, so that your users have more
# chance of getting a useful stack trace if problems occur.
AC_MSG_CHECKING([whether to include debugging symbols])
AC_ARG_ENABLE(debug,
AC_HELP_STRING([--disable-debug],
[disable debugging symbols and features]))
if test x"$enable_debug" = x"no"; then
AC_MSG_RESULT(no)
CFLAGS=${CFLAGS-"-O"}
else
AC_MSG_RESULT([yes])
# leave CFLAGS alone; AC_PROG_CC will try to include -g if it can
dnl AC_DEFINE(DEBUG, 1, [Define to turn on debugging code that may slow normal operation])
dnl CFLAGS=${CFLAGS-"-g"}
fi
AC_ARG_ENABLE(profile,
AC_HELP_STRING([--enable-profile],
[turn on CPU profiling]))
if test x"$enable_profile" = x"yes"; then
CFLAGS="$CFLAGS -pg"
fi
# Specifically, this turns on panic_action handling.
AC_ARG_ENABLE(maintainer-mode,
AC_HELP_STRING([--enable-maintainer-mode],
[turn on extra debug features]))
if test x"$enable_maintainer_mode" = x"yes"; then
CFLAGS="$CFLAGS -DMAINTAINER_MODE"
fi
# This is needed for our included version of popt. Kind of silly, but
# I don't want our version too far out of sync.
CFLAGS="$CFLAGS -DHAVE_CONFIG_H"
# If GCC, turn on warnings.
if test x"$GCC" = x"yes"; then
CFLAGS="$CFLAGS -Wall -W"
fi
AC_ARG_WITH(included-popt,
AC_HELP_STRING([--with-included-popt], [use bundled popt library, not from system]))
AC_ARG_WITH(rsync-path,
AC_HELP_STRING([--with-rsync-path=PATH], [set default --rsync-path to PATH (default: rsync)]),
[ RSYNC_PATH="$with_rsync_path" ],
[ RSYNC_PATH="rsync" ])
AC_DEFINE_UNQUOTED(RSYNC_PATH, "$RSYNC_PATH", [location of rsync on remote machine])
AC_ARG_WITH(rsyncd-conf,
AC_HELP_STRING([--with-rsyncd-conf=PATH], [set configuration file for rsync server to PATH (default: /etc/rsyncd.conf)]),
[ if test ! -z "$with_rsyncd_conf" ; then
case $with_rsyncd_conf in
yes|no)
RSYNCD_SYSCONF="/etc/rsyncd.conf"
;;
/*)
RSYNCD_SYSCONF="$with_rsyncd_conf"
;;
*)
AC_MSG_ERROR(You must specify an absolute path to --with-rsyncd-conf=PATH)
;;
esac
else
RSYNCD_SYSCONF="/etc/rsyncd.conf"
fi ],
[ RSYNCD_SYSCONF="/etc/rsyncd.conf" ])
AC_DEFINE_UNQUOTED(RSYNCD_SYSCONF, "$RSYNCD_SYSCONF", [location of configuration file for rsync server])
AC_ARG_WITH(rsh,
AC_HELP_STRING([--with-rsh=CMD], [set remote shell command to CMD (default: ssh)]))
AC_CHECK_PROG(HAVE_REMSH, remsh, 1, 0)
if test x$HAVE_REMSH = x1; then
AC_DEFINE(HAVE_REMSH, 1, [Define to 1 if remote shell is remsh, not rsh])
fi
if test x"$with_rsh" != x; then
RSYNC_RSH="$with_rsh"
else
RSYNC_RSH="ssh"
fi
AC_DEFINE_UNQUOTED(RSYNC_RSH, "$RSYNC_RSH", [default -e command])
AC_CHECK_PROG(HAVE_YODL2MAN, yodl2man, 1, 0)
if test x$HAVE_YODL2MAN = x1; then
MAKE_MAN=man
fi
AC_ARG_WITH(nobody-group,
AC_HELP_STRING([--with-nobody-group=GROUP],
[set the default unprivileged group (default nobody or nogroup)]),
[ NOBODY_GROUP="$with_nobody_group" ])
if test x"$with_nobody_group" = x; then
AC_MSG_CHECKING([the group for user "nobody"])
if grep '^nobody:' /etc/group >/dev/null 2>&1; then
NOBODY_GROUP=nobody
elif grep '^nogroup:' /etc/group >/dev/null 2>&1; then
NOBODY_GROUP=nogroup
else
NOBODY_GROUP=nobody # test for others?
fi
AC_MSG_RESULT($NOBODY_GROUP)
fi
AC_DEFINE_UNQUOTED(NOBODY_USER, "nobody", [unprivileged user--e.g. nobody])
AC_DEFINE_UNQUOTED(NOBODY_GROUP, "$NOBODY_GROUP", [unprivileged group for unprivileged user])
# arrgh. libc in some old debian version screwed up the largefile
# stuff, getting byte range locking wrong
AC_CACHE_CHECK([for broken largefile support],rsync_cv_HAVE_BROKEN_LARGEFILE,[
AC_TRY_RUN([
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
struct flock lock;
int status;
char tpl[32] = "/tmp/locktest.XXXXXX";
int fd = mkstemp(tpl);
if (fd < 0) {
strcpy(tpl, "conftest.dat");
fd = open(tpl, O_CREAT|O_RDWR, 0600);
}
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 1;
lock.l_pid = 0;
fcntl(fd,F_SETLK,&lock);
if (fork() == 0) {
lock.l_start = 1;
_exit(fcntl(fd,F_SETLK,&lock) == 0);
}
wait(&status);
unlink(tpl);
exit(WEXITSTATUS(status));
}
],
rsync_cv_HAVE_BROKEN_LARGEFILE=yes,rsync_cv_HAVE_BROKEN_LARGEFILE=no,rsync_cv_HAVE_BROKEN_LARGEFILE=cross)])
if test x"$rsync_cv_HAVE_BROKEN_LARGEFILE" != x"yes"; then
AC_SYS_LARGEFILE
fi
ipv6type=unknown
ipv6lib=none
ipv6trylibc=yes
AC_ARG_ENABLE(ipv6,
AC_HELP_STRING([--disable-ipv6],
[don't even try to use IPv6]))
if test x"$enable_ipv6" != x"no"; then
AC_MSG_CHECKING([ipv6 stack type])
for i in inria kame linux-glibc linux-inet6 toshiba v6d zeta; do
case $i in
inria)
# http://www.kame.net/
AC_EGREP_CPP(yes, [
#include <netinet/in.h>
#ifdef IPV6_INRIA_VERSION
yes
#endif],
[ipv6type=$i;
AC_DEFINE(INET6, 1, [true if you have IPv6])
])
;;
kame)
# http://www.kame.net/
AC_EGREP_CPP(yes, [
#include <netinet/in.h>
#ifdef __KAME__
yes
#endif],
[ipv6type=$i;
AC_DEFINE(INET6, 1, [true if you have IPv6])])
;;
linux-glibc)
# http://www.v6.linux.or.jp/
AC_EGREP_CPP(yes, [
#include <features.h>
#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
yes
#endif],
[ipv6type=$i;
AC_DEFINE(INET6, 1, [true if you have IPv6])])
;;
linux-inet6)
# http://www.v6.linux.or.jp/
if test -d /usr/inet6 -o -f /usr/inet6/lib/libinet6.a; then
ipv6type=$i
ipv6lib=inet6
ipv6libdir=/usr/inet6/lib
ipv6trylibc=yes;
AC_DEFINE(INET6, 1, [true if you have IPv6])
CFLAGS="-I/usr/inet6/include $CFLAGS"
fi
;;
toshiba)
AC_EGREP_CPP(yes, [
#include <sys/param.h>
#ifdef _TOSHIBA_INET6
yes
#endif],
[ipv6type=$i;
ipv6lib=inet6;
ipv6libdir=/usr/local/v6/lib;
AC_DEFINE(INET6, 1, [true if you have IPv6])])
;;
v6d)
AC_EGREP_CPP(yes, [
#include </usr/local/v6/include/sys/v6config.h>
#ifdef __V6D__
yes
#endif],
[ipv6type=$i;
ipv6lib=v6;
ipv6libdir=/usr/local/v6/lib;
AC_DEFINE(INET6, 1, [true if you have IPv6])])
;;
zeta)
AC_EGREP_CPP(yes, [
#include <sys/param.h>
#ifdef _ZETA_MINAMI_INET6
yes
#endif],
[ipv6type=$i;
ipv6lib=inet6;
ipv6libdir=/usr/local/v6/lib;
AC_DEFINE(INET6, 1, [true if you have IPv6])])
;;
esac
if test "$ipv6type" != "unknown"; then
break
fi
done
AC_MSG_RESULT($ipv6type)
AC_SEARCH_LIBS(getaddrinfo, inet6)
fi
dnl Do you want to disable use of locale functions
AC_ARG_ENABLE([locale],
AC_HELP_STRING([--disable-locale],
[disable locale features]))
AH_TEMPLATE([CONFIG_LOCALE],
[Undefine if you don't want locale features. By default this is defined.])
if test x"$enable_locale" != x"no"; then
AC_DEFINE(CONFIG_LOCALE)
fi
AC_MSG_CHECKING([whether to call shutdown on all sockets])
case $host_os in
*cygwin* ) AC_MSG_RESULT(yes)
AC_DEFINE(SHUTDOWN_ALL_SOCKETS, 1,
[Define to 1 if sockets need to be shutdown])
;;
* ) AC_MSG_RESULT(no);;
esac
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 grp.h compat.h sys/param.h ctype.h sys/wait.h \
sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h \
sys/un.h sys/attr.h mcheck.h arpa/inet.h arpa/nameser.h locale.h \
netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h \
sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h \
popt.h popt/popt.h)
AC_HEADER_MAJOR
AC_CACHE_CHECK([if makedev takes 3 args],rsync_cv_MAKEDEV_TAKES_3_ARGS,[
AC_TRY_RUN([
#include <sys/types.h>
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
# define makedev mkdev
# endif
#elif defined MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
int main(void)
{
dev_t dev = makedev(0, 5, 7);
if (major(dev) != 5 || minor(dev) != 7)
exit(1);
return 0;
}
],
rsync_cv_MAKEDEV_TAKES_3_ARGS=yes,rsync_cv_MAKEDEV_TAKES_3_ARGS=no,rsync_cv_MAKEDEV_TAKES_3_ARGS=no)])
if test x"$rsync_cv_MAKEDEV_TAKES_3_ARGS" = x"yes"; then
AC_DEFINE(MAKEDEV_TAKES_3_ARGS, 1, [Define to 1 if makedev() takes 3 args])
fi
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(long long)
AC_CHECK_SIZEOF(short)
AC_CHECK_SIZEOF(int16_t)
AC_CHECK_SIZEOF(uint16_t)
AC_CHECK_SIZEOF(int32_t)
AC_CHECK_SIZEOF(uint32_t)
AC_CHECK_SIZEOF(int64_t)
AC_CHECK_SIZEOF(off_t)
AC_CHECK_SIZEOF(off64_t)
AC_CHECK_SIZEOF(time_t)
AC_C_INLINE
AC_C_LONG_DOUBLE
AC_TYPE_SIGNAL
AC_TYPE_UID_T
AC_CHECK_TYPES([mode_t,off_t,size_t,pid_t,id_t])
AC_TYPE_GETGROUPS
AC_CHECK_MEMBERS([struct stat.st_rdev])
TYPE_SOCKLEN_T
AC_CACHE_CHECK([for errno in errno.h],rsync_cv_errno, [
AC_TRY_COMPILE([#include <errno.h>],[int i = errno],
rsync_cv_errno=yes,rsync_cv_have_errno_decl=no)])
if test x"$rsync_cv_errno" = x"yes"; then
AC_DEFINE(HAVE_ERRNO_DECL, 1, [Define to 1 if errno is declared in errno.h])
fi
# The following test taken from the cvs sources
# If we can't find connect, try looking in -lsocket, -lnsl, and -linet.
# These need checks to be before checks for any other functions that
# might be in the same libraries.
# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has
# libsocket.so which has a bad implementation of gethostbyname (it
# only looks in /etc/hosts), so we only look for -lsocket if we need
# it.
AC_CHECK_FUNCS(connect)
if test x"$ac_cv_func_connect" = x"no"; then
case "$LIBS" in
*-lnsl*) ;;
*) AC_CHECK_LIB(nsl_s, printf) ;;
esac
case "$LIBS" in
*-lnsl*) ;;
*) AC_CHECK_LIB(nsl, printf) ;;
esac
case "$LIBS" in
*-lsocket*) ;;
*) AC_CHECK_LIB(socket, connect) ;;
esac
case "$LIBS" in
*-linet*) ;;
*) AC_CHECK_LIB(inet, connect) ;;
esac
dnl We can't just call AC_CHECK_FUNCS(connect) here, because the value
dnl has been cached.
if test x"$ac_cv_lib_socket_connect" = x"yes" ||
test x"$ac_cv_lib_inet_connect" = x"yes"; then
# ac_cv_func_connect=yes
# don't! it would cause AC_CHECK_FUNC to succeed next time configure is run
AC_DEFINE(HAVE_CONNECT, 1, [Define to 1 if you have the "connect" function])
fi
fi
AC_SEARCH_LIBS(inet_ntop, resolv)
# Solaris and HP-UX weirdness:
# Search for libiconv_open (not iconv_open) to discover if -liconv is needed!
AC_SEARCH_LIBS(libiconv_open, iconv)
AC_MSG_CHECKING([for iconv declaration])
AC_CACHE_VAL(am_cv_proto_iconv, [
AC_TRY_COMPILE([
#include <stdlib.h>
#include <iconv.h>
extern
#ifdef __cplusplus
"C"
#endif
#if defined(__STDC__) || defined(__cplusplus)
size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
#else
size_t iconv();
#endif
], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
AC_MSG_RESULT([$]{ac_t:-
}[$]am_cv_proto_iconv)
AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
[Define as const if the declaration of iconv() needs const.])
dnl AC_MSG_NOTICE([Looking in libraries: $LIBS])
AC_CHECK_FUNCS(inet_ntop, , [AC_LIBOBJ(lib/inet_ntop)])
AC_CHECK_FUNCS(inet_pton, , [AC_LIBOBJ(lib/inet_pton)])
AC_HAVE_TYPE([struct addrinfo], [#include <netdb.h>])
AC_HAVE_TYPE([struct sockaddr_storage], [#include <sys/types.h>
#include <sys/socket.h>])
# Irix 6.5 has getaddrinfo but not the corresponding defines, so use
# builtin getaddrinfo if one of the defines don't exist
AC_CACHE_CHECK([whether defines needed by getaddrinfo exist],
rsync_cv_HAVE_GETADDR_DEFINES,[
AC_EGREP_CPP(yes, [
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#ifdef AI_PASSIVE
yes
#endif],
rsync_cv_HAVE_GETADDR_DEFINES=yes,
rsync_cv_HAVE_GETADDR_DEFINES=no)])
if test x"$rsync_cv_HAVE_GETADDR_DEFINES" = x"yes" -a x"$ac_cv_type_struct_addrinfo" = x"yes"; then
# Tru64 UNIX has getaddrinfo() but has it renamed in libc as
# something else so we must include <netdb.h> to get the
# redefinition.
AC_CHECK_FUNCS(getaddrinfo, ,
[AC_MSG_CHECKING([for getaddrinfo by including <netdb.h>])
AC_TRY_LINK([#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>],[getaddrinfo(NULL, NULL, NULL, NULL);],
[AC_MSG_RESULT([yes])
AC_DEFINE(HAVE_GETADDRINFO, 1,
[Define to 1 if you have the "getaddrinfo" function and required types.])],
[AC_MSG_RESULT([no])
AC_LIBOBJ(lib/getaddrinfo)])])
else
AC_LIBOBJ(lib/getaddrinfo)
fi
AC_CHECK_MEMBER([struct sockaddr.sa_len],
[ AC_DEFINE(HAVE_SOCKADDR_LEN, 1, [Do we have sockaddr.sa_len?]) ],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
])
AC_CHECK_MEMBER([struct sockaddr_in.sin_len],
[ AC_DEFINE(HAVE_SOCKADDR_IN_LEN, 1, [Do we have sockaddr_in.sin_len?]) ],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
])
AC_CHECK_MEMBER([struct sockaddr_un.sun_len],
[ AC_DEFINE(HAVE_SOCKADDR_UN_LEN, 1, [Do we have sockaddr_un.sun_len?]) ],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
])
AC_CHECK_MEMBER([struct sockaddr_in6.sin6_scope_id],
[ AC_DEFINE(HAVE_SOCKADDR_IN6_SCOPE_ID, 1, [Do we have sockaddr_in6.sin6_scope_id?]) ],
[],
[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
])
AC_HAVE_TYPE([struct stat64], [#include <stdio.h>
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#if STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# if HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
])
# if we can't find strcasecmp, look in -lresolv (for Unixware at least)
#
AC_CHECK_FUNCS(strcasecmp)
if test x"$ac_cv_func_strcasecmp" = x"no"; then
AC_CHECK_LIB(resolv, strcasecmp)
fi
AC_CHECK_FUNCS(aclsort)
if test x"$ac_cv_func_aclsort" = x"no"; then
AC_CHECK_LIB(sec, aclsort)
fi
dnl At the moment we don't test for a broken memcmp(), because all we
dnl need to do is test for equality, not comparison, and it seems that
dnl every platform has a memcmp that can do at least that.
dnl AC_FUNC_MEMCMP
AC_FUNC_UTIME_NULL
AC_FUNC_ALLOCA
AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
strerror putenv iconv_open locale_charset nl_langinfo getxattr \
extattr_get_link sigaction sigprocmask setattrlist)
dnl cygwin iconv.h defines iconv_open as libiconv_open
if test x"$ac_cv_func_iconv_open" != x"yes"; then
AC_CHECK_FUNC(libiconv_open, [ac_cv_func_iconv_open=yes; AC_DEFINE(HAVE_ICONV_OPEN, 1)])
fi
AC_CHECK_FUNCS(getpgrp tcgetpgrp)
if test $ac_cv_func_getpgrp = yes; then
AC_FUNC_GETPGRP
fi
AC_ARG_ENABLE(iconv,
AC_HELP_STRING([--disable-iconv],
[disable rsync's --iconv option]),
[], [enable_iconv=$ac_cv_func_iconv_open])
AH_TEMPLATE([ICONV_OPTION],
[Define if you want the --iconv option. Specifing a value will set the
default iconv setting (a NULL means no --iconv processing by default).])
if test x"$enable_iconv" != x"no"; then
if test x"$enable_iconv" = x"yes"; then
AC_DEFINE(ICONV_OPTION, NULL)
else
AC_DEFINE_UNQUOTED(ICONV_OPTION, "$enable_iconv")
fi
AC_DEFINE(UTF8_CHARSET, "UTF-8", [String to pass to iconv() for the UTF-8 charset.])
fi
AC_CACHE_CHECK([whether chown() modifies symlinks],rsync_cv_chown_modifies_symlink,[
AC_TRY_RUN([
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdlib.h>
#include <errno.h>
main() {
char const *dangling_symlink = "conftest.dangle";
unlink(dangling_symlink);
if (symlink("conftest.no-such", dangling_symlink) < 0) abort();
if (chown(dangling_symlink, getuid(), getgid()) < 0 && errno == ENOENT) exit(1);
exit(0);
}],
rsync_cv_chown_modifies_symlink=yes,rsync_cv_chown_modifies_symlink=no,rsync_cv_chown_modifies_symlink=no)])
if test $rsync_cv_chown_modifies_symlink = yes; then
AC_DEFINE(CHOWN_MODIFIES_SYMLINK, 1, [Define to 1 if chown modifies symlinks.])
fi
AC_CACHE_CHECK([whether link() can hard-link symlinks],rsync_cv_can_hardlink_symlink,[
AC_TRY_RUN([
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdlib.h>
#include <errno.h>
#define FILENAME "conftest.dangle"
main() {
unlink(FILENAME);
if (symlink("conftest.no-such", FILENAME) < 0) abort();
if (link(FILENAME, FILENAME "2") < 0) exit(1);
exit(0);
}],
rsync_cv_can_hardlink_symlink=yes,rsync_cv_can_hardlink_symlink=no,rsync_cv_can_hardlink_symlink=no)])
if test $rsync_cv_can_hardlink_symlink = yes; then
AC_DEFINE(CAN_HARDLINK_SYMLINK, 1, [Define to 1 if link() can hard-link symlinks.])
fi
AC_CACHE_CHECK([whether link() can hard-link special files],rsync_cv_can_hardlink_special,[
AC_TRY_RUN([
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdlib.h>
#include <errno.h>
#define FILENAME "conftest.fifi"
main() {
unlink(FILENAME);
if (mkfifo(FILENAME, 0777) < 0) abort();
if (link(FILENAME, FILENAME "2") < 0) exit(1);
exit(0);
}],
rsync_cv_can_hardlink_special=yes,rsync_cv_can_hardlink_special=no,rsync_cv_can_hardlink_special=no)])
if test $rsync_cv_can_hardlink_special = yes; then
AC_DEFINE(CAN_HARDLINK_SPECIAL, 1, [Define to 1 if link() can hard-link special files.])
fi
AC_CACHE_CHECK([for working socketpair],rsync_cv_HAVE_SOCKETPAIR,[
AC_TRY_RUN([
#include <sys/types.h>
#include <sys/socket.h>
main() {
int fd[2];
exit((socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != -1) ? 0 : 1);
}],
rsync_cv_HAVE_SOCKETPAIR=yes,rsync_cv_HAVE_SOCKETPAIR=no,rsync_cv_HAVE_SOCKETPAIR=cross)])
if test x"$rsync_cv_HAVE_SOCKETPAIR" = x"yes"; then
AC_DEFINE(HAVE_SOCKETPAIR, 1, [Define to 1 if you have the "socketpair" function])
fi
if test x"$with_included_popt" != x"yes"; then
AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes])
fi
if test x"$ac_cv_header_popt_popt_h" = x"yes"; then
# If the system has /usr/include/popt/popt.h, we enable the
# included popt because an attempt to "#include <popt/popt.h>"
# would use our included header file anyway (due to -I.), and
# might conflict with the system popt.
with_included_popt=yes
elif test x"$ac_cv_header_popt_h" != x"yes"; then
with_included_popt=yes
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="$CFLAGS -I$srcdir/popt"
if test x"$ALLOCA" != x
then
# this can be removed when/if we add an included alloca.c;
# see autoconf documentation on AC_FUNC_ALLOCA
AC_MSG_WARN([included libpopt will use malloc, not alloca (which wastes a small amount of memory)])
fi
else
AC_MSG_RESULT(no)
fi
AC_CACHE_CHECK([for unsigned char],rsync_cv_SIGNED_CHAR_OK,[
AC_TRY_COMPILE([],[signed char *s = ""],
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
AC_CACHE_CHECK([for broken readdir],rsync_cv_HAVE_BROKEN_READDIR,[
AC_TRY_RUN([#include <sys/types.h>
#include <dirent.h>
main() { struct dirent *di; DIR *d = opendir("."); di = readdir(d);
if (di && di->d_name[-2] == '.' && di->d_name[-1] == 0 &&
di->d_name[0] == 0) exit(0); exit(1);} ],
rsync_cv_HAVE_BROKEN_READDIR=yes,rsync_cv_HAVE_BROKEN_READDIR=no,rsync_cv_HAVE_BROKEN_READDIR=cross)])
if test x"$rsync_cv_HAVE_BROKEN_READDIR" = x"yes"; then
AC_DEFINE(HAVE_BROKEN_READDIR, 1, [Define to 1 if readdir() is broken])
fi
AC_CACHE_CHECK([for utimbuf],rsync_cv_HAVE_STRUCT_UTIMBUF,[
AC_TRY_COMPILE([#include <sys/types.h>
#include <utime.h>],
[struct utimbuf tbuf; tbuf.actime = 0; tbuf.modtime = 1; exit(utime("foo.c",&tbuf));],
rsync_cv_HAVE_STRUCT_UTIMBUF=yes,rsync_cv_HAVE_STRUCT_UTIMBUF=no)])
if test x"$rsync_cv_HAVE_STRUCT_UTIMBUF" = x"yes"; then
AC_DEFINE(HAVE_STRUCT_UTIMBUF, 1, [Define to 1 if you have the "struct utimbuf" type])
fi
AC_CACHE_CHECK([if gettimeofday takes tz argument],rsync_cv_HAVE_GETTIMEOFDAY_TZ,[
AC_TRY_COMPILE([#include <sys/time.h>
#include <unistd.h>],
[struct timeval tv; exit(gettimeofday(&tv, NULL));],
rsync_cv_HAVE_GETTIMEOFDAY_TZ=yes,rsync_cv_HAVE_GETTIMEOFDAY_TZ=no)])
if test x"$rsync_cv_HAVE_GETTIMEOFDAY_TZ" != x"no"; then
AC_DEFINE(HAVE_GETTIMEOFDAY_TZ, 1, [Define to 1 if gettimeofday() takes a time-zone arg])
fi
AC_CACHE_CHECK([for C99 vsnprintf],rsync_cv_HAVE_C99_VSNPRINTF,[
AC_TRY_RUN([
#include <sys/types.h>
#include <stdarg.h>
void foo(const char *format, ...) {
va_list ap;
int len;
char buf[5];
va_start(ap, format);
len = vsnprintf(0, 0, format, ap);
va_end(ap);
if (len != 5) exit(1);
if (snprintf(buf, 3, "hello") != 5 || strcmp(buf, "he") != 0) exit(1);
exit(0);
}
main() { foo("hello"); }
],
rsync_cv_HAVE_C99_VSNPRINTF=yes,rsync_cv_HAVE_C99_VSNPRINTF=no,rsync_cv_HAVE_C99_VSNPRINTF=cross)])
if test x"$rsync_cv_HAVE_C99_VSNPRINTF" = x"yes"; then
AC_DEFINE(HAVE_C99_VSNPRINTF, 1, [Define to 1 if vsprintf has a C99-compatible return value])
fi
AC_CACHE_CHECK([for secure mkstemp],rsync_cv_HAVE_SECURE_MKSTEMP,[
AC_TRY_RUN([#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
main() {
struct stat st;
char tpl[20]="/tmp/test.XXXXXX";
int fd = mkstemp(tpl);
if (fd == -1) exit(1);
unlink(tpl);
if (fstat(fd, &st) != 0) exit(1);
if ((st.st_mode & 0777) != 0600) exit(1);
exit(0);
}],
rsync_cv_HAVE_SECURE_MKSTEMP=yes,
rsync_cv_HAVE_SECURE_MKSTEMP=no,
rsync_cv_HAVE_SECURE_MKSTEMP=cross)])
if test x"$rsync_cv_HAVE_SECURE_MKSTEMP" = x"yes"; then
case $target_os in
hpux*)
dnl HP-UX has a broken mkstemp() implementation they refuse to fix,
dnl so we noisily skip using it. See HP change request JAGaf34426
dnl for details. (sbonds)
AC_MSG_WARN(Skipping broken HP-UX mkstemp() -- using mktemp() instead)
;;
*)
AC_DEFINE(HAVE_SECURE_MKSTEMP, 1, [Define to 1 if mkstemp() is available and works right])
;;
esac
fi
AC_CACHE_CHECK([if mknod creates FIFOs],rsync_cv_MKNOD_CREATES_FIFOS,[
AC_TRY_RUN([
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
main() { int rc, ec; char *fn = "fifo-test";
unlink(fn); rc = mknod(fn,S_IFIFO,0600); ec = errno; unlink(fn);
if (rc) {printf("(%d %d) ",rc,ec); return ec;}
return 0;}],
rsync_cv_MKNOD_CREATES_FIFOS=yes,rsync_cv_MKNOD_CREATES_FIFOS=no,rsync_cv_MKNOD_CREATES_FIFOS=cross)])
if test x"$rsync_cv_MKNOD_CREATES_FIFOS" = x"yes"; then
AC_DEFINE(MKNOD_CREATES_FIFOS, 1, [Define to 1 if mknod() can create FIFOs.])
fi
AC_CACHE_CHECK([if mknod creates sockets],rsync_cv_MKNOD_CREATES_SOCKETS,[
AC_TRY_RUN([
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
main() { int rc, ec; char *fn = "sock-test";
unlink(fn); rc = mknod(fn,S_IFSOCK,0600); ec = errno; unlink(fn);
if (rc) {printf("(%d %d) ",rc,ec); return ec;}
return 0;}],
rsync_cv_MKNOD_CREATES_SOCKETS=yes,rsync_cv_MKNOD_CREATES_SOCKETS=no,rsync_cv_MKNOD_CREATES_SOCKETS=cross)])
if test x"$rsync_cv_MKNOD_CREATES_SOCKETS" = x"yes"; then
AC_DEFINE(MKNOD_CREATES_SOCKETS, 1, [Define to 1 if mknod() can create sockets.])
fi
#
# The following test was mostly taken from the tcl/tk plus patches
#
AC_CACHE_CHECK([whether -c -o works],rsync_cv_DASHC_WORKS_WITH_DASHO,[
rm -rf conftest*
cat > conftest.$ac_ext <<EOF
int main() { return 0; }
EOF
${CC-cc} -c -o conftest..o conftest.$ac_ext
if test -f conftest..o; then
rsync_cv_DASHC_WORKS_WITH_DASHO=yes
else
rsync_cv_DASHC_WORKS_WITH_DASHO=no
fi
rm -rf conftest*
])
if test x"$rsync_cv_DASHC_WORKS_WITH_DASHO" = x"yes"; then
OBJ_SAVE="#"
OBJ_RESTORE="#"
CC_SHOBJ_FLAG='-o $@'
else
OBJ_SAVE=' @b=`basename $@ .o`;rm -f $$b.o.sav;if test -f $$b.o; then mv $$b.o $$b.o.sav;fi;'
OBJ_RESTORE=' @b=`basename $@ .o`;if test "$$b.o" != "$@"; then mv $$b.o $@; if test -f $$b.o.sav; then mv $$b.o.sav $$b.o; fi; fi'
CC_SHOBJ_FLAG=""
fi
AC_SUBST(OBJ_SAVE)
AC_SUBST(OBJ_RESTORE)
AC_SUBST(CC_SHOBJ_FLAG)
AC_SUBST(BUILD_POPT)
AC_SUBST(MAKE_MAN)
AC_CHECK_FUNCS(_acl __acl _facl __facl)
#################################################
# check for ACL support
AC_MSG_CHECKING([whether to support ACLs])
AC_ARG_ENABLE(acl-support,
AC_HELP_STRING([--disable-acl-support],
[disable ACL support]))
if test x"$enable_acl_support" = x"no"; then
AC_MSG_RESULT(no)
else
case "$host_os" in
*sysv5*)
AC_MSG_RESULT(Using UnixWare ACLs)
AC_DEFINE(HAVE_UNIXWARE_ACLS, 1, [true if you have UnixWare ACLs])
AC_DEFINE(SUPPORT_ACLS, 1, [Define to 1 to add support for ACLs])
;;
*solaris*|*cygwin*)
AC_MSG_RESULT(Using solaris ACLs)
AC_DEFINE(HAVE_SOLARIS_ACLS, 1, [true if you have solaris ACLs])
AC_DEFINE(SUPPORT_ACLS, 1)
;;
*hpux*)
AC_MSG_RESULT(Using HPUX ACLs)
AC_DEFINE(HAVE_HPUX_ACLS, 1, [true if you have HPUX ACLs])
AC_DEFINE(SUPPORT_ACLS, 1)
;;
*irix*)
AC_MSG_RESULT(Using IRIX ACLs)
AC_DEFINE(HAVE_IRIX_ACLS, 1, [true if you have IRIX ACLs])
AC_DEFINE(SUPPORT_ACLS, 1)
;;
*aix*)
AC_MSG_RESULT(Using AIX ACLs)
AC_DEFINE(HAVE_AIX_ACLS, 1, [true if you have AIX ACLs])
AC_DEFINE(SUPPORT_ACLS, 1)
;;
*osf*)
AC_MSG_RESULT(Using Tru64 ACLs)
AC_DEFINE(HAVE_TRU64_ACLS, 1, [true if you have Tru64 ACLs])
AC_DEFINE(SUPPORT_ACLS, 1)
LIBS="$LIBS -lpacl"
;;
darwin*)
AC_MSG_RESULT(Using OS X ACLs)
AC_DEFINE(HAVE_OSX_ACLS, 1, [true if you have Mac OS X ACLs])
AC_DEFINE(SUPPORT_ACLS, 1)
;;
*)
AC_MSG_RESULT(running tests:)
AC_CHECK_LIB(acl,acl_get_file)
AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
AC_TRY_LINK([#include <sys/types.h>
#include <sys/acl.h>],
[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);],
samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)])
AC_MSG_CHECKING(ACL test results)
if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
AC_MSG_RESULT(Using posix ACLs)
AC_DEFINE(HAVE_POSIX_ACLS, 1, [true if you have posix ACLs])
AC_DEFINE(SUPPORT_ACLS, 1)
AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
AC_TRY_LINK([#include <sys/types.h>
#include <sys/acl.h>],
[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);],
samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)])
if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
AC_DEFINE(HAVE_ACL_GET_PERM_NP, 1, [true if you have acl_get_perm_np])
fi
else
if test x"$enable_acl_support" = x"yes"; then
AC_MSG_ERROR(Failed to find ACL support)
else
AC_MSG_RESULT(No ACL support found)
fi
fi
;;
esac
fi
#################################################
# check for extended attribute support
AC_MSG_CHECKING(whether to support extended attributes)
AC_ARG_ENABLE(xattr-support,
AC_HELP_STRING([--disable-xattr-support],
[disable extended attributes]),
[], [case "$ac_cv_func_getxattr$ac_cv_func_extattr_get_link" in
*yes*) enable_xattr_support=maybe ;;
*) enable_xattr_support=no ;;
esac])
AH_TEMPLATE([SUPPORT_XATTRS],
[Define to 1 to add support for extended attributes])
if test x"$enable_xattr_support" = x"no"; then
AC_MSG_RESULT(no)
else
case "$host_os" in
*linux*)
AC_MSG_RESULT(Using Linux xattrs)
AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs])
AC_DEFINE(SUPPORT_XATTRS, 1)
;;
darwin*)
AC_MSG_RESULT(Using OS X xattrs)
AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs])
AC_DEFINE(SUPPORT_XATTRS, 1)
;;
freebsd*)
AC_MSG_RESULT(Using FreeBSD extattrs)
AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
AC_DEFINE(SUPPORT_XATTRS, 1)
;;
*)
if test x"$enable_xattr_support" = x"yes"; then
AC_MSG_ERROR(Failed to find extended attribute support)
else
AC_MSG_RESULT(No extended attribute support found)
fi
;;
esac
fi
if test x"$enable_acl_support" = x"no" -o x"$enable_xattr_support" = x"no" -o x"$enable_iconv" = x"no"; then
AC_MSG_CHECKING([whether $CC supports -Wno-unused-parameter])
OLD_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wno-unused-parameter"
AC_COMPILE_IFELSE([ ], [rsync_warn_flag=yes], [rsync_warn_flag=no])
AC_MSG_RESULT([$rsync_warn_flag])
if test x"$rsync_warn_flag" = x"no"; then
CFLAGS="$OLD_CFLAGS"
fi
fi
case "$CC" in
' checker'*|checker*)
AC_DEFINE(FORCE_FD_ZERO_MEMSET, 1, [Used to make "checker" understand that FD_ZERO() clears memory.])
;;
esac
AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
AC_OUTPUT
AC_MSG_RESULT()
AC_MSG_RESULT([ rsync ${RSYNC_VERSION} configuration successful])
AC_MSG_RESULT()
+4 -1
View File
@@ -2,6 +2,7 @@
* Support the max connections option.
*
* Copyright (C) 1998 Andrew Tridgell
* Copyright (C) 2006-2020 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,7 +30,9 @@ int claim_connection(char *fname, int max_connections)
if (max_connections == 0)
return 1;
if ((fd = open(fname, O_RDWR|O_CREAT, 0600)) < 0)
/* 'lock file = PATH': refuse symlinks not owned by uid 0 or our euid so
* a planted parent can't redirect the root daemon's O_CREAT open. */
if ((fd = vfs_open_owner_walk(fname, O_RDWR|O_CREAT, 0600, 0)) < 0)
return 0;
/* Find a free spot. */
+38 -16
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
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/awk -f
# The caller must pass arg: daemon-parm.txt
# The resulting code is output into daemon-parm.h
BEGIN {
heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from a list of values in " ARGV[1] "! */\n\n"
sect = psect = defines = accessors = prior_ptype = ""
parms = "\nstatic const struct parm_struct parm_table[] = {"
comment_fmt = "\n/********** %s **********/\n"
tdstruct = "typedef struct {"
}
/^\s*$/ { next }
/^#/ { next }
/^Globals:/ {
if (defines != "") {
print "The Globals section must come first!"
defines = ""
exit
}
defines = tdstruct
values = "\nstatic const all_vars Defaults = {\n { /* Globals: */\n"
exps = exp_values = sprintf(comment_fmt, "EXP")
sect = "GLOBAL"
psect = ", P_GLOBAL, &Vars.g."
next
}
/^Locals:/ {
if (sect == "") {
print "The Locals section must come after the Globals!"
exit
}
defines = defines exps "} global_vars;\n\n" tdstruct
values = values exp_values "\n }, { /* Locals: */\n"
exps = exp_values = sprintf(comment_fmt, "EXP")
sect = "LOCAL"
psect = ", P_LOCAL, &Vars.l."
next
}
/^(STRING|CHAR|PATH|INTEGER|ENUM|OCTAL|BOOL|BOOLREV|BOOL3)[ \t]/ {
ptype = $1
name = $2
$1 = $2 = ""
sub(/^[ \t]+/, "")
if (ptype != prior_ptype) {
comment = sprintf(comment_fmt, ptype)
defines = defines comment
values = values comment
parms = parms "\n"
accessors = accessors "\n"
prior_ptype = ptype
}
if (ptype == "STRING" || ptype == "PATH") {
atype = "STRING"
vtype = "char*"
} else if (ptype ~ /BOOL/) {
atype = vtype = "BOOL"
} else if (ptype == "CHAR") {
atype = "CHAR"
vtype = "char"
} else {
atype = "INTEGER"
vtype = "int"
}
# The name might be var_name|public_name
pubname = name
sub(/\|.*/, "", name)
sub(/.*\|/, "", pubname)
gsub(/_/, " ", pubname)
gsub(/-/, "", name)
if (ptype == "ENUM")
enum = "enum_" name
else
enum = "NULL"
defines = defines "\t" vtype " " name ";\n"
values = values "\t" $0 ", /* " name " */\n"
parms = parms " {\"" pubname "\", P_" ptype psect name ", " enum ", 0},\n"
# The shell-executed hook params (whose %RSYNC_*% expansion is fed to
# /bin/sh) use the _SHELL accessor, which single-quotes peer-controlled
# values to prevent injection. Ordinary string params must NOT quote --
# it would corrupt a documented `path = /home/%RSYNC_USER_NAME%` etc.
if (atype == "STRING" && (name == "early_exec" || name == "prexfer_exec" || name == "postxfer_exec" || name == "name_converter"))
accessors = accessors "FN_" sect "_STRING_SHELL(lp_" name ", " name ")\n"
else
accessors = accessors "FN_" sect "_" atype "(lp_" name ", " name ")\n"
if (vtype == "char*") {
exps = exps "\tBOOL " name "_EXP;\n"
exp_values = exp_values "\tFalse, /* " name "_EXP */\n"
}
next
}
/./ {
print "Extraneous line:" $0
defines = ""
exit
}
END {
if (sect != "" && defines != "") {
defines = defines exps "} local_vars;\n\n"
defines = defines tdstruct "\n\tglobal_vars g;\n\tlocal_vars l;\n} all_vars;\n"
values = values exp_values "\n }\n};\n\nstatic all_vars Vars;\n"
parms = parms "\n {NULL, P_BOOL, P_NONE, NULL, NULL, 0}\n};\n"
print heading defines values parms accessors > "daemon-parm.h"
} else {
print "Failed to parse the data in " ARGV[1]
exit 1
}
}
+71
View File
@@ -0,0 +1,71 @@
Globals: ================================================================
STRING bind_address|address NULL
STRING daemon_chroot NULL
STRING daemon_gid NULL
STRING daemon_uid NULL
STRING motd_file NULL
STRING pid_file NULL
STRING proxy_protocol_hosts NULL
STRING socket_options NULL
INTEGER listen_backlog 5
INTEGER rsync_port|port 0
BOOL proxy_protocol False
Locals: =================================================================
STRING auth_digest NULL
STRING auth_users NULL
STRING charset NULL
STRING comment NULL
STRING dont_compress DEFAULT_DONT_COMPRESS
STRING early_exec NULL
STRING exclude NULL
STRING exclude_from NULL
STRING filter NULL
STRING gid NULL
STRING hosts_allow NULL
STRING hosts_deny NULL
STRING include NULL
STRING include_from NULL
STRING incoming_chmod NULL
STRING lock_file DEFAULT_LOCK_FILE
STRING log_file NULL
STRING log_format "%o %h [%a] %m (%u) %f %l"
STRING name NULL
STRING name_converter NULL
STRING outgoing_chmod NULL
STRING post-xfer_exec NULL
STRING pre-xfer_exec NULL
STRING refuse_options NULL
STRING secrets_file NULL
STRING syslog_tag "rsyncd"
STRING uid NULL
PATH path NULL
PATH temp_dir NULL
INTEGER max_connections 0
INTEGER max_verbosity 1
INTEGER timeout 0
ENUM syslog_facility LOG_DAEMON
BOOL fake_super False
BOOL forward_lookup True
BOOL ignore_errors False
BOOL ignore_nonreadable False
BOOL insecure_links False
BOOL list True
BOOL read_only True
BOOL reverse_lookup True
BOOL strict_modes True
BOOL transfer_logging False
BOOL write_only False
BOOL3 munge_symlinks Unset
BOOL3 numeric_ids Unset
BOOL3 open_noatime Unset
BOOL3 use_chroot Unset
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/awk -f
# The caller must pass args: -v hfile=NAME rsync.1.md
BEGIN {
heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from a list of values in " ARGV[1] "! */"
if (hfile ~ /compress/) {
define = "#define DEFAULT_DONT_COMPRESS"
prefix = "*."
} else {
define = "#define DEFAULT_CVSIGNORE"
prefix = ""
}
value_list = ""
}
/^ > [^ ]+$/ {
gsub(/`/, "")
if (value_list != "") value_list = value_list " "
value_list = value_list prefix $2
next
}
value_list ~ /\.gz / && hfile ~ /compress/ {
exit
}
value_list ~ /SCCS / && hfile ~ /cvsignore/ {
exit
}
value_list = ""
END {
if (value_list != "")
print heading "\n\n" define " \"" value_list "\"" > hfile
else {
print "Failed to find a value list in " ARGV[1] " for " hfile
exit 1
}
}
+310
View File
@@ -0,0 +1,310 @@
/*
* Deletion routines used in rsync.
*
* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, visit the http://fsf.org website.
*/
#include "rsync.h"
extern int am_root;
extern int make_backups;
extern int max_delete;
extern char *backup_dir;
extern char *backup_suffix;
extern int backup_suffix_len;
extern struct stats stats;
int ignore_perishable = 0;
int non_perishable_cnt = 0;
int skipped_deletes = 0;
/* Held fd of the directory whose contents delete_dir_contents() is currently
* removing, so delete_item()'s per-entry rmdir/unlink/chmod go through it
* instead of re-resolving the full path for every entry. Set (with save/
* restore across the recursion) around the delete loop; -1 outside a recursive
* delete or when the secure resolver is gated off (chroot / non-receiver) or
* the path doesn't live directly in that dir. */
static int del_dirfd = -1;
static const char *del_dir_prefix;
static int del_dir_prefix_len;
/* If `path` is a single component directly inside the dir being deleted,
* point *leaf at its basename and return the held dir fd; else return -1. */
static int del_held_dfd(const char *path, const char **leaf)
{
if (del_dirfd >= 0
&& strncmp(path, del_dir_prefix, del_dir_prefix_len) == 0
&& path[del_dir_prefix_len] == '/'
&& strchr(path + del_dir_prefix_len + 1, '/') == NULL) {
*leaf = path + del_dir_prefix_len + 1;
return del_dirfd;
}
return -1;
}
static void del_chmod(const char *fbuf, mode_t mode)
{
const char *leaf;
int dfd = del_held_dfd(fbuf, &leaf);
if (dfd >= 0)
vfs_chmod(dfd, leaf, mode, 0);
else
vfs_chmod(VFS_AT_FDCWD, fbuf, mode, 0);
}
/* vfs_flags carries VFS_OPERATOR_PATH for a backup-tree delete (DEL_FOR_BACKUP):
* the path-based fallback then resolves the leaf's parent via the ownership walk,
* matching the confinement the base gives this unlink under make_backup() (where
* the held dirfd is absent for a cross-tree --backup-dir leaf). A held-dirfd
* delete is already confined, so it ignores the flag. */
static int del_unlink(const char *fbuf, int vfs_flags)
{
const char *leaf;
int dfd = del_held_dfd(fbuf, &leaf);
if (dfd >= 0 && vfs_unlink(dfd, leaf, 0) == 0)
return 0;
return robust_unlink(fbuf, vfs_flags); /* fall back (ETXTBSY retry, or not held) */
}
static inline int is_backup_file(char *fn)
{
int k = strlen(fn) - backup_suffix_len;
return k > 0 && strcmp(fn+k, backup_suffix) == 0;
}
/* The directory is about to be deleted: if DEL_RECURSE is given, delete all
* its contents, otherwise just checks for content. Returns DR_SUCCESS or
* DR_NOT_EMPTY. Note that fname must point to a MAXPATHLEN buffer! (The
* buffer is used for recursion, but returned unchanged.)
*/
static enum delret delete_dir_contents(char *fname, uint16 flags)
{
struct file_list *dirlist;
enum delret ret;
unsigned remainder;
void *save_filters;
int j, dlen;
char *p;
if (DEBUG_GTE(DEL, 3)) {
rprintf(FINFO, "delete_dir_contents(%s) flags=%d\n",
fname, flags);
}
dlen = strlen(fname);
save_filters = push_local_filters(fname, dlen);
non_perishable_cnt = 0;
dirlist = get_dirlist(fname, dlen, 0);
ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS;
if (!dirlist->used)
goto done;
if (!(flags & DEL_RECURSE)) {
ret = DR_NOT_EMPTY;
goto done;
}
p = fname + dlen;
if (dlen != 1 || *fname != '/')
*p++ = '/';
remainder = MAXPATHLEN - (p - fname);
/* We do our own recursion, so make delete_item() non-recursive. */
flags = (flags & ~(DEL_RECURSE|DEL_MAKE_ROOM|DEL_NO_UID_WRITE))
| DEL_DIR_IS_EMPTY;
/* Hold this dir open so the per-entry chmod/rmdir/unlink below (and in
* delete_item) become *at() calls against it rather than re-resolving the
* full path for every entry. Save/restore around the recursion. */
int save_del_dirfd = del_dirfd;
const char *save_del_prefix = del_dir_prefix;
int save_del_prefix_len = del_dir_prefix_len;
fname[dlen] = '\0';
del_dirfd = vfs_opendir(fname);
fname[dlen] = '/';
del_dir_prefix = fname;
del_dir_prefix_len = dlen;
for (j = dirlist->used; j--; ) {
struct file_struct *fp = dirlist->files[j];
if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) {
if (DEBUG_GTE(DEL, 1)) {
rprintf(FINFO,
"mount point, %s, pins parent directory\n",
f_name(fp, NULL));
}
ret = DR_NOT_EMPTY;
continue;
}
strlcpy(p, fp->basename, remainder);
if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
del_chmod(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)
ret = DR_NOT_EMPTY;
}
if (delete_item(fname, fp->mode, flags) != DR_SUCCESS)
ret = DR_NOT_EMPTY;
}
if (del_dirfd >= 0)
close(del_dirfd);
del_dirfd = save_del_dirfd;
del_dir_prefix = save_del_prefix;
del_dir_prefix_len = save_del_prefix_len;
fname[dlen] = '\0';
done:
flist_free(dirlist);
pop_local_filters(save_filters);
if (ret == DR_NOT_EMPTY) {
rprintf(FINFO, "cannot delete non-empty directory: %s\n",
fname);
}
return ret;
}
/* Delete a file or directory. If DEL_RECURSE is set in the flags, this will
* delete recursively.
*
* Note that fbuf must point to a MAXPATHLEN buffer if the mode indicates it's
* a directory! (The buffer is used for recursion, but returned unchanged.)
*/
enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
{
enum delret ret;
char *what;
int ok;
if (DEBUG_GTE(DEL, 2)) {
rprintf(FINFO, "delete_item(%s) mode=%o flags=%d\n",
fbuf, (int)mode, (int)flags);
}
if (flags & DEL_NO_UID_WRITE)
del_chmod(fbuf, mode | S_IWUSR);
if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
/* This only happens on the first call to delete_item() since
* delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
ignore_perishable = 1;
/* If DEL_RECURSE is not set, this just reports emptiness. */
ret = delete_dir_contents(fbuf, flags);
ignore_perishable = 0;
if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT)
goto check_ret;
/* OK: try to delete the directory. */
}
if (!(flags & DEL_MAKE_ROOM) && max_delete >= 0 && stats.deleted_files >= max_delete) {
skipped_deletes++;
return DR_AT_LIMIT;
}
if (S_ISDIR(mode)) {
const char *leaf;
int dfd = del_held_dfd(fbuf, &leaf);
what = "rmdir";
ok = (dfd >= 0 ? vfs_unlink(dfd, leaf, VFS_REMOVEDIR)
: vfs_unlink(VFS_AT_FDCWD, fbuf,
VFS_REMOVEDIR | ((flags & DEL_FOR_BACKUP) ? VFS_OPERATOR_PATH : 0))) == 0;
} else {
if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
what = "make_backup";
ok = make_backup(fbuf, True);
if (ok == 2) {
what = "unlink";
ok = del_unlink(fbuf, (flags & DEL_FOR_BACKUP) ? VFS_OPERATOR_PATH : 0) == 0;
}
} else {
what = "unlink";
ok = del_unlink(fbuf, (flags & DEL_FOR_BACKUP) ? VFS_OPERATOR_PATH : 0) == 0;
}
}
if (ok) {
if (!(flags & DEL_MAKE_ROOM)) {
log_delete(fbuf, mode);
stats.deleted_files++;
if (S_ISREG(mode)) {
/* Nothing more to count */
} else if (S_ISDIR(mode))
stats.deleted_dirs++;
#ifdef SUPPORT_LINKS
else if (S_ISLNK(mode))
stats.deleted_symlinks++;
#endif
else if (IS_DEVICE(mode))
stats.deleted_devices++;
else
stats.deleted_specials++;
}
ret = DR_SUCCESS;
} else {
if (S_ISDIR(mode) && errno == ENOTEMPTY) {
rprintf(FINFO, "cannot delete non-empty directory: %s\n",
fbuf);
ret = DR_NOT_EMPTY;
} else if (errno != ENOENT) {
rsyserr(FERROR_XFER, errno, "delete_file: %s(%s) failed",
what, fbuf);
ret = DR_FAILURE;
} else
ret = DR_SUCCESS;
}
check_ret:
if (ret != DR_SUCCESS && flags & DEL_MAKE_ROOM) {
const char *desc;
switch (flags & DEL_MAKE_ROOM) {
case DEL_FOR_FILE: desc = "regular file"; break;
case DEL_FOR_DIR: desc = "directory"; break;
case DEL_FOR_SYMLINK: desc = "symlink"; break;
case DEL_FOR_DEVICE: desc = "device file"; break;
case DEL_FOR_SPECIAL: desc = "special file"; break;
default: exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE */
}
rprintf(FERROR_XFER, "could not make way for %s %s: %s\n",
flags & DEL_FOR_BACKUP ? "backup" : "new",
desc, fbuf);
}
return ret;
}
uint16 get_del_for_flag(uint16 mode)
{
if (S_ISREG(mode))
return DEL_FOR_FILE;
if (S_ISDIR(mode))
return DEL_FOR_DIR;
if (S_ISLNK(mode))
return DEL_FOR_SYMLINK;
if (IS_DEVICE(mode))
return DEL_FOR_DEVICE;
if (IS_SPECIAL(mode))
return DEL_FOR_SPECIAL;
exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE */
}
-20
View File
@@ -1,20 +0,0 @@
Handling the rsync SGML documentation
rsync documentation is now primarily in Docbook format. Docbook is an
SGML/XML documentation format that is becoming standard on free
operating systems. It's also used for Samba documentation.
The SGML files are source code that can be translated into various
useful output formats, primarily PDF, HTML, Postscript and plain text.
To do this transformation on Debian, you should install the
docbook-utils package. Having done that, you can say
docbook2pdf rsync.sgml
and so on.
On other systems you probably need James Clark's "sp" and "JadeTeX"
packages. Work it out for yourself and send a note to the mailing
list.
-42
View File
@@ -1,42 +0,0 @@
Notes on rsync profiling
strlcpy is hot:
0.00 0.00 1/7735635 push_dir [68]
0.00 0.00 1/7735635 pop_dir [71]
0.00 0.00 1/7735635 send_file_list [15]
0.01 0.00 18857/7735635 send_files [4]
0.04 0.00 129260/7735635 send_file_entry [18]
0.04 0.00 129260/7735635 make_file [20]
0.04 0.00 141666/7735635 send_directory <cycle 1> [36]
2.29 0.00 7316589/7735635 f_name [13]
[14] 11.7 2.42 0.00 7735635 strlcpy [14]
Here's the top few functions:
46.23 9.57 9.57 13160929 0.00 0.00 mdfour64
14.78 12.63 3.06 13160929 0.00 0.00 copy64
11.69 15.05 2.42 7735635 0.00 0.00 strlcpy
10.05 17.13 2.08 41438 0.05 0.38 sum_update
4.11 17.98 0.85 13159996 0.00 0.00 mdfour_update
1.50 18.29 0.31 file_compare
1.45 18.59 0.30 129261 0.00 0.01 send_file_entry
1.23 18.84 0.26 2557585 0.00 0.00 f_name
1.11 19.07 0.23 1483750 0.00 0.00 u_strcmp
1.11 19.30 0.23 118129 0.00 0.00 writefd_unbuffered
0.92 19.50 0.19 1085011 0.00 0.00 writefd
0.43 19.59 0.09 156987 0.00 0.00 read_timeout
0.43 19.68 0.09 129261 0.00 0.00 clean_fname
0.39 19.75 0.08 32887 0.00 0.38 matched
0.34 19.82 0.07 1 70.00 16293.92 send_files
0.29 19.89 0.06 129260 0.00 0.00 make_file
0.29 19.95 0.06 75430 0.00 0.00 read_unbuffered
mdfour could perhaps be made faster:
/* NOTE: This code makes no attempt to be fast! */
There might be an optimized version somewhere that we can borrow.
-351
View File
@@ -1,351 +0,0 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
<book id="rsync">
<bookinfo>
<title>rsync</title>
<copyright>
<year>1996 -- 2002</year>
<holder>Martin Pool</holder>
<holder>Andrew Tridgell</holder>
</copyright>
<author>
<firstname>Martin</firstname>
<surname>Pool</surname>
</author>
</bookinfo>
<chapter>
<title>Introduction</title>
<para>rsync is a flexible program for efficiently copying files or
directory trees.
<para>rsync has many options to select which files will be copied
and how they are to be transferred. It may be used as an
alternative to ftp, http, scp or rcp.
<para>The rsync remote-update protocol allows rsync to transfer just
the differences between two sets of files across the network link,
using an efficient checksum-search algorithm described in the
technical report that accompanies this package.</para>
<para>Some of the additional features of rsync are:</para>
<itemizedlist>
<listitem>
<para>support for copying links, devices, owners, groups and
permissions
</para>
</listitem>
<listitem>
<para>
exclude and exclude-from options similar to GNU tar
</para>
</listitem>
<listitem>
<para>
a CVS exclude mode for ignoring the same files that CVS would ignore
</listitem>
<listitem>
<para>
can use any transparent remote shell, including rsh or ssh
</listitem>
<listitem>
<para>
does not require root privileges
</listitem>
<listitem>
<para>
pipelining of file transfers to minimize latency costs
</listitem>
<listitem>
<para>
support for anonymous or authenticated rsync servers (ideal for
mirroring)
</para>
</listitem>
</itemizedlist>
</chapter>
<chapter>
<title>Using rsync</title>
<section>
<title>
Introductory example
</title>
<para>
Probably the most common case of rsync usage is to copy files
to or from a remote machine using
<application>ssh</application> as a network transport. In
this situation rsync is a good alternative to
<application>scp</application>.
</para>
<para>
The most commonly used arguments for rsync are
</para>
<variablelist>
<varlistentry>
<term><option>-v</option></term>
<listitem>
<para>Be verbose. Primarily, display the name of each file as it is copied.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-a</option></term>
<listitem>
<para>
Reproduce the structure and attributes of the origin files as exactly
as possible: this includes copying subdirectories, symlinks, special
files, ownership and permissions. (@xref{Attributes to
copy}.)
</para>
</listitem>
</varlistentry>
</variablelist>
<para><option>-v </option>
<para><option>-z</option>
Compress network traffic, using a modified version of the
@command{zlib} library.</para>
<para><option>-P</option>
Display a progress indicator while files are transferred. This should
normally be ommitted if rsync is not run on a terminal.
</para>
</section>
<section>
<title>Local and remote</title>
<para>There are six different ways of using rsync. They
are:</para>
<!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE MSGSET PROCEDURE SIDEBAR QANDASET ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS ABSTRACT AUTHORBLURB EPIGRAPH INDEXTERM REFENTRY SECTION) -->
<orderedlist>
<listitem>
<para>
for copying local files. This is invoked when neither
source nor destination path contains a @code{:} separator
<listitem>
<para>
for copying from the local machine to a remote machine using
a remote shell program as the transport (such as rsh or
ssh). This is invoked when the destination path contains a
single @code{:} separator.
<listitem>
<para>
for copying from a remote machine to the local machine
using a remote shell program. This is invoked when the source
contains a @code{:} separator.
<listitem>
<para>
for copying from a remote rsync server to the local
machine. This is invoked when the source path contains a @code{::}
separator or a @code{rsync://} URL.
<listitem>
<para>
for copying from the local machine to a remote rsync
server. This is invoked when the destination path contains a @code{::}
separator.
<listitem>
<para>
for listing files on a remote machine. This is done the
same way as rsync transfers except that you leave off the
local destination.
</listitem>
</orderedlist>
<para>
Note that in all cases (other than listing) at least one of the source
and destination paths must be local.
<para>
Any one invocation of rsync makes a copy in a single direction. rsync
currently has no equivalent of @command{ftp}'s interactive mode.
@cindex @sc{nfs}
@cindex network filesystems
@cindex remote filesystems
<para>
rsync's network protocol is generally faster at copying files than
network filesystems such as @sc{nfs} or @sc{cifs}. It is better to
run rsync on the file server either as a daemon or over ssh than
running rsync giving the network directory.
</para>
</section>
</chapter>
<chapter>
<title>Frequently asked questions</title>
<!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE MSGSET PROCEDURE SIDEBAR QANDASET ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS ABSTRACT AUTHORBLURB EPIGRAPH INDEXTERM SECTION SIMPLESECT REFENTRY SECT1) -->
<qandaset>
<!-- one of (QANDADIV QANDAENTRY) -->
<qandaentry>
<question>
<!-- one of (CALLOUTLIST GLOSSLIST ITEMIZEDLIST ORDEREDLIST
SEGMENTEDLIST SIMPLELIST VARIABLELIST CAUTION IMPORTANT NOTE
TIP WARNING LITERALLAYOUT PROGRAMLISTING PROGRAMLISTINGCO
SCREEN SCREENCO SCREENSHOT SYNOPSIS CMDSYNOPSIS FUNCSYNOPSIS
CLASSSYNOPSIS FIELDSYNOPSIS CONSTRUCTORSYNOPSIS
DESTRUCTORSYNOPSIS METHODSYNOPSIS FORMALPARA PARA SIMPARA
ADDRESS BLOCKQUOTE GRAPHIC GRAPHICCO MEDIAOBJECT
MEDIAOBJECTCO INFORMALEQUATION INFORMALEXAMPLE
INFORMALFIGURE INFORMALTABLE EQUATION EXAMPLE FIGURE TABLE
PROCEDURE ANCHOR BRIDGEHEAD REMARK HIGHLIGHTS INDEXTERM) -->
<para>Are there mailing lists for rsync?
</question>
<answer>
<para>Yes, and you can subscribe and unsubscribe through a
web interface at
<ulink
url="http://lists.samba.org/">http://lists.samba.org/</ulink>
</para>
<para>
If you are having trouble with the mailing list, please
send mail to the administrator
<email>rsync-admin@lists.samba.org</email>
not to the list itself.
</para>
<para>
The mailing list archives are searchable. Use
<ulink url="http://google.com/">Google</ulink> and prepend
the search with <userinput>site:lists.samba.org
rsync</userinput>, plus relevant keywords.
</para>
</answer>
</qandaentry>
<qandaentry>
<question>
<para>
Why is rsync so much bigger when I build it with
<command>gcc</command>?
</para>
</question>
<answer>
<para>
On gcc, rsync builds by default with debug symbols
included. If you strip both executables, they should end
up about the same size. (Use <command>make
install-strip</command>.)
</para>
</answer>
</qandaentry>
<qandaentry>
<question>
<para>Is rsync useful for a single large file like an ISO image?</para>
</question>
<answer>
<para>
Yes, but note the following:
<para>
Background: A common use of rsync is to update a file (or set of files) in one location from a more
correct or up-to-date copy in another location, taking advantage of portions of the files that are
identical to speed up the process. (Note that rsync will transfer a file in its entirety if no copy
exists at the destination.)
<para>
(This discussion is written in terms of updating a local copy of a file from a correct file in a
remote location, although rsync can work in either direction.)
<para>
The file to be updated (the local file) must be in a destination directory that has enough space for
two copies of the file. (In addition, keep an extra copy of the file to be updated in a different
location for safety -- see the discussion (below) about rsync's behavior when the rsync process is
interrupted before completion.)
<para>
The local file must have the same name as the remote file being sync'd to (I think?). If you are
trying to upgrade an iso from, for example, beta1 to beta2, rename the local file to the same name
as the beta2 file. *(This is a useful thing to do -- only the changed portions will be
transmitted.)*
<para>
The extra copy of the local file kept in a different location is because of rsync's behavior if
interrupted before completion:
<para>
* If you specify the --partial option and rsync is interrupted, rsync will save the partially
rsync'd file and throw away the original local copy. (The partially rsync'd file is correct but
truncated.) If rsync is restarted, it will not have a local copy of the file to check for duplicate
blocks beyond the section of the file that has already been rsync'd, thus the remainder of the rsync
process will be a "pure transfer" of the file rather than taking advantage of the rsync algorithm.
<para>
* If you don't specify the --partial option and rsync is interrupted, rsync will throw away the
partially rsync'd file, and, when rsync is restarted starts the rsync process over from the
beginning.
<para>
Which of these is most desirable depends on the degree of commonality between the local and remote
copies of the file *and how much progress was made before the interruption*.
<para>
The ideal approach after an interruption would be to create a new file by taking the original file
and deleting a portion equal in size to the portion already rsync'd and then appending *the
remaining* portion to the portion of the file that has already been rsync'd. (There has been some
discussion about creating an option to do this automatically.)
The --compare-dest option is useful when transferring multiple files, but is of no benefit in
transferring a single file. (AFAIK)
*Other potentially useful information can be found at:
-[3]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFile
This answer, formatted with "real" bullets, can be found at:
-[4]http://twiki.org/cgi-bin/view/Wikilearn/RsyncingALargeFileFAQ*
</para>
</answer>
</qandaentry>
</qandaset>
</chapter>
<appendix>
<title>Other Resources</title>
<para><ulink url="http://www.ccp14.ac.uk/ccp14admin/rsync/"></ulink></para>
</appendix>
</book>
+1 -1
View File
@@ -2,7 +2,7 @@
* Error codes returned by rsync.
*
* Copyright (C) 1998-2000 Andrew Tridgell
* Copyright (C) 2003-2008 Wayne Davison
* Copyright (C) 2003-2019 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
+1113 -407
View File
File diff suppressed because it is too large. Load diff
+182 -66
View File
@@ -3,7 +3,7 @@
*
* Copyright (C) 1998 Andrew Tridgell
* Copyright (C) 2002 Martin Pool
* Copyright (C) 2004-2008 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
@@ -20,68 +20,166 @@
*/
#include "rsync.h"
#include "inums.h"
#ifndef ENODATA
#define ENODATA EAGAIN
#endif
/* We want all reads to be aligned on 1K boundaries. */
#define ALIGN_BOUNDARY 1024
/* How far past the boundary is an offset? */
#define ALIGNED_OVERSHOOT(oft) ((oft) & (ALIGN_BOUNDARY-1))
/* Round up a length to the next boundary */
#define ALIGNED_LENGTH(len) ((((len) - 1) | (ALIGN_BOUNDARY-1)) + 1)
extern int sparse_files;
static char last_byte;
static size_t sparse_seek = 0;
OFF_T preallocated_len = 0;
int sparse_end(int f)
static OFF_T sparse_seek = 0;
static OFF_T sparse_past_write = 0;
int sparse_end(int f, OFF_T size, int updating_basis_or_equiv)
{
int ret;
int ret = 0;
if (!sparse_seek)
return 0;
if (updating_basis_or_equiv) {
if (sparse_seek && vfs_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 = vfs_ftruncate(f, size);
#endif
} else if (sparse_seek) {
#ifdef HAVE_FTRUNCATE
ret = vfs_ftruncate(f, size);
#else
if (vfs_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
ret = -1;
else {
do {
ret = write(f, "", 1);
} while (ret < 0 && errno == EINTR);
do_lseek(f, sparse_seek-1, SEEK_CUR);
sparse_seek = 0;
ret = ret <= 0 ? -1 : 0;
}
#endif
}
do {
ret = write(f, "", 1);
} while (ret < 0 && errno == EINTR);
sparse_past_write = sparse_seek = 0;
return ret <= 0 ? -1 : 0;
return ret;
}
static int write_sparse(int f, char *buf, size_t len)
/* Note that the offset is just the caller letting us know where
* the current file position is in the file. The use_seek arg tells
* us that we should seek over matching data instead of writing it. */
/* Flush any deferred run of zero bytes as a hole, advancing the file
* position past it (both vfs_lseek() and vfs_punch_hole() move the offset). */
static int flush_sparse_hole(int f)
{
size_t l1 = 0, l2 = 0;
int ret;
if (!sparse_seek)
return 0;
if (sparse_past_write >= preallocated_len) {
if (vfs_lseek(f, sparse_seek, SEEK_CUR) < 0) {
sparse_seek = 0;
return -1;
}
} else if (vfs_punch_hole(f, sparse_past_write, sparse_seek) < 0) {
sparse_seek = 0;
return -1;
}
sparse_seek = 0;
return 0;
}
static int full_sparse_write(int f, const char *buf, int len)
{
while (len > 0) {
int ret = write(f, buf, len);
if (ret <= 0) {
if (ret < 0 && errno == EINTR)
continue;
sparse_seek = 0;
return -1;
}
buf += ret;
len -= ret;
}
return 0;
}
/* Emit one span of data that is not being turned into a hole. For an in-place
* update (use_seek) the bytes on disk already match, so we only need to move
* past them; otherwise we write them out. Either way a deferred hole is
* flushed first so that the span lands at the right offset. */
static int emit_sparse_span(int f, int use_seek, const char *buf, int len)
{
if (flush_sparse_hole(f) < 0)
return -1;
if (use_seek)
return vfs_lseek(f, len, SEEK_CUR) < 0 ? -1 : 0;
return full_sparse_write(f, buf, len);
}
static int write_sparse(int f, int use_seek, OFF_T offset, const char *buf, int len)
{
int l1, l2, i, start, end;
/* Always treat a leading and trailing run of zeros as a (deferred)
* hole, since they may merge with holes in the adjacent write calls. */
for (l1 = 0; l1 < len && buf[l1] == 0; l1++) {}
for (l2 = 0; l2 < len-l1 && buf[len-(l2+1)] == 0; l2++) {}
/* XXX Riddle me this: why does this function SLOW DOWN when I
* remove the following (unneeded) line?? Core Duo weirdness? */
last_byte = buf[len-1];
sparse_seek += l1;
if (l1 == len)
return len;
if (sparse_seek)
do_lseek(f, sparse_seek, SEEK_CUR);
sparse_seek = l2;
while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) {
if (ret < 0 && errno == EINTR)
/* Scan the middle [l1, len-l2) for interior runs of zeros that are at
* least SPARSE_WRITE_SIZE long (the hole granularity rsync has always
* used) and defer those as holes. Everything in between -- which may
* include shorter zero runs not worth a hole -- is emitted in one go,
* rather than being chopped into SPARSE_WRITE_SIZE-byte pieces, which
* made copying a large non-sparse file cost ~one write() per KiB.
*
* The matched (use_seek) case runs through the same scan: its interior
* zero runs still have to be punched out, which is what --inplace
* --sparse relies on to keep a hole-y basis file sparse. */
start = l1;
end = len - l2;
for (i = l1; i < end; ) {
int z;
if (buf[i] != 0) {
i++;
continue;
return ret;
}
for (z = 1; i + z < end && buf[i+z] == 0; z++) {}
if (z < SPARSE_WRITE_SIZE) {
i += z;
continue;
}
if (i > start) {
if (emit_sparse_span(f, use_seek, buf + start, i - start) < 0)
return -1;
sparse_past_write = offset + i;
}
sparse_seek += z;
i += z;
start = i;
}
if (end > start) {
if (emit_sparse_span(f, use_seek, buf + start, end - start) < 0)
return -1;
}
if (ret != (int)(len - (l1+l2)))
return l1+ret;
sparse_seek = l2;
sparse_past_write = offset + len - l2;
return len;
}
static char *wf_writeBuf;
static size_t wf_writeBufSize;
static size_t wf_writeBufCnt;
@@ -103,29 +201,28 @@ int flush_write_file(int f)
return ret;
}
/*
* write_file does not allow incomplete writes. It loops internally
* until len bytes are written or errno is set.
*/
int write_file(int f,char *buf,size_t len)
/* write_file does not allow incomplete writes. It loops internally
* until len bytes are written or errno is set. Note that use_seek and
* offset are only used in sparse processing (see write_sparse()). */
int write_file(int f, int use_seek, OFF_T offset, const char *buf, int len)
{
int ret = 0;
while (len > 0) {
int r1;
if (sparse_files > 0) {
int len1 = MIN(len, SPARSE_WRITE_SIZE);
r1 = write_sparse(f, buf, len1);
/* write_sparse() handles the whole span itself, scanning
* for holes and coalescing the non-zero data into large
* write()s instead of SPARSE_WRITE_SIZE-byte dribbles. */
r1 = write_sparse(f, use_seek, offset, buf, len);
offset += r1;
} else {
if (!wf_writeBuf) {
wf_writeBufSize = WRITE_SIZE * 8;
wf_writeBufCnt = 0;
wf_writeBuf = new_array(char, wf_writeBufSize);
if (!wf_writeBuf)
out_of_memory("write_file");
}
r1 = MIN(len, wf_writeBufSize - wf_writeBufCnt);
r1 = (int)MIN((size_t)len, wf_writeBufSize - wf_writeBufCnt);
if (r1) {
memcpy(wf_writeBuf + wf_writeBufCnt, buf, r1);
wf_writeBufCnt += r1;
@@ -149,25 +246,47 @@ int write_file(int f,char *buf,size_t len)
return ret;
}
/* An in-place update found identical data at an identical location. We either
* just seek past it, or (for an in-place sparse update), we give the data to
* the sparse processor with the use_seek flag set. */
int skip_matched(int fd, OFF_T offset, const char *buf, int len)
{
OFF_T pos;
if (sparse_files > 0) {
if (write_file(fd, 1, offset, buf, len) != len)
return -1;
return 0;
}
if (flush_write_file(fd) < 0)
return -1;
if ((pos = vfs_lseek(fd, len, SEEK_CUR)) != offset + len) {
rsyserr(FERROR_XFER, errno, "lseek returned %s, not %s",
big_num(pos), big_num(offset));
return -1;
}
return 0;
}
/* This provides functionality somewhat similar to mmap() but using read().
* It gives sliding window access to a file. mmap() is not used because of
* the possibility of another program (such as a mailer) truncating the
* file thus giving us a SIGBUS. */
struct map_struct *map_file(int fd, OFF_T len, int32 read_size,
int32 blk_size)
struct map_struct *map_file(int fd, OFF_T len, int32 read_size, int32 blk_size)
{
struct map_struct *map;
if (!(map = new0(struct map_struct)))
out_of_memory("map_file");
map = new0(struct map_struct);
if (blk_size && (read_size % blk_size))
read_size += blk_size - (read_size % blk_size);
map->fd = fd;
map->file_size = len;
map->def_window_size = read_size;
map->def_window_size = ALIGNED_LENGTH(read_size);
return map;
}
@@ -176,9 +295,8 @@ struct map_struct *map_file(int fd, OFF_T len, int32 read_size,
/* slide the read window in the file */
char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
{
int32 nread;
OFF_T window_start, read_start;
int32 window_size, read_size, read_offset;
int32 window_size, read_size, read_offset, align_fudge;
if (len == 0)
return NULL;
@@ -193,26 +311,23 @@ char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
return map->p + (offset - map->p_offset);
/* nope, we are going to have to do a read. Work out our desired window */
window_start = offset;
align_fudge = (int32)ALIGNED_OVERSHOOT(offset);
window_start = offset - align_fudge;
window_size = map->def_window_size;
if (window_start + window_size > map->file_size)
window_size = (int32)(map->file_size - window_start);
if (len > window_size)
window_size = len;
if (window_size < len + align_fudge)
window_size = ALIGNED_LENGTH(len + align_fudge);
/* make sure we have allocated enough memory for the window */
if (window_size > map->p_size) {
map->p = realloc_array(map->p, char, window_size);
if (!map->p)
out_of_memory("map_ptr");
map->p_size = window_size;
}
/* Now try to avoid re-reading any bytes by reusing any bytes
* from the previous buffer. */
if (window_start >= map->p_offset &&
window_start < map->p_offset + map->p_len &&
window_start + window_size >= map->p_offset + map->p_len) {
/* Now try to avoid re-reading any bytes by reusing any bytes from the previous buffer. */
if (window_start >= map->p_offset && window_start < map->p_offset + map->p_len
&& window_start + window_size >= map->p_offset + map->p_len) {
read_start = map->p_offset + map->p_len;
read_offset = (int32)(read_start - window_start);
read_size = window_size - read_offset;
@@ -230,10 +345,10 @@ char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
}
if (map->p_fd_offset != read_start) {
OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET);
OFF_T ret = vfs_lseek(map->fd, read_start, SEEK_SET);
if (ret != read_start) {
rsyserr(FERROR, errno, "lseek returned %.0f, not %.0f",
(double)ret, (double)read_start);
rsyserr(FERROR, errno, "lseek returned %s, not %s",
big_num(ret), big_num(read_start));
exit_cleanup(RERR_FILEIO);
}
map->p_fd_offset = read_start;
@@ -242,7 +357,7 @@ char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
map->p_len = window_size;
while (read_size > 0) {
nread = read(map->fd, map->p + read_offset, read_size);
int32 nread = read(map->fd, map->p + read_offset, read_size);
if (nread <= 0) {
if (!map->status)
map->status = nread ? errno : ENODATA;
@@ -256,10 +371,9 @@ char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
read_size -= nread;
}
return map->p;
return map->p + align_fudge;
}
int unmap_file(struct map_struct *map)
{
int ret;
@@ -269,7 +383,9 @@ int unmap_file(struct map_struct *map)
map->p = NULL;
}
ret = map->status;
memset(map, 0, sizeof map[0]);
#if 0 /* I don't think we really need this. */
force_memzero(map, sizeof map[0]);
#endif
free(map);
return ret;
+1178 -338
View File
File diff suppressed because it is too large. Load diff
+1347 -767
View File
File diff suppressed because it is too large. Load diff
+1 -2
View File
@@ -15,8 +15,7 @@
fprintf(stderr, "Unable to stat `%s'\n", *argv);
exit(1);
}
printf("%ld/%ld\n", (long)major(st.st_dev),
(long)minor(st.st_dev));
printf("%ld/%ld\n", (long)major(st.st_dev), (long)minor(st.st_dev));
}
return 0;
+3 -3
View File
@@ -3,7 +3,7 @@
* `id -G` on Linux, but it's too hard to find a portable equivalent.
*
* Copyright (C) 2002 Martin Pool
* Copyright (C) 2003-2008 Wayne Davison
* Copyright (C) 2003-2020 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 3 as
@@ -20,8 +20,7 @@
#include "rsync.h"
int
main(UNUSED(int argc), UNUSED(char *argv[]))
int main(UNUSED(int argc), UNUSED(char *argv[]))
{
int n, i;
gid_t *list;
@@ -58,5 +57,6 @@ main(UNUSED(int argc), UNUSED(char *argv[]))
printf("%lu", (unsigned long)gid);
printf("\n");
free(list);
return 0;
}
+554 -25
View File
@@ -1,7 +1,7 @@
/*
* Routines to provide a memory-efficient hashtable.
*
* Copyright (C) 2007-2008 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
@@ -19,64 +19,115 @@
#include "rsync.h"
#define HASH_LOAD_LIMIT(size) ((size)*3/4)
#define HASH_LOAD_LIMIT(size) ((size)/4*3) /* /4 first: never overflows int */
struct hashtable *hashtable_create(int size, int key64)
{
int req = size;
struct hashtable *tbl;
int node_size = key64 ? sizeof (struct ht_int64_node)
: sizeof (struct ht_int32_node);
/* Pick a power of 2 that can hold the requested size. */
if (size & (size-1) || size < 16) {
int req = size;
/* Pick a power of 2 that can hold the requested size. Test size < 16 first
* so a negative/zero req short-circuits before the size-1 (INT_MIN is UB). */
if (size < 16 || (size & (size-1))) {
size = 16;
while (size < req)
while (size < req) {
if (size > INT_MAX/2) { /* the next doubling would overflow int */
rprintf(FERROR, "[%s] hashtable_create: requested size %d is too large\n",
who_am_i(), req);
exit_cleanup(RERR_MALLOC);
}
size *= 2;
}
}
if (!(tbl = new(struct hashtable))
|| !(tbl->nodes = new_array0(char, size * node_size)))
out_of_memory("hashtable_create");
tbl = new(struct hashtable);
/* Pass size and node_size as SEPARATE factors so my_alloc's overflow /
* --max-alloc guard sees both; computing size*node_size as int would wrap to
* a tiny count and under-allocate (heap overflow on later node access). */
tbl->nodes = my_alloc(do_calloc, size, node_size, __FILE__, __LINE__);
tbl->size = size;
tbl->entries = 0;
tbl->node_size = node_size;
tbl->key64 = key64;
tbl->key64 = key64 ? 1 : 0;
if (DEBUG_GTE(HASH, 1)) {
char buf[32];
if (req != size)
snprintf(buf, sizeof buf, "req: %d, ", req);
else
*buf = '\0';
rprintf(FINFO, "[%s] created hashtable %lx (%ssize: %d, keys: %d-bit)\n",
who_am_i(), (long)tbl, buf, size, key64 ? 64 : 32);
}
return tbl;
}
void hashtable_destroy(struct hashtable *tbl)
{
if (DEBUG_GTE(HASH, 1)) {
rprintf(FINFO, "[%s] destroyed hashtable %lx (size: %d, keys: %d-bit)\n",
who_am_i(), (long)tbl, tbl->size, tbl->key64 ? 64 : 32);
}
free(tbl->nodes);
free(tbl);
}
/* This returns the node for the indicated key, either newly created or
* already existing. Returns NULL if not allocating and not found. */
void *hashtable_find(struct hashtable *tbl, int64 key, int allocate_if_missing)
/* Returns the node that holds the indicated key if it exists. When it does not
* exist, it returns either NULL (when data_when_new is NULL), or it returns a
* new node with its node->data set to the indicated value.
*
* If your code doesn't know the data value for a new node in advance (usually
* because it doesn't know if a node is new or not) you should pass in a unique
* (non-0) value that you can use to check if the returned node is new. You can
* then overwrite the data with any value you want (even 0) since it only needs
* to be different than whatever data_when_new value you use later on.
*
* This return is a void* just because it might be pointing at a ht_int32_node
* or a ht_int64_node, and that makes the caller's assignment a little easier. */
void *hashtable_find(struct hashtable *tbl, int64 key, void *data_when_new)
{
int key64 = tbl->key64;
struct ht_int32_node *node;
uint32 ndx;
if (allocate_if_missing && tbl->entries > HASH_LOAD_LIMIT(tbl->size)) {
void *old_nodes = tbl->nodes;
int size = tbl->size * 2;
int i;
if (key64 ? key == 0 : (int32)key == 0) {
rprintf(FERROR, "Internal hashtable error: illegal key supplied!\n");
exit_cleanup(RERR_MESSAGEIO);
}
if (!(tbl->nodes = new_array0(char, size * tbl->node_size)))
out_of_memory("hashtable_node");
if (data_when_new && tbl->entries > HASH_LOAD_LIMIT(tbl->size)) {
void *old_nodes = tbl->nodes;
int size, i;
if (tbl->size > INT_MAX/2) { /* doubling would overflow int */
rprintf(FERROR, "[%s] hashtable grow: size overflow\n", who_am_i());
exit_cleanup(RERR_MALLOC);
}
size = tbl->size * 2;
/* Separate factors so my_alloc's guard sees both (see hashtable_create). */
tbl->nodes = my_alloc(do_calloc, size, tbl->node_size, __FILE__, __LINE__);
tbl->size = size;
tbl->entries = 0;
if (DEBUG_GTE(HASH, 1)) {
rprintf(FINFO, "[%s] growing hashtable %lx (size: %d, keys: %d-bit)\n",
who_am_i(), (long)tbl, size, key64 ? 64 : 32);
}
for (i = size / 2; i-- > 0; ) {
struct ht_int32_node *move_node = HT_NODE(tbl, old_nodes, i);
int64 move_key = HT_KEY(move_node, key64);
if (move_key == 0)
continue;
node = hashtable_find(tbl, move_key, 1);
node->data = move_node->data;
if (move_node->data)
hashtable_find(tbl, move_key, move_node->data);
else {
node = hashtable_find(tbl, move_key, "");
node->data = 0;
}
}
free(old_nodes);
@@ -84,10 +135,10 @@ void *hashtable_find(struct hashtable *tbl, int64 key, int allocate_if_missing)
if (!key64) {
/* Based on Jenkins One-at-a-time hash. */
uchar buf[4], *keyp = buf;
uchar buf[4] = {0}, *keyp = buf; /* {0} only to satisfy the analyzer (SIVALu fills buf) */
int i;
SIVAL(buf, 0, key);
SIVALu(buf, 0, key);
for (ndx = 0, i = 0; i < 4; i++) {
ndx += keyp[i];
ndx += (ndx << 10);
@@ -131,7 +182,7 @@ void *hashtable_find(struct hashtable *tbl, int64 key, int allocate_if_missing)
if (nkey == key)
return node;
if (nkey == 0) {
if (!allocate_if_missing)
if (!data_when_new)
return NULL;
break;
}
@@ -142,7 +193,485 @@ void *hashtable_find(struct hashtable *tbl, int64 key, int allocate_if_missing)
if (key64)
((struct ht_int64_node*)node)->key = key;
else
node->key = key;
node->key = (int32)key;
node->data = data_when_new;
tbl->entries++;
return node;
}
#ifndef WORDS_BIGENDIAN
# define HASH_LITTLE_ENDIAN 1
# define HASH_BIG_ENDIAN 0
#else
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 1
#endif
/*
-------------------------------------------------------------------------------
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
These are functions for producing 32-bit hashes for hash table lookup.
hash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and 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 hashlittle(). hashlittle() and hashbig()
hash byte arrays. hashlittle() is is faster than hashbig() on
little-endian machines. Intel and AMD are little-endian machines.
On second thought, you probably want hashlittle2(), which is identical to
hashlittle() except it returns two 32-bit hashes for the price of one.
You could implement hashbig2() 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;
mix(a,b,c);
a += i4; b += i5; c += i6;
mix(a,b,c);
a += i7;
final(a,b,c);
then use c as the hash value. If you have a variable length array of
4-byte integers to hash, use hash_word(). If you have a byte array (like
a character string), use hashlittle(). If you have several byte arrays, or
a mix of things, see the comments above hashlittle().
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.
*/
#define hashsize(n) ((uint32_t)1<<(n))
#define hashmask(n) (hashsize(n)-1)
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
/*
-------------------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
This is reversible, so any information in (a,b,c) before mix() is
still in (a,b,c) after mix().
If four pairs of (a,b,c) inputs are run through mix(), or through
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^=rot(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 mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
/*
-------------------------------------------------------------------------------
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 final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c,4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
/*
-------------------------------------------------------------------------------
hashlittle() -- hash a variable-length key into a 32-bit value
k : the key (the unaligned variable-length array of bytes)
length : the length of the key, counting by bytes
val2 : IN: can be any 4-byte value OUT: second 32 bit hash.
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. Note that the return value is better
mixed than val2, so use that first.
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 = hashlittle( k[i], len[i], h);
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.
-------------------------------------------------------------------------------
*/
#define NON_ZERO_32(x) ((x) ? (x) : (uint32_t)1)
#define NON_ZERO_64(x, y) ((x) || (y) ? (y) | (uint64_t)(x) << 32 | (y) : (int64)1)
uint32_t hashlittle(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_32(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_32(c); /* zero length requires 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_32(c);
}
}
final(a,b,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
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/awk -f
# The caller must pass args: -v hfile=help-NAME.h NAME.NUM.md
BEGIN {
heading = "/* DO NOT EDIT THIS FILE! It is auto-generated from the option list in " ARGV[1] "! */"
findcomment = hfile
sub("\\.", "\\.", findcomment)
findcomment = "\\[comment\\].*" findcomment
backtick_cnt = 0
prints = ""
}
/^```/ {
backtick_cnt++
next
}
foundcomment {
if (backtick_cnt > 1) exit
if (backtick_cnt == 1) {
gsub(/"/, "\\\"")
prints = prints "\n rprintf(F,\"" $0 "\\n\");"
}
next
}
$0 ~ findcomment {
foundcomment = 1
backtick_cnt = 0
}
END {
if (foundcomment && backtick_cnt > 1)
print heading "\n" prints > hfile
else {
print "Failed to find " hfile " section in " ARGV[1]
exit 1
}
}
+135 -84
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-2008 Wayne Davison
* Copyright (C) 2004-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,23 +21,24 @@
*/
#include "rsync.h"
#include "inums.h"
#include "ifuncs.h"
extern int verbose;
extern int dry_run;
extern int list_only;
extern int am_sender;
extern int inc_recurse;
extern int do_xfers;
extern int link_dest;
extern int alt_dest_type;
extern int preserve_acls;
extern int make_backups;
extern int preserve_xattrs;
extern int protocol_version;
extern int remove_source_files;
extern int stdout_format_has_i;
extern int maybe_ATTRS_REPORT;
extern int unsort_ndx;
extern char *basis_dir[];
extern struct file_list *cur_flist, *first_flist;
extern char *basis_dir[MAX_BASIS_DIRS+1];
extern struct file_list *cur_flist;
#ifdef SUPPORT_HARD_LINKS
@@ -47,6 +48,8 @@ extern struct file_list *cur_flist, *first_flist;
* we can avoid the pool of dev+inode data. For incremental recursion mode,
* the receiver will use a ndx hash to remember old pathnames. */
static void *data_when_new = "";
static struct hashtable *dev_tbl;
static struct hashtable *prior_hlinks;
@@ -56,25 +59,29 @@ static struct file_list *hlink_flist;
void init_hard_links(void)
{
if (am_sender || protocol_version < 30)
dev_tbl = hashtable_create(16, SIZEOF_INT64 == 8);
dev_tbl = hashtable_create(16, HT_KEY64);
else if (inc_recurse)
prior_hlinks = hashtable_create(1024, 0);
prior_hlinks = hashtable_create(1024, HT_KEY32);
}
struct ht_int64_node *idev_find(int64 dev, int64 ino)
{
static struct ht_int64_node *dev_node = NULL;
struct hashtable *tbl;
if (!dev_node || dev_node->key != dev) {
/* Note that some OSes have a dev == 0, so increment to avoid storing a 0. */
if (!dev_node || dev_node->key != dev+1) {
/* We keep a separate hash table of inodes for every device. */
dev_node = hashtable_find(dev_tbl, dev, 1);
if (!(tbl = dev_node->data))
tbl = dev_node->data = hashtable_create(512, SIZEOF_INT64 == 8);
} else
tbl = dev_node->data;
dev_node = hashtable_find(dev_tbl, dev+1, data_when_new);
if (dev_node->data == data_when_new) {
dev_node->data = hashtable_create(512, HT_KEY64);
if (DEBUG_GTE(HLINK, 3)) {
rprintf(FINFO, "[%s] created hashtable for dev %s\n",
who_am_i(), big_num(dev));
}
}
}
return hashtable_find(tbl, ino, 1);
return hashtable_find(dev_node->data, ino, (void*)-1L);
}
void idev_destroy(void)
@@ -110,23 +117,36 @@ 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]];
gnum = F_HL_GNUM(file);
if (inc_recurse) {
node = hashtable_find(prior_hlinks, gnum, 1);
if (!node->data) {
node = hashtable_find(prior_hlinks, gnum, data_when_new);
if (node->data == data_when_new) {
if (gnum < hlink_flist->ndx_start) {
/* A non-first hard-link entry whose
* gnum points before this flist's
* ndx_start should already have been
* recorded in prior_hlinks by an
* earlier flist. A peer that sends
* such a back-reference on the first
* flist (or to a gnum that was never
* declared XMIT_HLINK_FIRST) is
* misbehaving. */
rprintf(FERROR,
"hard-link gnum %d precedes flist start %d\n",
(int)gnum, (int)hlink_flist->ndx_start);
exit_cleanup(RERR_PROTOCOL);
}
node->data = new_array0(char, 5);
assert(gnum >= hlink_flist->ndx_start);
file->flags |= FLAG_HLINK_FIRST;
prev = -1;
} else if (CVAL(node->data, 0) == 0) {
struct file_list *flist;
prev = IVAL(node->data, 1);
flist = flist_for_ndx(prev);
flist = flist_for_ndx(prev, NULL);
if (flist)
flist->files[prev - flist->ndx_start]->flags &= ~FLAG_HLINK_LAST;
else {
@@ -179,12 +199,11 @@ static void match_gnums(int32 *ndx_list, int ndx_count)
* to first when we're done. */
void match_hard_links(struct file_list *flist)
{
if (!list_only) {
if (!list_only && flist->used) {
int i, ndx_count = 0;
int32 *ndx_list;
if (!(ndx_list = new_array(int32, flist->used)))
out_of_memory("match_hard_links");
ndx_list = new_array(int32, flist->used);
for (i = 0; i < flist->used; i++) {
if (F_IS_HLINKED(flist->sorted[i]))
@@ -203,7 +222,7 @@ void match_hard_links(struct file_list *flist)
}
static int maybe_hard_link(struct file_struct *file, int ndx,
const char *fname, int statret, stat_x *sxp,
char *fname, int statret, stat_x *sxp,
const char *oldname, STRUCT_STAT *old_stp,
const char *realname, int itemizing, enum logcode code)
{
@@ -215,31 +234,24 @@ static int maybe_hard_link(struct file_struct *file, int ndx,
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
0, "");
}
if (verbose > 1 && maybe_ATTRS_REPORT)
if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
rprintf(FCLIENT, "%s is uptodate\n", fname);
file->flags |= FLAG_HLINK_DONE;
return 0;
}
if (make_backups > 0) {
if (!make_backup(fname))
return -1;
} else if (robust_unlink(fname)) {
rsyserr(FERROR_XFER, errno, "unlink %s failed",
full_fname(fname));
return -1;
}
}
if (hard_link_one(file, fname, oldname, 0)) {
if (atomic_create(file, fname, NULL, oldname, MAKEDEV(0, 0), sxp, statret == 0 ? DEL_FOR_FILE : 0)) {
if (itemizing) {
itemize(fname, file, ndx, statret, sxp,
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
realname);
}
if (code != FNONE && verbose)
if (code != FNONE && INFO_GTE(NAME, 1))
rprintf(code, "%s => %s\n", fname, realname);
return 0;
}
return -1;
}
@@ -255,7 +267,7 @@ static char *check_prior(struct file_struct *file, int gnum,
while (1) {
struct file_list *flist;
if (prev_ndx < 0
|| (flist = flist_for_ndx(prev_ndx)) == NULL)
|| (flist = flist_for_ndx(prev_ndx, NULL)) == NULL)
break;
fp = flist->files[prev_ndx - flist->ndx_start];
if (!(fp->flags & FLAG_SKIP_HLINK)) {
@@ -267,7 +279,7 @@ static char *check_prior(struct file_struct *file, int gnum,
}
if (inc_recurse
&& (node = hashtable_find(prior_hlinks, gnum, 0)) != NULL) {
&& (node = hashtable_find(prior_hlinks, gnum, NULL)) != NULL) {
assert(node->data != NULL);
if (CVAL(node->data, 0) != 0) {
*prev_ndx_p = -1;
@@ -285,7 +297,7 @@ static char *check_prior(struct file_struct *file, int gnum,
/* Only called if FLAG_HLINKED is set and FLAG_HLINK_FIRST is not. Returns:
* 0 = process the file, 1 = skip the file, -1 = error occurred. */
int hard_link_check(struct file_struct *file, int ndx, const char *fname,
int hard_link_check(struct file_struct *file, int ndx, char *fname,
int statret, stat_x *sxp, int itemizing,
enum logcode code)
{
@@ -304,6 +316,10 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
if (!flist) {
/* The previous file was skipped, so this one is
* treated as if it were the first in its group. */
if (DEBUG_GTE(HLINK, 2)) {
rprintf(FINFO, "hlink for %d (%s,%d): virtual first\n",
ndx, f_name(file, NULL), gnum);
}
return 0;
}
@@ -320,8 +336,16 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
F_HL_PREV(prev_file) = ndx;
file->flags |= FLAG_FILE_SENT;
cur_flist->in_progress++;
if (DEBUG_GTE(HLINK, 2)) {
rprintf(FINFO, "hlink for %d (%s,%d): waiting for %d\n",
ndx, f_name(file, NULL), gnum, F_HL_PREV(file));
}
return 1;
}
if (DEBUG_GTE(HLINK, 2)) {
rprintf(FINFO, "hlink for %d (%s,%d): looking for a leader\n",
ndx, f_name(file, NULL), gnum);
}
return 0;
}
@@ -329,7 +353,6 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
if (!(prev_file->flags & FLAG_HLINK_FIRST)) {
/* The previous previous is FIRST when prev is not. */
prev_name = realname = check_prior(prev_file, gnum, &prev_ndx, &flist);
assert(prev_name != NULL || flist != NULL);
/* Update our previous pointer to point to the FIRST. */
F_HL_PREV(file) = prev_ndx;
}
@@ -337,9 +360,14 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
if (!prev_name) {
int alt_dest;
assert(flist != NULL);
prev_file = flist->files[prev_ndx - flist->ndx_start];
/* F_HL_PREV() is alt_dest value when DONE && FIRST. */
alt_dest = F_HL_PREV(prev_file);
if (DEBUG_GTE(HLINK, 2)) {
rprintf(FINFO, "hlink for %d (%s,%d): found flist match (alt %d)\n",
ndx, f_name(file, NULL), gnum, alt_dest);
}
if (alt_dest >= 0 && dry_run) {
pathjoin(namebuf, MAXPATHLEN, basis_dir[alt_dest],
@@ -353,10 +381,19 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
}
}
if (DEBUG_GTE(HLINK, 2)) {
rprintf(FINFO, "hlink for %d (%s,%d): leader is %d (%s)\n",
ndx, f_name(file, NULL), gnum, prev_ndx, prev_name);
}
if (link_stat(prev_name, &prev_st, 0) < 0) {
rsyserr(FERROR_XFER, errno, "stat %s failed",
full_fname(prev_name));
return -1;
if (!dry_run || errno != ENOENT) {
rsyserr(FERROR_XFER, errno, "stat %s failed", full_fname(prev_name));
return -1;
}
/* A new hard-link will get a new dev & inode, so approximate
* those values in dry-run mode by zeroing them. */
memset(&prev_st, 0, sizeof prev_st);
}
if (statret < 0 && basis_dir[0] != NULL) {
@@ -364,28 +401,33 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
char cmpbuf[MAXPATHLEN];
stat_x alt_sx;
int j = 0;
#ifdef SUPPORT_ACLS
alt_sx.acc_acl = alt_sx.def_acl = NULL;
#endif
init_stat_x(&alt_sx);
do {
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
if (link_stat(cmpbuf, &alt_sx.st, 0) < 0)
continue;
if (link_dest) {
if (alt_dest_type == LINK_DEST) {
if (prev_st.st_dev != alt_sx.st.st_dev
|| prev_st.st_ino != alt_sx.st.st_ino)
continue;
statret = 1;
if (stdout_format_has_i == 0
|| (verbose < 2 && stdout_format_has_i < 2)) {
|| (!INFO_GTE(NAME, 2) && stdout_format_has_i < 2)) {
itemizing = 0;
code = FNONE;
if (verbose > 1 && maybe_ATTRS_REPORT)
if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
rprintf(FCLIENT, "%s is uptodate\n", fname);
}
break;
}
if (!unchanged_file(cmpbuf, file, &alt_sx.st))
/* Content-based basis match only applies to regular
* files: for a hard-linked symlink/device/special the
* exact-inode check above is the only meaningful test,
* and quick_check_ok(FT_REG, ...) would read F_SUM()
* on a file_struct that has no SUM_EXTRA_CNT space
* (recv_file_entry only allocates it for S_ISREG). */
if (!S_ISREG(file->mode)
|| !quick_check_ok(FT_REG, cmpbuf, file, &alt_sx.st))
continue;
statret = 1;
if (unchanged_attrs(cmpbuf, file, &alt_sx))
@@ -395,19 +437,29 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
sxp->st = alt_sx.st;
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
if (!ACL_READY(*sxp))
free_acl(sxp);
if (!ACL_READY(alt_sx))
get_acl(cmpbuf, sxp);
else {
sxp->acc_acl = alt_sx.acc_acl;
sxp->def_acl = alt_sx.def_acl;
alt_sx.acc_acl = alt_sx.def_acl = NULL;
}
}
#endif
}
#ifdef SUPPORT_ACLS
else if (preserve_acls)
free_acl(&alt_sx);
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
free_xattr(sxp);
if (!XATTR_READY(alt_sx))
get_xattr(cmpbuf, -1, sxp);
else {
sxp->xattr = alt_sx.xattr;
alt_sx.xattr = NULL;
}
}
#endif
} else
free_stat_x(&alt_sx);
}
if (maybe_hard_link(file, ndx, fname, statret, sxp, prev_name, &prev_st,
@@ -415,18 +467,21 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
return -1;
if (remove_source_files == 1 && do_xfers)
send_msg_int(MSG_SUCCESS, ndx);
send_msg_success(fname, ndx);
return 1;
}
int hard_link_one(struct file_struct *file, const char *fname,
const char *oldname, int terse)
const char *oldname, int terse, int vfs_flags)
{
if (do_link(oldname, fname) < 0) {
/* oldname is the link source (vfs_flags carries its policy -- VFS_OPERATOR_PATH
* for an alt-dest basis on a non-daemon receiver, else 0); fname is the
* transfer destination, always under the secure receiver resolve. */
if (vfs_link_at(oldname, fname, vfs_flags, 0) < 0) {
enum logcode code;
if (terse) {
if (!verbose)
if (!INFO_GTE(NAME, 1))
return 0;
code = FINFO;
} else
@@ -453,7 +508,7 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
if (stp == NULL && prev_ndx >= 0) {
if (link_stat(fname, &st, 0) < 0) {
if (link_stat(fname, &st, 0) < 0 && !dry_run) {
rsyserr(FERROR_XFER, errno, "stat %s failed",
full_fname(fname));
return;
@@ -471,22 +526,11 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
} else
our_name = fname;
#ifdef SUPPORT_ACLS
prev_sx.acc_acl = prev_sx.def_acl = NULL;
#endif
init_stat_x(&prev_sx);
while ((ndx = prev_ndx) >= 0) {
int val;
flist = flist_for_ndx(ndx);
if (flist == NULL) {
int start1 = first_flist ? first_flist->ndx_start : 0;
int start2 = first_flist ? first_flist->prev->ndx_start : 0;
int used = first_flist ? first_flist->prev->used : 0;
rprintf(FERROR,
"File index not found: %d (%d - %d)\n",
ndx, start1 - 1, start2 + used - 1);
exit_cleanup(RERR_PROTOCOL);
}
flist = flist_for_ndx(ndx, "finish_hard_link");
file = flist->files[ndx - flist->ndx_start];
file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
prev_ndx = F_HL_PREV(file);
@@ -495,24 +539,31 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
val = maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_sx,
our_name, stp, fname, itemizing, code);
flist->in_progress--;
#ifdef SUPPORT_ACLS
if (preserve_acls)
free_acl(&prev_sx);
#endif
free_stat_x(&prev_sx);
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) {
int gnum = F_HL_GNUM(file);
struct ht_int32_node *node = hashtable_find(prior_hlinks, gnum, 0);
assert(node != NULL && node->data != NULL);
assert(CVAL(node->data, 0) == 0);
struct ht_int32_node *node = hashtable_find(prior_hlinks, gnum, NULL);
if (node == NULL) {
rprintf(FERROR, "Unable to find a hlink node for %d (%s)\n", gnum, f_name(file, prev_name));
exit_cleanup(RERR_MESSAGEIO);
}
if (node->data == NULL) {
rprintf(FERROR, "Hlink node data for %d is NULL (%s)\n", gnum, f_name(file, prev_name));
exit_cleanup(RERR_MESSAGEIO);
}
if (CVAL(node->data, 0) != 0) {
rprintf(FERROR, "Hlink node data for %d already has path=%s (%s)\n",
gnum, (char*)node->data, f_name(file, prev_name));
exit_cleanup(RERR_MESSAGEIO);
}
free(node->data);
if (!(node->data = strdup(our_name)))
out_of_memory("finish_hard_link");
node->data = strdup(our_name);
}
}
+40 -38
View File
@@ -1,6 +1,6 @@
/* Inline functions for rsync.
*
* Copyright (C) 2007-2008 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
@@ -19,8 +19,7 @@
static inline void
alloc_xbuf(xbuf *xb, size_t sz)
{
if (!(xb->buf = new_array(char, sz)))
out_of_memory("alloc_xbuf");
xb->buf = new_array(char, sz);
xb->size = sz;
xb->len = xb->pos = 0;
}
@@ -29,12 +28,18 @@ static inline void
realloc_xbuf(xbuf *xb, size_t sz)
{
char *bf = realloc_array(xb->buf, char, sz);
if (!bf)
out_of_memory("realloc_xbuf");
xb->buf = bf;
xb->size = sz;
}
static inline void
free_xbuf(xbuf *xb)
{
if (xb->buf)
free(xb->buf);
memset(xb, 0, sizeof (xbuf));
}
static inline int
to_wire_mode(mode_t mode)
{
@@ -67,44 +72,41 @@ d_name(struct dirent *di)
#endif
}
static inline int
isDigit(const char *ptr)
static inline void
init_stat_x(stat_x *sx_p)
{
return isdigit(*(unsigned char *)ptr);
sx_p->crtime = 0;
#ifdef SUPPORT_ACLS
sx_p->acc_acl = sx_p->def_acl = NULL;
#endif
#ifdef SUPPORT_XATTRS
sx_p->xattr = NULL;
#endif
}
static inline int
isPrint(const char *ptr)
static inline void
free_stat_x(stat_x *sx_p)
{
return isprint(*(unsigned char *)ptr);
#ifdef SUPPORT_ACLS
{
extern int preserve_acls;
if (preserve_acls)
free_acl(sx_p);
}
#endif
#ifdef SUPPORT_XATTRS
{
extern int preserve_xattrs;
if (preserve_xattrs)
free_xattr(sx_p);
}
#endif
}
static inline int
isSpace(const char *ptr)
static inline char *my_strdup(const char *str, const char *file, int line)
{
return isspace(*(unsigned char *)ptr);
}
static inline int
isLower(const char *ptr)
{
return islower(*(unsigned char *)ptr);
}
static inline int
isUpper(const char *ptr)
{
return isupper(*(unsigned char *)ptr);
}
static inline int
toLower(const char *ptr)
{
return tolower(*(unsigned char *)ptr);
}
static inline int
toUpper(const char *ptr)
{
return toupper(*(unsigned char *)ptr);
int len = strlen(str)+1;
char *buf = my_alloc(NULL, len, 1, file, line);
memcpy(buf, str, len);
return buf;
}
+1 -1
View File
@@ -115,7 +115,7 @@ else
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
if [ -f $src ] || [ -d $src ]
then
true
else
+57
View File
@@ -0,0 +1,57 @@
/* Inline functions for rsync.
*
* Copyright (C) 2008-2019 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, visit the http://fsf.org website.
*/
static inline char *
big_num(int64 num)
{
return do_big_num(num, 0, NULL);
}
static inline char *
comma_num(int64 num)
{
extern int human_readable;
return do_big_num(num, human_readable != 0, NULL);
}
static inline char *
human_num(int64 num)
{
extern int human_readable;
return do_big_num(num, human_readable, NULL);
}
static inline char *
big_dnum(double dnum, int decimal_digits)
{
return do_big_dnum(dnum, 0, decimal_digits);
}
static inline char *
comma_dnum(double dnum, int decimal_digits)
{
extern int human_readable;
return do_big_dnum(dnum, human_readable != 0, decimal_digits);
}
static inline char *
human_dnum(double dnum, int decimal_digits)
{
extern int human_readable;
return do_big_dnum(dnum, human_readable, decimal_digits);
}
+1891 -1083
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2007-2008 Wayne Davison
* Copyright (C) 2007-2019 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
+71
View File
@@ -0,0 +1,71 @@
/* Inline functions for rsync.
*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, visit the http://fsf.org website.
*/
static inline int
isDigit(const char *ptr)
{
return isdigit(*(unsigned char *)ptr);
}
static inline int
isHexDigit(const char *ptr)
{
return isxdigit(*(unsigned char *)ptr);
}
static inline int
isPrint(const char *ptr)
{
return isprint(*(unsigned char *)ptr);
}
static inline int
isSpace(const char *ptr)
{
return isspace(*(unsigned char *)ptr);
}
static inline int
isAlNum(const char *ptr)
{
return isalnum(*(unsigned char *)ptr);
}
static inline int
isLower(const char *ptr)
{
return islower(*(unsigned char *)ptr);
}
static inline int
isUpper(const char *ptr)
{
return isupper(*(unsigned char *)ptr);
}
static inline int
toLower(const char *ptr)
{
return tolower(*(unsigned char *)ptr);
}
static inline int
toUpper(const char *ptr)
{
return toupper(*(unsigned char *)ptr);
}
+1
View File
@@ -0,0 +1 @@
#define LATEST_YEAR "2026"
+451
View File
@@ -0,0 +1,451 @@
/*
* POSIX ACL get/set/delete via the generic xattr syscalls.
*
* POSIX ACLs are stored by the kernel as the "system.posix_acl_access" and
* "system.posix_acl_default" extended attributes, in a fixed little-endian
* wire format (see include/acl_ea.h in the acl package). By serializing that
* format ourselves and using fgetxattr/fsetxattr on a held O_NOFOLLOW fd -- or
* getxattrat/setxattrat(AT_SYMLINK_NOFOLLOW) on a dirfd+leaf -- we get a
* symlink-race-safe ACL primitive that also covers the *default* ACL, which
* libacl's fd API (acl_get_fd/acl_set_fd, access-only) cannot.
*
* This file knows nothing about rsync's globals or its internal ACL form: it
* speaks a neutral (tag, perm, id) entry array, which makes it directly
* comparable against the system libacl in the t_acl unit test.
*
* Copyright (C) 2026 Wayne Davison & the rsync project
*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, visit the http://fsf.org website.
*/
#include "rsync.h"
#include "acl.h"
#ifdef SUPPORT_ACL_FD
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h> /* AT_SYMLINK_NOFOLLOW */
#if defined HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#elif defined HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#endif
#ifdef HAVE_XATTRAT_SYSCALLS
#include <sys/syscall.h>
/* Self-contained copy of the kernel's struct xattr_args (stable ABI: an
* 8-byte-aligned u64 pointer, then two u32s). Defined locally to avoid
* pulling <linux/xattr.h>, whose XATTR_* macros clash with <sys/xattr.h>. */
struct rsync_xattr_args {
uint64_t value __attribute__((aligned(8)));
uint32_t size;
uint32_t flags;
};
#endif
/* Linux 2.4 didn't have a distinct ENOATTR. */
#ifndef ENOATTR
#define ENOATTR ENODATA
#endif
#define ACL_XATTR_ACCESS "system.posix_acl_access"
#define ACL_XATTR_DEFAULT "system.posix_acl_default"
/* On-disk layout: a 4-byte LE version header followed by 8-byte LE entries. */
#define ACL_EA_VERSION 0x0002
#define ACL_EA_HDR_LEN 4
#define ACL_EA_ENT_LEN 8
/* === little-endian (de)serialization (host-endianness independent) === */
static void put_le16(unsigned char *p, uint16_t v)
{
p[0] = (unsigned char)(v & 0xff);
p[1] = (unsigned char)((v >> 8) & 0xff);
}
static void put_le32(unsigned char *p, uint32_t v)
{
p[0] = (unsigned char)(v & 0xff);
p[1] = (unsigned char)((v >> 8) & 0xff);
p[2] = (unsigned char)((v >> 16) & 0xff);
p[3] = (unsigned char)((v >> 24) & 0xff);
}
static uint16_t get_le16(const unsigned char *p)
{
return (uint16_t)(p[0] | (p[1] << 8));
}
static uint32_t get_le32(const unsigned char *p)
{
return (uint32_t)p[0] | ((uint32_t)p[1] << 8)
| ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static int is_named_tag(uint16_t tag)
{
return tag == RACL_USER || tag == RACL_GROUP;
}
/* Canonical order: tag ascending, then id ascending within a tag. This is
* the order libacl's __acl_reorder_obj_p() produces and what the kernel's
* validator expects (USER_OBJ, USER*, GROUP_OBJ, GROUP*, MASK, OTHER). */
static int ent_compare(const void *a, const void *b)
{
const rsync_acl_ent *x = a, *y = b;
if (x->tag != y->tag)
return x->tag < y->tag ? -1 : 1;
if (x->id != y->id)
return x->id < y->id ? -1 : 1;
return 0;
}
/* Serialize entries into a freshly-malloc'd xattr buffer (canonical order). */
static unsigned char *acl_to_xattr(const rsync_acl_ent *ents, int count, size_t *len_out)
{
size_t len = ACL_EA_HDR_LEN + (size_t)count * ACL_EA_ENT_LEN;
unsigned char *buf = malloc(len);
rsync_acl_ent *sorted = NULL;
unsigned char *p;
int i;
if (!buf)
return NULL;
if (count > 1) {
sorted = malloc((size_t)count * sizeof sorted[0]);
if (!sorted) {
free(buf);
return NULL;
}
memcpy(sorted, ents, (size_t)count * sizeof sorted[0]);
qsort(sorted, count, sizeof sorted[0], ent_compare);
ents = sorted;
}
put_le32(buf, ACL_EA_VERSION);
p = buf + ACL_EA_HDR_LEN;
for (i = 0; i < count; i++, p += ACL_EA_ENT_LEN) {
put_le16(p, ents[i].tag);
put_le16(p + 2, ents[i].perm);
put_le32(p + 4, is_named_tag(ents[i].tag) ? ents[i].id : RACL_UNDEFINED_ID);
}
if (sorted)
free(sorted);
*len_out = len;
return buf;
}
/* Parse an xattr buffer into a malloc'd entry array (canonical order). */
static int xattr_to_acl(const unsigned char *buf, size_t len,
rsync_acl_ent **out, int *count_out)
{
rsync_acl_ent *ents;
const unsigned char *p;
int n, i;
if (len < ACL_EA_HDR_LEN || (len - ACL_EA_HDR_LEN) % ACL_EA_ENT_LEN != 0
|| get_le32(buf) != ACL_EA_VERSION) {
errno = EINVAL;
return -1;
}
n = (int)((len - ACL_EA_HDR_LEN) / ACL_EA_ENT_LEN);
ents = n ? malloc((size_t)n * sizeof ents[0]) : NULL;
if (n && !ents)
return -1;
p = buf + ACL_EA_HDR_LEN;
for (i = 0; i < n; i++, p += ACL_EA_ENT_LEN) {
ents[i].tag = get_le16(p);
ents[i].perm = get_le16(p + 2);
ents[i].id = is_named_tag(ents[i].tag) ? get_le32(p + 4) : RACL_UNDEFINED_ID;
}
if (n > 1)
qsort(ents, n, sizeof ents[0], ent_compare);
*out = ents;
*count_out = n;
return 0;
}
/* === syscall dispatchers (fd-variant vs at-variant) === */
/* Pre-6.13 fallback for the dirfd+leaf at-variants: address the leaf as
* /proc/self/fd/<dirfd>/<leaf> and use the l*xattr (no-follow-leaf) calls. The
* /proc/self/fd/<dirfd> magic symlink resolves to the pinned parent inode -- a
* raced parent symlink cannot redirect it -- and l*xattr does not follow a raced
* leaf symlink, so this is race-safe without the Linux 6.13 *xattrat syscalls, as
* long as procfs is mounted. (`leaf` is a single component, <= NAME_MAX.)
* Returns 0 and fills `buf`, or -1 with ENAMETOOLONG. */
static int proc_fd_leaf_path(char *buf, size_t buflen, int dirfd, const char *leaf)
{
int n = snprintf(buf, buflen, "/proc/self/fd/%d/%s", dirfd, leaf);
if (n < 0 || (size_t)n >= buflen) {
errno = ENAMETOOLONG;
return -1;
}
return 0;
}
static ssize_t do_getxattr(int fd, int dirfd, const char *leaf,
const char *name, void *val, size_t size)
{
char p[MAXPATHLEN];
if (fd >= 0)
return fgetxattr(fd, name, val, size);
#ifdef HAVE_XATTRAT_SYSCALLS
{
struct rsync_xattr_args args;
ssize_t ret;
args.value = (uint64_t)(uintptr_t)val;
args.size = (uint32_t)size;
args.flags = 0;
ret = syscall(SYS_getxattrat, dirfd, leaf, AT_SYMLINK_NOFOLLOW,
name, &args, sizeof args);
if (ret != -1 || errno != ENOSYS)
return ret;
/* ENOSYS: kernel < 6.13 -- fall through to the /proc compat. */
}
#endif
if (proc_fd_leaf_path(p, sizeof p, dirfd, leaf) < 0)
return -1;
return lgetxattr(p, name, val, size);
}
static int do_setxattr(int fd, int dirfd, const char *leaf,
const char *name, const void *val, size_t size)
{
char p[MAXPATHLEN];
if (fd >= 0)
return fsetxattr(fd, name, val, size, 0);
#ifdef HAVE_XATTRAT_SYSCALLS
{
struct rsync_xattr_args args;
int ret;
args.value = (uint64_t)(uintptr_t)val;
args.size = (uint32_t)size;
args.flags = 0; /* replace */
ret = syscall(SYS_setxattrat, dirfd, leaf, AT_SYMLINK_NOFOLLOW,
name, &args, sizeof args);
if (ret != -1 || errno != ENOSYS)
return ret;
}
#endif
if (proc_fd_leaf_path(p, sizeof p, dirfd, leaf) < 0)
return -1;
return lsetxattr(p, name, val, size, 0);
}
static int do_removexattr(int fd, int dirfd, const char *leaf, const char *name)
{
char p[MAXPATHLEN];
if (fd >= 0)
return fremovexattr(fd, name);
#ifdef HAVE_XATTRAT_SYSCALLS
{
int ret = syscall(SYS_removexattrat, dirfd, leaf, AT_SYMLINK_NOFOLLOW, name);
if (ret != -1 || errno != ENOSYS)
return ret;
}
#endif
if (proc_fd_leaf_path(p, sizeof p, dirfd, leaf) < 0)
return -1;
return lremovexattr(p, name);
}
/* Read the whole named xattr into a malloc'd buffer, growing as needed. */
static int read_full_xattr(int fd, int dirfd, const char *leaf,
const char *name, unsigned char **buf_out, size_t *len_out)
{
unsigned char *buf = NULL;
size_t size = 0;
int tries;
for (tries = 0; tries < 8; tries++) {
ssize_t n = do_getxattr(fd, dirfd, leaf, name, size ? buf : NULL, size);
if (n >= 0) {
if (size == 0) {
/* First call just learned the length. */
size = n ? (size_t)n : 1;
buf = malloc(size);
if (!buf)
return -1;
continue;
}
*buf_out = buf;
*len_out = (size_t)n;
return 0;
}
if (errno == ERANGE) { /* grew under us: re-probe the size */
if (buf)
free(buf);
buf = NULL;
size = 0;
continue;
}
if (buf)
free(buf);
return -1; /* ENODATA / EOPNOTSUPP / ENOSYS / ... in errno */
}
if (buf)
free(buf);
errno = ERANGE;
return -1;
}
/* === public API === */
static int acl_get_common(int fd, int dirfd, const char *leaf,
int want_default, rsync_acl_ent **entries, int *count)
{
const char *name = want_default ? ACL_XATTR_DEFAULT : ACL_XATTR_ACCESS;
unsigned char *buf;
size_t len;
int rc;
*entries = NULL;
*count = 0;
if (read_full_xattr(fd, dirfd, leaf, name, &buf, &len) < 0) {
if (errno == ENODATA || errno == ENOATTR)
return 0; /* no explicit ACL present */
return -1; /* EOPNOTSUPP / ENOSYS / real error */
}
rc = xattr_to_acl(buf, len, entries, count);
free(buf);
return rc;
}
int xacl_get_fd(int fd, int want_default, rsync_acl_ent **entries, int *count)
{
return acl_get_common(fd, -1, NULL, want_default, entries, count);
}
int xacl_get_at(int dirfd, const char *leaf, int want_default,
rsync_acl_ent **entries, int *count)
{
return acl_get_common(-1, dirfd, leaf, want_default, entries, count);
}
static int acl_set_common(int fd, int dirfd, const char *leaf,
int want_default, const rsync_acl_ent *ents, int count)
{
const char *name = want_default ? ACL_XATTR_DEFAULT : ACL_XATTR_ACCESS;
unsigned char *buf;
size_t len;
int rc, save_errno;
buf = acl_to_xattr(ents, count, &len);
if (!buf) {
errno = ENOMEM;
return -1;
}
rc = do_setxattr(fd, dirfd, leaf, name, buf, len);
save_errno = errno;
free(buf);
errno = save_errno;
return rc < 0 ? -1 : 0;
}
int xacl_set_fd(int fd, int want_default, const rsync_acl_ent *ents, int count)
{
return acl_set_common(fd, -1, NULL, want_default, ents, count);
}
int xacl_set_at(int dirfd, const char *leaf, int want_default,
const rsync_acl_ent *ents, int count)
{
return acl_set_common(-1, dirfd, leaf, want_default, ents, count);
}
static int acl_del_default_common(int fd, int dirfd, const char *leaf)
{
if (do_removexattr(fd, dirfd, leaf, ACL_XATTR_DEFAULT) < 0) {
if (errno == ENODATA || errno == ENOATTR)
return 0; /* already absent: success, like acl_delete_def_file */
return -1;
}
return 0;
}
int xacl_del_default_fd(int fd)
{
return acl_del_default_common(fd, -1, NULL);
}
int xacl_del_default_at(int dirfd, const char *leaf)
{
return acl_del_default_common(-1, dirfd, leaf);
}
/* True iff /proc/self/fd magic symlinks are usable, so the dirfd+leaf at-variants
* work race-safely via the /proc compat on a pre-6.13 kernel. */
static int proc_self_fd_usable(void)
{
int dfd = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
char p[64];
int usable = 0;
if (dfd < 0)
return 0;
if (snprintf(p, sizeof p, "/proc/self/fd/%d/.", dfd) < (int)sizeof p) {
/* The probe attr is absent; the path resolving (any errno but
* ENOENT/ENOTDIR -- e.g. ENODATA/ENOTSUP/EACCES) means procfs gives us
* the magic fd-symlink we need. */
errno = 0;
lgetxattr(p, "user.rsync_acl_probe", NULL, 0);
usable = !(errno == ENOENT || errno == ENOTDIR);
}
close(dfd);
return usable;
}
int xacl_at_available(void)
{
static int avail = -1;
if (avail < 0) {
#ifdef HAVE_XATTRAT_SYSCALLS
/* Probe the *xattrat syscall directly (not via do_getxattr's /proc
* fallback): any errno other than ENOSYS means it is present (6.13+). */
struct rsync_xattr_args args;
args.value = 0;
args.size = 0;
args.flags = 0;
errno = 0;
syscall(SYS_getxattrat, AT_FDCWD, ".", AT_SYMLINK_NOFOLLOW,
"user.rsync_acl_probe", &args, sizeof args);
if (errno != ENOSYS) {
avail = 1;
return avail;
}
#endif
/* No *xattrat syscalls (pre-6.13, or a kernel built without them): the dirfd+leaf ACL ops
* are still race-safe via /proc/self/fd if procfs is mounted, closing
* the parent-symlink-race gap that otherwise forces the path-based set. */
avail = proc_self_fd_usable();
}
return avail;
}
#endif /* SUPPORT_ACL_FD */
+74
View File
@@ -0,0 +1,74 @@
/*
* POSIX ACL get/set/delete via the generic xattr syscalls, addressing the
* kernel "system.posix_acl_{access,default}" attributes directly so that the
* operation can be confined to a held O_NOFOLLOW fd (fsetxattr) or a
* dirfd+leaf with AT_SYMLINK_NOFOLLOW (setxattrat). This replaces the path-
* based libacl acl_*_file() calls on Linux, where those would re-resolve the
* path and could be redirected by a parent-component symlink race.
*
* Copyright (C) 2026 Wayne Davison & the rsync project
*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, visit the http://fsf.org website.
*/
#ifdef SUPPORT_ACL_FD
#include <stdint.h>
/* A single logical POSIX ACL entry in host-native form. The tag values are
* the stable kernel ABI numbers (== the libacl ACL_* constants), so they map
* straight onto the on-disk e_tag without translation. */
typedef struct {
uint16_t tag; /* RACL_USER_OBJ / USER / GROUP_OBJ / GROUP / MASK / OTHER */
uint16_t perm; /* permission bits: read=4, write=2, execute=1 */
uint32_t id; /* uid/gid for USER/GROUP entries; RACL_UNDEFINED_ID otherwise */
} rsync_acl_ent;
#define RACL_USER_OBJ 0x01
#define RACL_USER 0x02
#define RACL_GROUP_OBJ 0x04
#define RACL_GROUP 0x08
#define RACL_MASK 0x10
#define RACL_OTHER 0x20
#define RACL_UNDEFINED_ID ((uint32_t)-1)
/* Read the access (want_default==0) or default (want_default!=0) ACL.
*
* On success returns 0 and sets *entries to a malloc()ed array of *count
* entries (the caller frees it with free(); *entries may be NULL when
* *count==0, which means "no explicit ACL present" -- e.g. ENODATA).
*
* On failure returns -1 with errno set. Callers distinguish:
* ENOTSUP/EOPNOTSUPP - this filesystem has no ACL support (may differ per fs)
* ENOSYS - the at-variant syscalls are unavailable on this kernel
* The fd-variant operates on a held, already-NOFOLLOW-opened descriptor. The
* at-variant resolves leaf relative to dirfd and never follows a leaf symlink. */
int xacl_get_fd(int fd, int want_default, rsync_acl_ent **entries, int *count);
int xacl_get_at(int dirfd, const char *leaf, int want_default, rsync_acl_ent **entries, int *count);
/* Write the given entries as the access/default ACL. The entries are emitted
* in canonical order; the kernel validates them (a malformed set -> EINVAL). */
int xacl_set_fd(int fd, int want_default, const rsync_acl_ent *entries, int count);
int xacl_set_at(int dirfd, const char *leaf, int want_default, const rsync_acl_ent *entries, int count);
/* Delete a directory's default ACL. A missing default ACL is success. */
int xacl_del_default_fd(int fd);
int xacl_del_default_at(int dirfd, const char *leaf);
/* Cached runtime probe: are the *xattrat syscalls usable on this kernel?
* Returns 0 when they are absent (so callers can fall back) or unbuilt. */
int xacl_at_available(void);
#endif /* SUPPORT_ACL_FD */
+129 -10
View File
@@ -3,7 +3,7 @@
*
* Copyright (C) 1998 Andrew Tridgell
* Copyright (C) 2002 Martin Pool
* Copyright (C) 2004, 2005, 2006 Wayne Davison
* Copyright (C) 2004-2020 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,17 +20,28 @@
*/
#include "rsync.h"
#include "itypes.h"
#ifndef HAVE_STRDUP
char *strdup(char *s)
static char number_separator;
char get_number_separator(void)
{
int len = strlen(s) + 1;
char *ret = (char *)malloc(len);
if (ret)
memcpy(ret, s, len);
return ret;
if (!number_separator) {
char buf[32];
snprintf(buf, sizeof buf, "%f", 3.14);
if (strchr(buf, '.') != NULL)
number_separator = ',';
else
number_separator = '.';
}
return number_separator;
}
char get_decimal_point(void)
{
return get_number_separator() == ',' ? '.' : ',';
}
#endif
#ifndef HAVE_GETCWD
char *getcwd(char *buf, int size)
@@ -76,7 +87,7 @@
#ifndef HAVE_STRPBRK
/**
* Find the first ocurrence in @p s of any character in @p accept.
* Find the first occurrence in @p s of any character in @p accept.
*
* Derived from glibc
**/
@@ -151,3 +162,111 @@ int sys_gettimeofday(struct timeval *tv)
return gettimeofday(tv);
#endif
}
/* Return the int64 number as a string. If the human_flag arg is non-zero,
* we may output the number in K, M, G, or T units. If we don't add a unit
* suffix, we will append the fract string, if it is non-NULL. We can
* return up to 4 buffers at a time. */
char *do_big_num(int64 num, int human_flag, const char *fract)
{
static char bufs[4][128]; /* more than enough room */
static unsigned int n;
char *s;
int len, negated;
if (human_flag && !number_separator)
(void)get_number_separator();
n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
if (human_flag > 1) {
int mult = human_flag == 2 ? 1000 : 1024;
if (num >= mult || num <= -mult) {
double dnum = (double)num / mult;
char units;
if (num < 0)
dnum = -dnum;
if (dnum < mult)
units = 'K';
else if ((dnum /= mult) < mult)
units = 'M';
else if ((dnum /= mult) < mult)
units = 'G';
else if ((dnum /= mult) < mult)
units = 'T';
else {
dnum /= mult;
units = 'P';
}
if (num < 0)
dnum = -dnum;
snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units);
return bufs[n];
}
}
s = bufs[n] + sizeof bufs[0] - 1;
if (fract) {
len = strlen(fract);
s -= len;
strlcpy(s, fract, len + 1);
} else
*s = '\0';
len = 0;
if (!num)
*--s = '0';
if (num < 0) {
/* A maximum-size negated number can't fit as a positive,
* so do one digit in negated form to start us off. */
*--s = (char)(-(num % 10)) + '0';
num = -(num / 10);
len++;
negated = 1;
} else
negated = 0;
while (num) {
if (human_flag) {
if (len == 3) {
*--s = number_separator;
len = 1;
} else
len++;
}
*--s = (char)(num % 10) + '0';
num /= 10;
}
if (negated)
*--s = '-';
return s;
}
/* Return the double number as a string. If the human_flag option is > 1,
* we may output the number in K, M, G, or T units. The buffer we use for
* our result is either a single static buffer defined here, or a buffer
* we get from do_big_num(). */
char *do_big_dnum(double dnum, int human_flag, int decimal_digits)
{
static char tmp_buf[128];
#if SIZEOF_INT64 >= 8
char *fract;
snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
if (!human_flag || (dnum < 1000.0 && dnum > -1000.0))
return tmp_buf;
for (fract = tmp_buf+1; isDigit(fract); fract++) {}
return do_big_num((int64)dnum, human_flag, fract);
#else
/* A big number might lose digits converting to a too-short int64,
* so let's just return the raw double conversion. */
snprintf(tmp_buf, sizeof tmp_buf, "%.*f", decimal_digits, dnum);
return tmp_buf;
#endif
}
+4 -8
View File
@@ -295,9 +295,8 @@ int getaddrinfo(const char *node,
res);
} else if (hints.ai_flags & AI_NUMERICHOST) {
struct in_addr ip;
if (!inet_aton(node, &ip)) {
if (inet_pton(AF_INET, node, &ip) <= 0)
return EAI_FAIL;
}
return getaddr_info_single_addr(service,
ntohl(ip.s_addr),
&hints,
@@ -492,13 +491,10 @@ int getnameinfo(const struct sockaddr *sa, socklen_t salen,
return EAI_FAIL;
}
/* We don't support those. */
if ((node && !(flags & NI_NUMERICHOST))
|| (service && !(flags & NI_NUMERICSERV)))
return EAI_FAIL;
if (node) {
return gethostnameinfo(sa, node, nodelen, flags);
int ret = gethostnameinfo(sa, node, nodelen, flags);
if (ret)
return ret;
}
if (service) {
+72
View File
@@ -0,0 +1,72 @@
/*
* An implementation of getpass for systems that lack one.
*
* Copyright (C) 2013 Roman Donchenko
*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, visit the http://fsf.org website.
*/
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include "rsync.h"
char *getpass(const char *prompt)
{
static char password[256];
BOOL tty_changed = False, read_success;
struct termios tty_old, tty_new;
FILE *in = stdin, *out = stderr;
FILE *tty = fopen("/dev/tty", "w+");
if (tty)
in = out = tty;
if (tcgetattr(fileno(in), &tty_old) == 0) {
tty_new = tty_old;
tty_new.c_lflag &= ~(ECHO | ISIG);
if (tcsetattr(fileno(in), TCSAFLUSH, &tty_new) == 0)
tty_changed = True;
}
if (!tty_changed)
fputs("(WARNING: will be visible) ", out);
fputs(prompt, out);
fflush(out);
read_success = fgets(password, sizeof password, in) != NULL;
/* Print the newline that hasn't been echoed. */
fputc('\n', out);
if (tty_changed)
tcsetattr(fileno(in), TCSAFLUSH, &tty_old);
if (tty)
fclose(tty);
if (read_success) {
/* Remove the trailing newline. */
size_t password_len = strlen(password);
if (password_len && password[password_len - 1] == '\n')
password[password_len - 1] = '\0';
return password;
}
return NULL;
}
+37
View File
@@ -0,0 +1,37 @@
/* 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
#define CSUM_MD4_OLD 3
#define CSUM_MD4 4
#define CSUM_MD5 5
#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
+703
View File
@@ -0,0 +1,703 @@
/*
* x86-64 optimized assembler MD5 implementation
*
* Author: Marc Bevand, 2004
*
* This code was placed in the public domain by the author. The original
* publication can be found at:
*
* https://www.zorinaq.com/papers/md5-amd64.html
*/
/*
* No modifications were made aside from changing the function and file names.
* The MD5_CTX structure as expected here (from OpenSSL) is binary compatible
* with the md_context used by rsync, for the fields accessed.
*
* Benchmarks (in MB/s) C ASM
* - Intel Atom D2700 302 334
* - Intel i7-7700hq 351 376
* - AMD ThreadRipper 2950x 728 784
*
* The original code was also incorporated into OpenSSL. It has since been
* modified there. Those changes have not been made here due to licensing
* incompatibilities. Benchmarks of those changes on the above CPUs did not
* show any significant difference in performance, though.
*/
#include "config.h"
#include "md-defines.h"
#ifdef USE_MD5_ASM /* { */
#ifdef __APPLE__
#define md5_process_asm _md5_process_asm
#endif
.text
/* .balign = N bytes everywhere; bare .align means 2^N on Mach-O (would ask
* for 64KB alignment and trip a macOS linker warning). */
.balign 16
.globl md5_process_asm
md5_process_asm:
push %rbp
push %rbx
push %r12
push %r13 # not really useful (r13 is unused)
push %r14
push %r15
# rdi = arg #1 (ctx, MD5_CTX pointer)
# rsi = arg #2 (ptr, data pointer)
# rdx = arg #3 (nbr, number of 16-word blocks to process)
mov %rdi, %rbp # rbp = ctx
shl $6, %rdx # rdx = nbr in bytes
lea (%rsi,%rdx), %rdi # rdi = end
mov 0*4(%rbp), %eax # eax = ctx->A
mov 1*4(%rbp), %ebx # ebx = ctx->B
mov 2*4(%rbp), %ecx # ecx = ctx->C
mov 3*4(%rbp), %edx # edx = ctx->D
# end is 'rdi'
# ptr is 'rsi'
# A is 'eax'
# B is 'ebx'
# C is 'ecx'
# D is 'edx'
cmp %rdi, %rsi # cmp end with ptr
je 1f # jmp if ptr == end
# BEGIN of loop over 16-word blocks
2: # save old values of A, B, C, D
mov %eax, %r8d
mov %ebx, %r9d
mov %ecx, %r14d
mov %edx, %r15d
mov 0*4(%rsi), %r10d /* (NEXT STEP) X[0] */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
xor %ecx, %r11d /* y ^ ... */
lea -680876936(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r11d /* x & ... */
xor %edx, %r11d /* z ^ ... */
mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
add %r11d, %eax /* dst += ... */
rol $7, %eax /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %ebx, %eax /* dst += x */
xor %ebx, %r11d /* y ^ ... */
lea -389564586(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r11d /* x & ... */
xor %ecx, %r11d /* z ^ ... */
mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
add %r11d, %edx /* dst += ... */
rol $12, %edx /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %eax, %edx /* dst += x */
xor %eax, %r11d /* y ^ ... */
lea 606105819(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r11d /* x & ... */
xor %ebx, %r11d /* z ^ ... */
mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
add %r11d, %ecx /* dst += ... */
rol $17, %ecx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %edx, %ecx /* dst += x */
xor %edx, %r11d /* y ^ ... */
lea -1044525330(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r11d /* x & ... */
xor %eax, %r11d /* z ^ ... */
mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
add %r11d, %ebx /* dst += ... */
rol $22, %ebx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %ecx, %ebx /* dst += x */
xor %ecx, %r11d /* y ^ ... */
lea -176418897(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r11d /* x & ... */
xor %edx, %r11d /* z ^ ... */
mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
add %r11d, %eax /* dst += ... */
rol $7, %eax /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %ebx, %eax /* dst += x */
xor %ebx, %r11d /* y ^ ... */
lea 1200080426(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r11d /* x & ... */
xor %ecx, %r11d /* z ^ ... */
mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
add %r11d, %edx /* dst += ... */
rol $12, %edx /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %eax, %edx /* dst += x */
xor %eax, %r11d /* y ^ ... */
lea -1473231341(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r11d /* x & ... */
xor %ebx, %r11d /* z ^ ... */
mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
add %r11d, %ecx /* dst += ... */
rol $17, %ecx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %edx, %ecx /* dst += x */
xor %edx, %r11d /* y ^ ... */
lea -45705983(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r11d /* x & ... */
xor %eax, %r11d /* z ^ ... */
mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
add %r11d, %ebx /* dst += ... */
rol $22, %ebx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %ecx, %ebx /* dst += x */
xor %ecx, %r11d /* y ^ ... */
lea 1770035416(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r11d /* x & ... */
xor %edx, %r11d /* z ^ ... */
mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
add %r11d, %eax /* dst += ... */
rol $7, %eax /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %ebx, %eax /* dst += x */
xor %ebx, %r11d /* y ^ ... */
lea -1958414417(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r11d /* x & ... */
xor %ecx, %r11d /* z ^ ... */
mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
add %r11d, %edx /* dst += ... */
rol $12, %edx /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %eax, %edx /* dst += x */
xor %eax, %r11d /* y ^ ... */
lea -42063(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r11d /* x & ... */
xor %ebx, %r11d /* z ^ ... */
mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
add %r11d, %ecx /* dst += ... */
rol $17, %ecx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %edx, %ecx /* dst += x */
xor %edx, %r11d /* y ^ ... */
lea -1990404162(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r11d /* x & ... */
xor %eax, %r11d /* z ^ ... */
mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
add %r11d, %ebx /* dst += ... */
rol $22, %ebx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %ecx, %ebx /* dst += x */
xor %ecx, %r11d /* y ^ ... */
lea 1804603682(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r11d /* x & ... */
xor %edx, %r11d /* z ^ ... */
mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
add %r11d, %eax /* dst += ... */
rol $7, %eax /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %ebx, %eax /* dst += x */
xor %ebx, %r11d /* y ^ ... */
lea -40341101(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r11d /* x & ... */
xor %ecx, %r11d /* z ^ ... */
mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
add %r11d, %edx /* dst += ... */
rol $12, %edx /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %eax, %edx /* dst += x */
xor %eax, %r11d /* y ^ ... */
lea -1502002290(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r11d /* x & ... */
xor %ebx, %r11d /* z ^ ... */
mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
add %r11d, %ecx /* dst += ... */
rol $17, %ecx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %edx, %ecx /* dst += x */
xor %edx, %r11d /* y ^ ... */
lea 1236535329(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r11d /* x & ... */
xor %eax, %r11d /* z ^ ... */
mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
add %r11d, %ebx /* dst += ... */
rol $22, %ebx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %ecx, %ebx /* dst += x */
mov 1*4(%rsi), %r10d /* (NEXT STEP) X[1] */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
mov %edx, %r12d /* (NEXT STEP) z' = %edx */
not %r11d /* not z */
lea -165796510(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r12d /* x & z */
and %ecx, %r11d /* y & (not z) */
mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %r12d, %eax /* dst += ... */
mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
rol $5, %eax /* dst <<< s */
add %ebx, %eax /* dst += x */
not %r11d /* not z */
lea -1069501632(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r12d /* x & z */
and %ebx, %r11d /* y & (not z) */
mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %r12d, %edx /* dst += ... */
mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
rol $9, %edx /* dst <<< s */
add %eax, %edx /* dst += x */
not %r11d /* not z */
lea 643717713(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r12d /* x & z */
and %eax, %r11d /* y & (not z) */
mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %r12d, %ecx /* dst += ... */
mov %eax, %r12d /* (NEXT STEP) z' = %eax */
rol $14, %ecx /* dst <<< s */
add %edx, %ecx /* dst += x */
not %r11d /* not z */
lea -373897302(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r12d /* x & z */
and %edx, %r11d /* y & (not z) */
mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %r12d, %ebx /* dst += ... */
mov %edx, %r12d /* (NEXT STEP) z' = %edx */
rol $20, %ebx /* dst <<< s */
add %ecx, %ebx /* dst += x */
not %r11d /* not z */
lea -701558691(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r12d /* x & z */
and %ecx, %r11d /* y & (not z) */
mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %r12d, %eax /* dst += ... */
mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
rol $5, %eax /* dst <<< s */
add %ebx, %eax /* dst += x */
not %r11d /* not z */
lea 38016083(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r12d /* x & z */
and %ebx, %r11d /* y & (not z) */
mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %r12d, %edx /* dst += ... */
mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
rol $9, %edx /* dst <<< s */
add %eax, %edx /* dst += x */
not %r11d /* not z */
lea -660478335(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r12d /* x & z */
and %eax, %r11d /* y & (not z) */
mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %r12d, %ecx /* dst += ... */
mov %eax, %r12d /* (NEXT STEP) z' = %eax */
rol $14, %ecx /* dst <<< s */
add %edx, %ecx /* dst += x */
not %r11d /* not z */
lea -405537848(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r12d /* x & z */
and %edx, %r11d /* y & (not z) */
mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %r12d, %ebx /* dst += ... */
mov %edx, %r12d /* (NEXT STEP) z' = %edx */
rol $20, %ebx /* dst <<< s */
add %ecx, %ebx /* dst += x */
not %r11d /* not z */
lea 568446438(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r12d /* x & z */
and %ecx, %r11d /* y & (not z) */
mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %r12d, %eax /* dst += ... */
mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
rol $5, %eax /* dst <<< s */
add %ebx, %eax /* dst += x */
not %r11d /* not z */
lea -1019803690(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r12d /* x & z */
and %ebx, %r11d /* y & (not z) */
mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %r12d, %edx /* dst += ... */
mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
rol $9, %edx /* dst <<< s */
add %eax, %edx /* dst += x */
not %r11d /* not z */
lea -187363961(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r12d /* x & z */
and %eax, %r11d /* y & (not z) */
mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %r12d, %ecx /* dst += ... */
mov %eax, %r12d /* (NEXT STEP) z' = %eax */
rol $14, %ecx /* dst <<< s */
add %edx, %ecx /* dst += x */
not %r11d /* not z */
lea 1163531501(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r12d /* x & z */
and %edx, %r11d /* y & (not z) */
mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %r12d, %ebx /* dst += ... */
mov %edx, %r12d /* (NEXT STEP) z' = %edx */
rol $20, %ebx /* dst <<< s */
add %ecx, %ebx /* dst += x */
not %r11d /* not z */
lea -1444681467(%eax,%r10d),%eax /* Const + dst + ... */
and %ebx, %r12d /* x & z */
and %ecx, %r11d /* y & (not z) */
mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ecx, %r11d /* (NEXT STEP) z' = %ecx */
add %r12d, %eax /* dst += ... */
mov %ecx, %r12d /* (NEXT STEP) z' = %ecx */
rol $5, %eax /* dst <<< s */
add %ebx, %eax /* dst += x */
not %r11d /* not z */
lea -51403784(%edx,%r10d),%edx /* Const + dst + ... */
and %eax, %r12d /* x & z */
and %ebx, %r11d /* y & (not z) */
mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %ebx, %r11d /* (NEXT STEP) z' = %ebx */
add %r12d, %edx /* dst += ... */
mov %ebx, %r12d /* (NEXT STEP) z' = %ebx */
rol $9, %edx /* dst <<< s */
add %eax, %edx /* dst += x */
not %r11d /* not z */
lea 1735328473(%ecx,%r10d),%ecx /* Const + dst + ... */
and %edx, %r12d /* x & z */
and %eax, %r11d /* y & (not z) */
mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %eax, %r11d /* (NEXT STEP) z' = %eax */
add %r12d, %ecx /* dst += ... */
mov %eax, %r12d /* (NEXT STEP) z' = %eax */
rol $14, %ecx /* dst <<< s */
add %edx, %ecx /* dst += x */
not %r11d /* not z */
lea -1926607734(%ebx,%r10d),%ebx /* Const + dst + ... */
and %ecx, %r12d /* x & z */
and %edx, %r11d /* y & (not z) */
mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
or %r11d, %r12d /* (y & (not z)) | (x & z) */
mov %edx, %r11d /* (NEXT STEP) z' = %edx */
add %r12d, %ebx /* dst += ... */
mov %edx, %r12d /* (NEXT STEP) z' = %edx */
rol $20, %ebx /* dst <<< s */
add %ecx, %ebx /* dst += x */
mov 5*4(%rsi), %r10d /* (NEXT STEP) X[5] */
mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
lea -378558(%eax,%r10d),%eax /* Const + dst + ... */
mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
xor %edx, %r11d /* z ^ ... */
xor %ebx, %r11d /* x ^ ... */
add %r11d, %eax /* dst += ... */
rol $4, %eax /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
add %ebx, %eax /* dst += x */
lea -2022574463(%edx,%r10d),%edx /* Const + dst + ... */
mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
xor %ecx, %r11d /* z ^ ... */
xor %eax, %r11d /* x ^ ... */
add %r11d, %edx /* dst += ... */
rol $11, %edx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) y' = %eax */
add %eax, %edx /* dst += x */
lea 1839030562(%ecx,%r10d),%ecx /* Const + dst + ... */
mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
xor %ebx, %r11d /* z ^ ... */
xor %edx, %r11d /* x ^ ... */
add %r11d, %ecx /* dst += ... */
rol $16, %ecx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) y' = %edx */
add %edx, %ecx /* dst += x */
lea -35309556(%ebx,%r10d),%ebx /* Const + dst + ... */
mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
xor %eax, %r11d /* z ^ ... */
xor %ecx, %r11d /* x ^ ... */
add %r11d, %ebx /* dst += ... */
rol $23, %ebx /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
add %ecx, %ebx /* dst += x */
lea -1530992060(%eax,%r10d),%eax /* Const + dst + ... */
mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
xor %edx, %r11d /* z ^ ... */
xor %ebx, %r11d /* x ^ ... */
add %r11d, %eax /* dst += ... */
rol $4, %eax /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
add %ebx, %eax /* dst += x */
lea 1272893353(%edx,%r10d),%edx /* Const + dst + ... */
mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
xor %ecx, %r11d /* z ^ ... */
xor %eax, %r11d /* x ^ ... */
add %r11d, %edx /* dst += ... */
rol $11, %edx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) y' = %eax */
add %eax, %edx /* dst += x */
lea -155497632(%ecx,%r10d),%ecx /* Const + dst + ... */
mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
xor %ebx, %r11d /* z ^ ... */
xor %edx, %r11d /* x ^ ... */
add %r11d, %ecx /* dst += ... */
rol $16, %ecx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) y' = %edx */
add %edx, %ecx /* dst += x */
lea -1094730640(%ebx,%r10d),%ebx /* Const + dst + ... */
mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
xor %eax, %r11d /* z ^ ... */
xor %ecx, %r11d /* x ^ ... */
add %r11d, %ebx /* dst += ... */
rol $23, %ebx /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
add %ecx, %ebx /* dst += x */
lea 681279174(%eax,%r10d),%eax /* Const + dst + ... */
mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
xor %edx, %r11d /* z ^ ... */
xor %ebx, %r11d /* x ^ ... */
add %r11d, %eax /* dst += ... */
rol $4, %eax /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
add %ebx, %eax /* dst += x */
lea -358537222(%edx,%r10d),%edx /* Const + dst + ... */
mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
xor %ecx, %r11d /* z ^ ... */
xor %eax, %r11d /* x ^ ... */
add %r11d, %edx /* dst += ... */
rol $11, %edx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) y' = %eax */
add %eax, %edx /* dst += x */
lea -722521979(%ecx,%r10d),%ecx /* Const + dst + ... */
mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
xor %ebx, %r11d /* z ^ ... */
xor %edx, %r11d /* x ^ ... */
add %r11d, %ecx /* dst += ... */
rol $16, %ecx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) y' = %edx */
add %edx, %ecx /* dst += x */
lea 76029189(%ebx,%r10d),%ebx /* Const + dst + ... */
mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
xor %eax, %r11d /* z ^ ... */
xor %ecx, %r11d /* x ^ ... */
add %r11d, %ebx /* dst += ... */
rol $23, %ebx /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
add %ecx, %ebx /* dst += x */
lea -640364487(%eax,%r10d),%eax /* Const + dst + ... */
mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
xor %edx, %r11d /* z ^ ... */
xor %ebx, %r11d /* x ^ ... */
add %r11d, %eax /* dst += ... */
rol $4, %eax /* dst <<< s */
mov %ebx, %r11d /* (NEXT STEP) y' = %ebx */
add %ebx, %eax /* dst += x */
lea -421815835(%edx,%r10d),%edx /* Const + dst + ... */
mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
xor %ecx, %r11d /* z ^ ... */
xor %eax, %r11d /* x ^ ... */
add %r11d, %edx /* dst += ... */
rol $11, %edx /* dst <<< s */
mov %eax, %r11d /* (NEXT STEP) y' = %eax */
add %eax, %edx /* dst += x */
lea 530742520(%ecx,%r10d),%ecx /* Const + dst + ... */
mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
xor %ebx, %r11d /* z ^ ... */
xor %edx, %r11d /* x ^ ... */
add %r11d, %ecx /* dst += ... */
rol $16, %ecx /* dst <<< s */
mov %edx, %r11d /* (NEXT STEP) y' = %edx */
add %edx, %ecx /* dst += x */
lea -995338651(%ebx,%r10d),%ebx /* Const + dst + ... */
mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
xor %eax, %r11d /* z ^ ... */
xor %ecx, %r11d /* x ^ ... */
add %r11d, %ebx /* dst += ... */
rol $23, %ebx /* dst <<< s */
mov %ecx, %r11d /* (NEXT STEP) y' = %ecx */
add %ecx, %ebx /* dst += x */
mov 0*4(%rsi), %r10d /* (NEXT STEP) X[0] */
mov $0xffffffff, %r11d
xor %edx, %r11d /* (NEXT STEP) not z' = not %edx*/
lea -198630844(%eax,%r10d),%eax /* Const + dst + ... */
or %ebx, %r11d /* x | ... */
xor %ecx, %r11d /* y ^ ... */
add %r11d, %eax /* dst += ... */
mov 7*4(%rsi),%r10d /* (NEXT STEP) X[7] */
mov $0xffffffff, %r11d
rol $6, %eax /* dst <<< s */
xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
add %ebx, %eax /* dst += x */
lea 1126891415(%edx,%r10d),%edx /* Const + dst + ... */
or %eax, %r11d /* x | ... */
xor %ebx, %r11d /* y ^ ... */
add %r11d, %edx /* dst += ... */
mov 14*4(%rsi),%r10d /* (NEXT STEP) X[14] */
mov $0xffffffff, %r11d
rol $10, %edx /* dst <<< s */
xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
add %eax, %edx /* dst += x */
lea -1416354905(%ecx,%r10d),%ecx /* Const + dst + ... */
or %edx, %r11d /* x | ... */
xor %eax, %r11d /* y ^ ... */
add %r11d, %ecx /* dst += ... */
mov 5*4(%rsi),%r10d /* (NEXT STEP) X[5] */
mov $0xffffffff, %r11d
rol $15, %ecx /* dst <<< s */
xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
add %edx, %ecx /* dst += x */
lea -57434055(%ebx,%r10d),%ebx /* Const + dst + ... */
or %ecx, %r11d /* x | ... */
xor %edx, %r11d /* y ^ ... */
add %r11d, %ebx /* dst += ... */
mov 12*4(%rsi),%r10d /* (NEXT STEP) X[12] */
mov $0xffffffff, %r11d
rol $21, %ebx /* dst <<< s */
xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
add %ecx, %ebx /* dst += x */
lea 1700485571(%eax,%r10d),%eax /* Const + dst + ... */
or %ebx, %r11d /* x | ... */
xor %ecx, %r11d /* y ^ ... */
add %r11d, %eax /* dst += ... */
mov 3*4(%rsi),%r10d /* (NEXT STEP) X[3] */
mov $0xffffffff, %r11d
rol $6, %eax /* dst <<< s */
xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
add %ebx, %eax /* dst += x */
lea -1894986606(%edx,%r10d),%edx /* Const + dst + ... */
or %eax, %r11d /* x | ... */
xor %ebx, %r11d /* y ^ ... */
add %r11d, %edx /* dst += ... */
mov 10*4(%rsi),%r10d /* (NEXT STEP) X[10] */
mov $0xffffffff, %r11d
rol $10, %edx /* dst <<< s */
xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
add %eax, %edx /* dst += x */
lea -1051523(%ecx,%r10d),%ecx /* Const + dst + ... */
or %edx, %r11d /* x | ... */
xor %eax, %r11d /* y ^ ... */
add %r11d, %ecx /* dst += ... */
mov 1*4(%rsi),%r10d /* (NEXT STEP) X[1] */
mov $0xffffffff, %r11d
rol $15, %ecx /* dst <<< s */
xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
add %edx, %ecx /* dst += x */
lea -2054922799(%ebx,%r10d),%ebx /* Const + dst + ... */
or %ecx, %r11d /* x | ... */
xor %edx, %r11d /* y ^ ... */
add %r11d, %ebx /* dst += ... */
mov 8*4(%rsi),%r10d /* (NEXT STEP) X[8] */
mov $0xffffffff, %r11d
rol $21, %ebx /* dst <<< s */
xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
add %ecx, %ebx /* dst += x */
lea 1873313359(%eax,%r10d),%eax /* Const + dst + ... */
or %ebx, %r11d /* x | ... */
xor %ecx, %r11d /* y ^ ... */
add %r11d, %eax /* dst += ... */
mov 15*4(%rsi),%r10d /* (NEXT STEP) X[15] */
mov $0xffffffff, %r11d
rol $6, %eax /* dst <<< s */
xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
add %ebx, %eax /* dst += x */
lea -30611744(%edx,%r10d),%edx /* Const + dst + ... */
or %eax, %r11d /* x | ... */
xor %ebx, %r11d /* y ^ ... */
add %r11d, %edx /* dst += ... */
mov 6*4(%rsi),%r10d /* (NEXT STEP) X[6] */
mov $0xffffffff, %r11d
rol $10, %edx /* dst <<< s */
xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
add %eax, %edx /* dst += x */
lea -1560198380(%ecx,%r10d),%ecx /* Const + dst + ... */
or %edx, %r11d /* x | ... */
xor %eax, %r11d /* y ^ ... */
add %r11d, %ecx /* dst += ... */
mov 13*4(%rsi),%r10d /* (NEXT STEP) X[13] */
mov $0xffffffff, %r11d
rol $15, %ecx /* dst <<< s */
xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
add %edx, %ecx /* dst += x */
lea 1309151649(%ebx,%r10d),%ebx /* Const + dst + ... */
or %ecx, %r11d /* x | ... */
xor %edx, %r11d /* y ^ ... */
add %r11d, %ebx /* dst += ... */
mov 4*4(%rsi),%r10d /* (NEXT STEP) X[4] */
mov $0xffffffff, %r11d
rol $21, %ebx /* dst <<< s */
xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
add %ecx, %ebx /* dst += x */
lea -145523070(%eax,%r10d),%eax /* Const + dst + ... */
or %ebx, %r11d /* x | ... */
xor %ecx, %r11d /* y ^ ... */
add %r11d, %eax /* dst += ... */
mov 11*4(%rsi),%r10d /* (NEXT STEP) X[11] */
mov $0xffffffff, %r11d
rol $6, %eax /* dst <<< s */
xor %ecx, %r11d /* (NEXT STEP) not z' = not %ecx */
add %ebx, %eax /* dst += x */
lea -1120210379(%edx,%r10d),%edx /* Const + dst + ... */
or %eax, %r11d /* x | ... */
xor %ebx, %r11d /* y ^ ... */
add %r11d, %edx /* dst += ... */
mov 2*4(%rsi),%r10d /* (NEXT STEP) X[2] */
mov $0xffffffff, %r11d
rol $10, %edx /* dst <<< s */
xor %ebx, %r11d /* (NEXT STEP) not z' = not %ebx */
add %eax, %edx /* dst += x */
lea 718787259(%ecx,%r10d),%ecx /* Const + dst + ... */
or %edx, %r11d /* x | ... */
xor %eax, %r11d /* y ^ ... */
add %r11d, %ecx /* dst += ... */
mov 9*4(%rsi),%r10d /* (NEXT STEP) X[9] */
mov $0xffffffff, %r11d
rol $15, %ecx /* dst <<< s */
xor %eax, %r11d /* (NEXT STEP) not z' = not %eax */
add %edx, %ecx /* dst += x */
lea -343485551(%ebx,%r10d),%ebx /* Const + dst + ... */
or %ecx, %r11d /* x | ... */
xor %edx, %r11d /* y ^ ... */
add %r11d, %ebx /* dst += ... */
mov 0*4(%rsi),%r10d /* (NEXT STEP) X[0] */
mov $0xffffffff, %r11d
rol $21, %ebx /* dst <<< s */
xor %edx, %r11d /* (NEXT STEP) not z' = not %edx */
add %ecx, %ebx /* dst += x */
# add old values of A, B, C, D
add %r8d, %eax
add %r9d, %ebx
add %r14d, %ecx
add %r15d, %edx
# loop control
add $64, %rsi # ptr += 64
cmp %rdi, %rsi # cmp end with ptr
jb 2b # jmp if ptr < end
# END of loop over 16-word blocks
1:
mov %eax, 0*4(%rbp) # ctx->A = A
mov %ebx, 1*4(%rbp) # ctx->B = B
mov %ecx, 2*4(%rbp) # ctx->C = C
mov %edx, 3*4(%rbp) # ctx->D = D
pop %r15
pop %r14
pop %r13 # not really useful (r13 is unused)
pop %r12
pop %rbx
pop %rbp
ret
#endif /* } USE_MD5_ASM */
+43 -26
View File
@@ -2,6 +2,7 @@
* RFC 1321 compliant MD5 implementation
*
* Copyright (C) 2001-2003 Christophe Devine
* 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
@@ -38,22 +39,22 @@ static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
C = ctx->C;
D = ctx->D;
X[0] = IVAL(data, 0);
X[1] = IVAL(data, 4);
X[2] = IVAL(data, 8);
X[3] = IVAL(data, 12);
X[4] = IVAL(data, 16);
X[5] = IVAL(data, 20);
X[6] = IVAL(data, 24);
X[7] = IVAL(data, 28);
X[8] = IVAL(data, 32);
X[9] = IVAL(data, 36);
X[10] = IVAL(data, 40);
X[11] = IVAL(data, 44);
X[12] = IVAL(data, 48);
X[13] = IVAL(data, 52);
X[14] = IVAL(data, 56);
X[15] = IVAL(data, 60);
X[0] = IVALu(data, 0);
X[1] = IVALu(data, 4);
X[2] = IVALu(data, 8);
X[3] = IVALu(data, 12);
X[4] = IVALu(data, 16);
X[5] = IVALu(data, 20);
X[6] = IVALu(data, 24);
X[7] = IVALu(data, 28);
X[8] = IVALu(data, 32);
X[9] = IVALu(data, 36);
X[10] = IVALu(data, 40);
X[11] = IVALu(data, 44);
X[12] = IVALu(data, 48);
X[13] = IVALu(data, 52);
X[14] = IVALu(data, 56);
X[15] = IVALu(data, 60);
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
@@ -146,6 +147,13 @@ static void md5_process(md_context *ctx, const uchar data[CSUM_CHUNK])
ctx->D += D;
}
#ifdef USE_MD5_ASM
#if CSUM_CHUNK != 64
#error The MD5 ASM code does not support CSUM_CHUNK != 64
#endif
extern void md5_process_asm(md_context *ctx, const void *data, size_t num);
#endif
void md5_update(md_context *ctx, const uchar *input, uint32 length)
{
uint32 left, fill;
@@ -170,17 +178,26 @@ void md5_update(md_context *ctx, const uchar *input, uint32 length)
left = 0;
}
#ifdef USE_MD5_ASM /* { */
if (length >= CSUM_CHUNK) {
uint32 chunks = length / CSUM_CHUNK;
md5_process_asm(ctx, input, chunks);
length -= chunks * CSUM_CHUNK;
input += chunks * CSUM_CHUNK;
}
#else /* } { */
while (length >= CSUM_CHUNK) {
md5_process(ctx, input);
length -= CSUM_CHUNK;
input += CSUM_CHUNK;
}
#endif /* } */
if (length)
memcpy(ctx->buffer + left, input, length);
}
static uchar md5_padding[CSUM_CHUNK] = { 0x80 };
static const uchar md5_padding[CSUM_CHUNK] = { 0x80 };
void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
{
@@ -192,8 +209,8 @@ void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
| (ctx->totalN2 << 3);
low = (ctx->totalN << 3);
SIVAL(msglen, 0, low);
SIVAL(msglen, 4, high);
SIVALu(msglen, 0, low);
SIVALu(msglen, 4, high);
last = ctx->totalN & 0x3F;
padn = last < 56 ? 56 - last : 120 - last;
@@ -201,12 +218,14 @@ void md5_result(md_context *ctx, uchar digest[MD5_DIGEST_LEN])
md5_update(ctx, md5_padding, padn);
md5_update(ctx, msglen, 8);
SIVAL(digest, 0, ctx->A);
SIVAL(digest, 4, ctx->B);
SIVAL(digest, 8, ctx->C);
SIVAL(digest, 12, ctx->D);
SIVALu(digest, 0, ctx->A);
SIVALu(digest, 4, ctx->B);
SIVALu(digest, 8, ctx->C);
SIVALu(digest, 12, ctx->D);
}
#ifdef TEST_MD5 /* { */
void get_md5(uchar *out, const uchar *input, int n)
{
md_context ctx;
@@ -215,8 +234,6 @@ void get_md5(uchar *out, const uchar *input, int n)
md5_result(&ctx, out);
}
#ifdef TEST_MD5
#include <stdlib.h>
#include <stdio.h>
@@ -301,4 +318,4 @@ int main(int argc, char *argv[])
return 0;
}
#endif
#endif /* } */
+5 -4
View File
@@ -4,7 +4,7 @@
* An implementation of MD4 designed for use in the SMB authentication protocol.
*
* Copyright (C) 1997-1998 Andrew Tridgell
* Copyright (C) 2005-2008 Wayne Davison
* Copyright (C) 2005-2020 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
@@ -89,8 +89,8 @@ static void copy64(uint32 *M, const uchar *in)
int i;
for (i = 0; i < MD4_DIGEST_LEN; i++) {
M[i] = (in[i*4+3] << 24) | (in[i*4+2] << 16)
| (in[i*4+1] << 8) | (in[i*4+0] << 0);
M[i] = ((uint32)in[i*4+3] << 24) | ((uint32)in[i*4+2] << 16)
| ((uint32)in[i*4+1] << 8) | ((uint32)in[i*4+0] << 0);
}
}
@@ -193,6 +193,8 @@ void mdfour_result(md_context *md, uchar digest[MD4_DIGEST_LEN])
copy4(digest+12, m->D);
}
#ifdef TEST_MDFOUR
void mdfour(uchar digest[MD4_DIGEST_LEN], uchar *in, int length)
{
md_context md;
@@ -201,7 +203,6 @@ void mdfour(uchar digest[MD4_DIGEST_LEN], uchar *in, int length)
mdfour_result(&md, digest);
}
#ifdef TEST_MDFOUR
int protocol_version = 28;
static void file_checksum1(char *fname)
+5 -9
View File
@@ -1,10 +1,10 @@
/* The include file for both the MD4 and MD5 routines. */
#define MD4_DIGEST_LEN 16
#define MD5_DIGEST_LEN 16
#define MAX_DIGEST_LEN MD5_DIGEST_LEN
#define CSUM_CHUNK 64
#ifdef USE_OPENSSL
#include <openssl/sha.h>
#include <openssl/evp.h>
#endif
#include "md-defines.h"
typedef struct {
uint32 A, B, C, D;
@@ -17,10 +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]);
void get_mdfour(uchar digest[MD4_DIGEST_LEN], const uchar *in, int length);
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]);
void get_md5(uchar digest[MD5_DIGEST_LEN], const uchar *input, int n);
+1 -1
View File
@@ -4,7 +4,7 @@
* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001 Martin Pool <mbp@samba.org>
* Copyright (C) 2003, 2006 Wayne Davison
* Copyright (C) 2003-2019 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 -13
View File
@@ -33,7 +33,7 @@ pool_alloc, pool_free, pool_free_old, pool_talloc, pool_tfree, pool_create, pool
.SH SYNOPSIS
.B #include "pool_alloc.h"
\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char *), int \fIflags\fB);
\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char*,char*,int), int \fIflags\fB);
\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);
@@ -95,25 +95,39 @@ for
.I quantum
will produce a quantum that should meet maximal alignment
on most platforms.
If
.B POOL_QALIGN
Unless
.B POOL_NO_QALIGN
is set in the
.IR flags ,
allocations will be aligned to addresses that are a
multiple of
.IR quantum .
A
.B NULL
may be specified for the
.I bomb
function pointer if it is not needed. (See the
.B pool_alloc()
function for how it is used.)
If
.B POOL_CLEAR
is set in the
.IR flags ,
all allocations from the pool will be initialized to zeros.
You may specify a
.B NULL
for the
.I bomb
function pointer if you don't wish to use it. (See the
.B pool_alloc()
function for how it is used.)
If either
.B POOL_PREPEND
or
.B POOL_INTERN
is specified in the
.IR flags ,
each extent's data structure will be allocated at the start of the
.IR size -length
buffer (rather than as a separate, non-pool allocation), with the
former extending the
.I size
to hold the structure, and the latter subtracting the structure's
length from the indicated
.IR size .
.P
.B pool_destroy()
destroys an allocation
@@ -131,8 +145,8 @@ is
.BR 0 ,
.I quantum
bytes will be allocated.
If the pool has been created with
.BR POOL_QALIGN ,
If the pool has been created without
.BR POOL_NO_QALIGN ,
every chunk of memory that is returned will be suitably aligned.
You can use this with the default
.I quantum
@@ -169,7 +183,7 @@ an extent), its memory will be completely freed back to the system.
If
.I addr
is
.BR 0 ,
.BR NULL ,
no memory will be freed, but subsequent allocations will come
from a new extent.
.P
+163 -80
View File
@@ -2,18 +2,19 @@
#define POOL_DEF_EXTENT (32 * 1024)
#define POOL_QALIGN_P2 (1<<16) /* power-of-2 qalign */
struct alloc_pool
{
size_t size; /* extent size */
size_t quantum; /* allocation quantum */
struct pool_extent *extents; /* top extent is "live" */
void (*bomb)(); /* function to call if
* malloc fails */
void (*bomb)(const char*, const char*, int); /* called if malloc fails */
int flags;
/* statistical data */
unsigned long e_created; /* extents created */
unsigned long e_freed; /* extents detroyed */
unsigned long e_freed; /* extents destroyed */
int64 n_allocated; /* calls to alloc */
int64 n_freed; /* calls to free */
int64 b_allocated; /* cum. bytes allocated */
@@ -22,16 +23,18 @@ struct alloc_pool
struct pool_extent
{
struct pool_extent *next;
void *start; /* starting address */
size_t free; /* free bytecount */
size_t bound; /* bytes bound by padding,
* overhead and freed */
struct pool_extent *next;
size_t bound; /* trapped free bytes */
};
struct align_test {
void *foo;
int64 bar;
uchar foo;
union {
int64 i;
void *p;
} bar;
};
#define MINALIGN offsetof(struct align_test, bar)
@@ -39,24 +42,73 @@ 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)) )
/* Under AddressSanitizer, fence each pool_alloc() chunk with a poisoned
* redzone just below it (allocations grow downward from the top of an extent).
* A bump allocator hands out chunks from one big malloc, so ASan cannot see a
* write that underflows one chunk into its neighbour -- e.g. a miscomputed
* F_SUM() reaching before a file_struct's extras. The redzone turns that into
* a hard ASan report. We unpoison a whole extent whenever its space is reused
* (reset/reclaim), so legitimate later allocations never trip over old
* redzones; ASan unpoisons freed extents itself via free(). */
#if defined(__SANITIZE_ADDRESS__)
# define POOL_ASAN 1
#elif defined(__has_feature)
# if __has_feature(address_sanitizer)
# define POOL_ASAN 1
# endif
#endif
#ifdef POOL_ASAN
# include <sanitizer/asan_interface.h>
# define POOL_REDZONE 16 /* >= the largest pool-relative underflow we guard */
# define POOL_POISON(p,n) ASAN_POISON_MEMORY_REGION((p), (n))
# define POOL_UNPOISON(p,n) ASAN_UNPOISON_MEMORY_REGION((p), (n))
#else
# define POOL_POISON(p,n) ((void)0)
# define POOL_UNPOISON(p,n) ((void)0)
#endif
alloc_pool_t
pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags)
pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags)
{
struct alloc_pool *pool;
struct alloc_pool *pool;
if (!(pool = new(struct alloc_pool)))
return pool;
memset(pool, 0, sizeof (struct alloc_pool));
pool->size = size /* round extent size to min alignment reqs */
? (size + MINALIGN - 1) & ~(MINALIGN - 1)
: POOL_DEF_EXTENT;
if (flags & POOL_INTERN) {
pool->size -= sizeof (struct pool_extent);
flags |= POOL_APPEND;
if ((MINALIGN & (MINALIGN - 1)) != (0)) {
if (bomb)
(*bomb)("Compiler error: MINALIGN is not a power of 2", __FILE__, __LINE__);
return NULL;
}
pool->quantum = quantum ? quantum : MINALIGN;
if (!(pool = new0(struct alloc_pool)))
return NULL;
if (!size)
size = POOL_DEF_EXTENT;
if (!quantum)
quantum = MINALIGN;
if (flags & POOL_INTERN) {
if (size <= sizeof (struct pool_extent))
size = quantum;
else
size -= sizeof (struct pool_extent);
flags |= POOL_PREPEND;
}
if (quantum <= 1)
flags = (flags | POOL_NO_QALIGN) & ~POOL_QALIGN_P2;
else if (!(flags & POOL_NO_QALIGN)) {
if (size % quantum)
size += quantum - size % quantum;
/* If quantum is a power of 2, we'll avoid using modulus. */
if (!(quantum & (quantum - 1)))
flags |= POOL_QALIGN_P2;
}
pool->size = size;
pool->quantum = quantum;
pool->bomb = bomb;
pool->flags = flags;
@@ -67,17 +119,21 @@ void
pool_destroy(alloc_pool_t p)
{
struct alloc_pool *pool = (struct alloc_pool *) p;
struct pool_extent *cur, *next;
struct pool_extent *cur, *next;
if (!pool)
return;
for (cur = pool->extents; cur; cur = next) {
next = cur->next;
free(cur->start);
if (!(pool->flags & POOL_APPEND))
if (pool->flags & POOL_PREPEND)
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
else {
free(cur->start);
free(cur);
}
}
free(pool);
}
@@ -90,45 +146,40 @@ pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
if (!len)
len = pool->quantum;
else if (pool->quantum > 1 && len % pool->quantum)
len += pool->quantum - len % pool->quantum;
else if (pool->flags & POOL_QALIGN_P2) {
if (len & (pool->quantum - 1))
len += pool->quantum - (len & (pool->quantum - 1));
} else if (!(pool->flags & POOL_NO_QALIGN)) {
if (len % pool->quantum)
len += pool->quantum - len % pool->quantum;
}
if (len > pool->size)
goto bomb_out;
if (!pool->extents || len > pool->extents->free) {
void *start;
size_t free;
size_t bound;
size_t skew;
size_t asize;
void *start;
size_t asize;
struct pool_extent *ext;
free = pool->size;
bound = 0;
asize = pool->size;
if (pool->flags & POOL_APPEND)
if (pool->flags & POOL_PREPEND)
asize += sizeof (struct pool_extent);
if (!(start = new_array(char, asize)))
goto bomb_out;
if (pool->flags & POOL_CLEAR)
memset(start, 0, free);
memset(start, 0, asize);
if (pool->flags & POOL_APPEND)
ext = PTR_ADD(start, free);
else if (!(ext = new(struct pool_extent)))
if (pool->flags & POOL_PREPEND) {
ext = start;
start = PTR_ADD(start, sizeof (struct pool_extent));
} else if (!(ext = new(struct pool_extent)))
goto bomb_out;
if (pool->flags & POOL_QALIGN && pool->quantum > 1
&& (skew = (size_t)PTR_ADD(start, free) % pool->quantum)) {
bound += skew;
free -= skew;
}
ext->start = start;
ext->free = free;
ext->bound = bound;
ext->free = pool->size;
ext->bound = 0;
ext->next = pool->extents;
pool->extents = ext;
@@ -140,11 +191,22 @@ pool_alloc(alloc_pool_t p, size_t len, const char *bomb_msg)
pool->extents->free -= len;
return PTR_ADD(pool->extents->start, pool->extents->free);
{
void *ret = PTR_ADD(pool->extents->start, pool->extents->free);
#ifdef POOL_ASAN
size_t rz = pool->extents->free < POOL_REDZONE
? pool->extents->free : POOL_REDZONE;
if (rz) {
pool->extents->free -= rz;
POOL_POISON(PTR_ADD(pool->extents->start, pool->extents->free), rz);
}
#endif
return ret;
}
bomb_out:
if (pool->bomb)
(*pool->bomb)(bomb_msg);
(*pool->bomb)(bomb_msg, __FILE__, __LINE__);
return NULL;
}
@@ -160,10 +222,24 @@ pool_free(alloc_pool_t p, size_t len, void *addr)
if (!pool)
return;
if (!addr) {
/* A NULL addr starts a fresh extent for new allocations. */
if ((cur = pool->extents) != NULL && cur->free != pool->size) {
cur->bound += cur->free;
cur->free = 0;
}
return;
}
if (!len)
len = pool->quantum;
else if (pool->quantum > 1 && len % pool->quantum)
len += pool->quantum - len % pool->quantum;
else if (pool->flags & POOL_QALIGN_P2) {
if (len & (pool->quantum - 1))
len += pool->quantum - (len & (pool->quantum - 1));
} else if (!(pool->flags & POOL_NO_QALIGN)) {
if (len % pool->quantum)
len += pool->quantum - len % pool->quantum;
}
pool->n_freed++;
pool->b_freed += len;
@@ -176,22 +252,19 @@ pool_free(alloc_pool_t p, size_t len, void *addr)
if (!cur)
return;
/* This extent's space may be reused (and POOL_CLEAR may memset it)
* below, so drop any redzones in it first. */
POOL_UNPOISON(cur->start, pool->size);
if (!prev) {
/* The "live" extent is kept ready for more allocations. */
if (cur->free + cur->bound + len >= pool->size) {
size_t skew;
if (pool->flags & POOL_CLEAR) {
memset(PTR_ADD(cur->start, cur->free), 0,
pool->size - cur->free);
}
cur->free = pool->size;
cur->bound = 0;
if (pool->flags & POOL_QALIGN && pool->quantum > 1
&& (skew = (size_t)PTR_ADD(cur->start, cur->free) % pool->quantum)) {
cur->bound += skew;
cur->free -= skew;
}
} else if (addr == PTR_ADD(cur->start, cur->free)) {
if (pool->flags & POOL_CLEAR)
memset(addr, 0, len);
@@ -203,9 +276,12 @@ pool_free(alloc_pool_t p, size_t len, void *addr)
if (cur->free + cur->bound >= pool->size) {
prev->next = cur->next;
free(cur->start);
if (!(pool->flags & POOL_APPEND))
if (pool->flags & POOL_PREPEND)
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
else {
free(cur->start);
free(cur);
}
pool->e_freed++;
} else if (prev != pool->extents) {
/* Move the extent to be the first non-live extent. */
@@ -237,23 +313,18 @@ pool_free_old(alloc_pool_t p, void *addr)
if (!cur)
return;
POOL_UNPOISON(cur->start, pool->size);
if (addr == PTR_ADD(cur->start, cur->free)) {
if (prev) {
prev->next = NULL;
next = cur;
} else {
size_t skew;
/* The most recent live extent can just be reset. */
if (pool->flags & POOL_CLEAR)
memset(addr, 0, pool->size - cur->free);
cur->free = pool->size;
cur->bound = 0;
if (pool->flags & POOL_QALIGN && pool->quantum > 1
&& (skew = (size_t)PTR_ADD(cur->start, cur->free) % pool->quantum)) {
cur->bound += skew;
cur->free -= skew;
}
next = cur->next;
cur->next = NULL;
}
@@ -264,9 +335,12 @@ pool_free_old(alloc_pool_t p, void *addr)
while ((cur = next) != NULL) {
next = cur->next;
free(cur->start);
if (!(pool->flags & POOL_APPEND))
if (pool->flags & POOL_PREPEND)
free(PTR_SUB(cur->start, sizeof (struct pool_extent)));
else {
free(cur->start);
free(cur);
}
pool->e_freed++;
}
}
@@ -295,24 +369,30 @@ pool_boundary(alloc_pool_t p, size_t len)
}
#define FDPRINT(label, value) \
snprintf(buf, sizeof buf, label, value), \
write(fd, buf, strlen(buf))
do { \
int len = snprintf(buf, sizeof buf, label, value); \
if (write(fd, buf, len) != len) \
ret = -1; \
} while (0)
#define FDEXTSTAT(ext) \
snprintf(buf, sizeof buf, " %12ld %5ld\n", \
(long) ext->free, \
(long) ext->bound), \
write(fd, buf, strlen(buf))
do { \
int len = snprintf(buf, sizeof buf, " %12ld %5ld\n", \
(long)ext->free, (long)ext->bound); \
if (write(fd, buf, len) != len) \
ret = -1; \
} while (0)
void
int
pool_stats(alloc_pool_t p, int fd, int summarize)
{
struct alloc_pool *pool = (struct alloc_pool *) p;
struct pool_extent *cur;
struct pool_extent *cur;
char buf[BUFSIZ];
int ret = 0;
if (!pool)
return;
return ret;
FDPRINT(" Extent size: %12ld\n", (long) pool->size);
FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum);
@@ -324,13 +404,16 @@ pool_stats(alloc_pool_t p, int fd, int summarize)
FDPRINT(" Bytes freed: %12.0f\n", (double) pool->b_freed);
if (summarize)
return;
return ret;
if (!pool->extents)
return;
return ret;
write(fd, "\n", 1);
if (write(fd, "\n", 1) != 1)
ret = -1;
for (cur = pool->extents; cur; cur = cur->next)
FDEXTSTAT(cur);
return ret;
}
+3 -3
View File
@@ -1,13 +1,13 @@
#include <stddef.h>
#define POOL_CLEAR (1<<0) /* zero fill allocations */
#define POOL_QALIGN (1<<1) /* align data to quanta */
#define POOL_NO_QALIGN (1<<1) /* don't align data to quanta */
#define POOL_INTERN (1<<2) /* Allocate extent structures */
#define POOL_APPEND (1<<3) /* or appended to extent data */
#define POOL_PREPEND (1<<3) /* or prepend to extent data */
typedef void *alloc_pool_t;
alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char *), int flags);
alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(const char*, const char*, int), int flags);
void pool_destroy(alloc_pool_t pool);
void *pool_alloc(alloc_pool_t pool, size_t size, const char *bomb_msg);
void pool_free(alloc_pool_t pool, size_t size, void *addr);
+708 -217
View File
File diff suppressed because it is too large. Load diff
+571 -408
View File
File diff suppressed because it is too large. Load diff
+15 -2
View File
@@ -3,7 +3,7 @@
* Version 2.2.x
* Portable SMB ACL interface
* Copyright (C) Jeremy Allison 2000
* Copyright (C) 2007-2008 Wayne Davison
* Copyright (C) 2007-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -139,7 +139,9 @@ typedef struct acl *SMB_ACL_ENTRY_T;
/* Based on the Solaris & UnixWare code. */
#ifndef __TANDEM
#undef GROUP
#endif
#include <sys/aclv.h>
/* SVR4.2 ES/MP ACLs */
@@ -230,7 +232,7 @@ struct new_acl_entry{
#define SMB_ACL_ENTRY_T struct new_acl_entry*
#define SMB_ACL_T struct acl_entry_link*
#define SMB_ACL_TAG_T unsigned short
#define SMB_ACL_TYPE_T int
@@ -299,7 +301,18 @@ int sys_acl_valid(SMB_ACL_T theacl);
int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
int sys_acl_set_fd(int fd, SMB_ACL_T theacl);
int sys_acl_delete_def_file(const char *name);
#ifdef HAVE_SOLARIS_ACLS
SMB_ACL_T sys_acl_get_fd_type(int fd, SMB_ACL_TYPE_T type);
int sys_acl_set_fd_type(int fd, SMB_ACL_TYPE_T type, SMB_ACL_T theacl);
int sys_acl_delete_def_fd(int fd);
#endif
int sys_acl_free_acl(SMB_ACL_T the_acl);
int no_acl_syscall_error(int err);
#ifdef HAVE_LIBACL_AT
SMB_ACL_T sys_acl_get_file_at(int dirfd, const char *path_p, int at_flags, SMB_ACL_TYPE_T type);
int sys_acl_set_file_at(int dirfd, const char *path_p, int at_flags, SMB_ACL_TYPE_T type, SMB_ACL_T theacl);
int sys_acl_delete_def_file_at(int dirfd, const char *path_p, int at_flags);
#endif
#endif /* SUPPORT_ACLS */
+301 -10
View File
@@ -2,7 +2,7 @@
* Extended attribute support for rsync.
*
* Copyright (C) 2004 Red Hat, Inc.
* Copyright (C) 2003-2008 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
* Written by Jay Fenlason.
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,10 @@
#ifdef SUPPORT_XATTRS
#ifdef HAVE_OSX_XATTRS
#define GETXATTR_FETCH_LIMIT (64*1024*1024)
#endif
#if defined HAVE_LINUX_XATTRS
ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
@@ -41,21 +45,53 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
return lsetxattr(path, name, value, size, 0);
}
int sys_fsetxattr(int filedes, const char *name, const void *value, size_t size)
{
return fsetxattr(filedes, name, value, size, 0);
}
int sys_lremovexattr(const char *path, const char *name)
{
return lremovexattr(path, name);
}
int sys_fremovexattr(int filedes, const char *name)
{
return fremovexattr(filedes, name);
}
ssize_t sys_llistxattr(const char *path, char *list, size_t size)
{
return llistxattr(path, list, size);
}
ssize_t sys_flistxattr(int filedes, char *list, size_t size)
{
return flistxattr(filedes, list, size);
}
#elif HAVE_OSX_XATTRS
ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
{
return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
ssize_t len = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
/* If we're retrieving data, handle resource forks > 64MB specially */
if (value != NULL && len == GETXATTR_FETCH_LIMIT && (size_t)len < size) {
/* getxattr will only return 64MB of data at a time, need to call again with a new offset */
u_int32_t offset = len;
size_t data_retrieved = len;
while (data_retrieved < size) {
len = getxattr(path, name, (char*)value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
if (len <= 0)
break;
data_retrieved += len;
offset += (u_int32_t)len;
}
len = data_retrieved;
}
return len;
}
ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
@@ -68,16 +104,31 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
}
int sys_fsetxattr(int filedes, const char *name, const void *value, size_t size)
{
return fsetxattr(filedes, name, value, size, 0, 0);
}
int sys_lremovexattr(const char *path, const char *name)
{
return removexattr(path, name, XATTR_NOFOLLOW);
}
int sys_fremovexattr(int filedes, const char *name)
{
return fremovexattr(filedes, name, 0);
}
ssize_t sys_llistxattr(const char *path, char *list, size_t size)
{
return listxattr(path, list, size, XATTR_NOFOLLOW);
}
ssize_t sys_flistxattr(int filedes, char *list, size_t size)
{
return flistxattr(filedes, list, size, 0);
}
#elif HAVE_FREEBSD_XATTRS
ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
@@ -95,27 +146,46 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size);
}
int sys_fsetxattr(int filedes, const char *name, const void *value, size_t size)
{
return extattr_set_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size);
}
int sys_lremovexattr(const char *path, const char *name)
{
return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name);
}
ssize_t sys_llistxattr(const char *path, char *list, size_t size)
int sys_fremovexattr(int filedes, const char *name)
{
return extattr_delete_fd(filedes, EXTATTR_NAMESPACE_USER, name);
}
/* Turn the FreeBSD extattr_list_xx() output (a single length byte before each
* name, no '\0' terminator) into the series of null-terminated strings that the
* rest of rsync expects. Since the size is unchanged, transform in place.
* Shared by the path and fd list variants. */
static ssize_t freebsd_list_finish(char *list, size_t size, ssize_t len)
{
unsigned char keylen;
ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size);
ssize_t off;
if (len <= 0 || (size_t)len > size)
if (len <= 0 || size == 0)
return len;
/* 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
* output in place. */
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;
}
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;
}
@@ -126,6 +196,227 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
return len;
}
ssize_t sys_llistxattr(const char *path, char *list, size_t size)
{
return freebsd_list_finish(list, size,
extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size));
}
ssize_t sys_flistxattr(int filedes, char *list, size_t size)
{
return freebsd_list_finish(list, size,
extattr_list_fd(filedes, EXTATTR_NAMESPACE_USER, list, size));
}
#elif HAVE_SOLARIS_XATTRS
static ssize_t read_xattr(int attrfd, void *buf, size_t buflen)
{
STRUCT_STAT sb;
ssize_t ret;
if (fstat(attrfd, &sb) < 0)
ret = -1;
else if (sb.st_size > SSIZE_MAX) {
errno = ERANGE;
ret = -1;
} else if (buflen == 0)
ret = sb.st_size;
else if (sb.st_size > buflen) {
errno = ERANGE;
ret = -1;
} else {
size_t bufpos;
for (bufpos = 0; bufpos < sb.st_size; ) {
ssize_t cnt = read(attrfd, (char*)buf + bufpos, sb.st_size - bufpos);
if (cnt <= 0) {
if (cnt < 0 && errno == EINTR)
continue;
bufpos = -1;
break;
}
bufpos += cnt;
}
ret = bufpos;
}
close(attrfd);
return ret;
}
ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
{
int attrfd;
if ((attrfd = attropen(path, name, O_RDONLY)) < 0) {
errno = ENOATTR;
return -1;
}
return read_xattr(attrfd, value, size);
}
ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
{
int attrfd;
if ((attrfd = openat(filedes, name, O_RDONLY|O_XATTR, 0)) < 0) {
errno = ENOATTR;
return -1;
}
return read_xattr(attrfd, value, size);
}
/* Write a datum to the already-opened attribute fd, closing it. Shared by the
* path- and fd-keyed setters below. */
static int write_xattr(int attrfd, const void *value, size_t size)
{
size_t bufpos;
int ret = 0, saved_errno = 0;
for (bufpos = 0; bufpos < size; ) {
ssize_t cnt = write(attrfd, (const char *)value + bufpos, size - bufpos);
if (cnt < 0) {
if (errno == EINTR)
continue;
ret = -1;
saved_errno = errno;
break;
}
if (cnt == 0) {
ret = -1;
saved_errno = EIO;
break;
}
bufpos += cnt;
}
/* Don't let close() clobber the write error; do report a close() failure. */
if (close(attrfd) < 0 && ret == 0)
return -1;
if (ret < 0 && saved_errno)
errno = saved_errno;
return ret;
}
int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size)
{
int attrfd;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
if ((attrfd = attropen(path, name, O_CREAT|O_TRUNC|O_WRONLY, mode)) < 0)
return -1;
return write_xattr(attrfd, value, size);
}
int sys_fsetxattr(int filedes, const char *name, const void *value, size_t size)
{
int attrfd;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
if ((attrfd = openat(filedes, name, O_CREAT|O_TRUNC|O_WRONLY|O_XATTR, mode)) < 0)
return -1;
return write_xattr(attrfd, value, size);
}
int sys_lremovexattr(const char *path, const char *name)
{
int attrdirfd;
int ret;
if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0)
return -1;
ret = unlinkat(attrdirfd, name, 0);
close(attrdirfd);
return ret;
}
int sys_fremovexattr(int filedes, const char *name)
{
int attrdirfd;
int ret;
if ((attrdirfd = openat(filedes, ".", O_RDONLY|O_XATTR, 0)) < 0)
return -1;
ret = unlinkat(attrdirfd, name, 0);
close(attrdirfd);
return ret;
}
/* List the names in an already-opened attribute-dir fd, consuming it. Shared
* by the path- and fd-keyed listers below. */
static ssize_t list_xattr(int attrdirfd, char *list, size_t size)
{
DIR *dirp;
struct dirent *dp;
ssize_t ret = 0;
if ((dirp = fdopendir(attrdirfd)) == NULL) {
close(attrdirfd);
return -1;
}
while ((dp = readdir(dirp))) {
int len = strlen(dp->d_name);
if (dp->d_name[0] == '.' && (len == 1 || (len == 2 && dp->d_name[1] == '.')))
continue;
if (len == 11 && dp->d_name[0] == 'S' && strncmp(dp->d_name, "SUNWattr_r", 10) == 0
&& (dp->d_name[10] == 'o' || dp->d_name[10] == 'w'))
continue;
ret += len + 1;
if ((size_t)ret > size) {
if (size == 0)
continue;
ret = -1;
errno = ERANGE;
break;
}
memcpy(list, dp->d_name, len+1);
list += len+1;
}
closedir(dirp);
return ret;
}
ssize_t sys_llistxattr(const char *path, char *list, size_t size)
{
int attrdirfd;
if ((attrdirfd = attropen(path, ".", O_RDONLY)) < 0) {
errno = ENOTSUP;
return -1;
}
return list_xattr(attrdirfd, list, size);
}
ssize_t sys_flistxattr(int filedes, char *list, size_t size)
{
int attrdirfd;
if ((attrdirfd = openat(filedes, ".", O_RDONLY|O_XATTR, 0)) < 0) {
errno = ENOTSUP;
return -1;
}
return list_xattr(attrdirfd, list, size);
}
#else
#error You need to create xattr compatibility functions.
+6 -3
View File
@@ -1,9 +1,9 @@
#ifdef SUPPORT_XATTRS
#if defined HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#elif defined HAVE_SYS_XATTR_H
#if defined HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#elif defined HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#elif defined HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#endif
@@ -16,8 +16,11 @@
ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size);
ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size);
int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size);
int sys_fsetxattr(int filedes, const char *name, const void *value, size_t size);
int sys_lremovexattr(const char *path, const char *name);
int sys_fremovexattr(int filedes, const char *name);
ssize_t sys_llistxattr(const char *path, char *list, size_t size);
ssize_t sys_flistxattr(int filedes, char *list, size_t size);
#else
+15 -2
View File
@@ -89,6 +89,11 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
p_ch = *++p;
/* FALLTHROUGH */
default:
/* iwildmatch() folds the text to lower case above; fold the pattern
* char too so matching is truly case-insensitive (not just text-side).
* Without this an upper-case "hosts deny" token fails OPEN. */
if (force_lower_case && ISUPPER(p_ch))
p_ch = tolower(p_ch);
if (t_ch != p_ch)
return FALSE;
continue;
@@ -150,6 +155,8 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
p_ch = *++p;
if (!p_ch)
return ABORT_ALL;
if (force_lower_case && ISUPPER(p_ch))
p_ch = tolower(p_ch);
if (t_ch == p_ch)
matched = TRUE;
} else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
@@ -159,6 +166,8 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
if (!p_ch)
return ABORT_ALL;
}
if (force_lower_case && ISUPPER(p_ch))
p_ch = tolower(p_ch);
if (t_ch <= p_ch && t_ch >= prev_ch)
matched = TRUE;
p_ch = 0; /* This makes "prev_ch" get set to 0. */
@@ -216,8 +225,12 @@ static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
} else /* malformed [:class:] string */
return ABORT_ALL;
p_ch = 0; /* This makes "prev_ch" get set to 0. */
} else if (t_ch == p_ch)
matched = TRUE;
} else {
if (force_lower_case && ISUPPER(p_ch))
p_ch = tolower(p_ch);
if (t_ch == p_ch)
matched = TRUE;
}
} while (prev_ch = p_ch, (p_ch = *++p) != ']');
if (matched == special || t_ch == '/')
return FALSE;
+529 -690
View File
File diff suppressed because it is too large. Load diff
+291 -165
View File
@@ -3,7 +3,7 @@
*
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2000-2001 Martin Pool <mbp@samba.org>
* Copyright (C) 2003-2008 Wayne Davison
* Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,9 +20,10 @@
*/
#include "rsync.h"
#include "ifuncs.h"
#include "itypes.h"
#include "inums.h"
#include "rounding.h" /* EXTRA_ROUNDING, so log_delete() aligns its file_struct */
extern int verbose;
extern int dry_run;
extern int am_daemon;
extern int am_server;
@@ -31,18 +32,18 @@ extern int am_generator;
extern int local_server;
extern int quiet;
extern int module_id;
extern int msg_fd_out;
extern int allow_8bit_chars;
extern int protocol_version;
extern int preserve_times;
extern int uid_ndx;
extern int gid_ndx;
extern int progress_is_active;
extern int always_checksum;
extern int preserve_mtimes;
extern int msgs2stderr;
extern int stdout_format_has_i;
extern int stdout_format_has_o_or_i;
extern int logfile_format_has_i;
extern int logfile_format_has_o_or_i;
extern int receiver_symlink_times;
extern int64 total_data_written;
extern int64 total_data_read;
extern mode_t orig_umask;
extern char *auth_user;
extern char *stdout_format;
@@ -52,11 +53,14 @@ extern char *logfile_name;
extern iconv_t ic_chck;
#endif
#ifdef ICONV_OPTION
extern iconv_t ic_send, ic_recv;
extern iconv_t ic_recv;
#endif
extern char curr_dir[];
extern char *module_dir;
extern char *full_module_path;
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;
@@ -64,10 +68,15 @@ static FILE *logfile_fp;
struct stats stats;
int got_xfer_error = 0;
int output_needs_newline = 0;
int send_msgs_to_gen = 0;
static int64 initial_data_written;
static int64 initial_data_read;
struct {
int code;
char const *name;
int code;
char const *name;
} const rerr_names[] = {
{ RERR_SYNTAX , "syntax or usage error" },
{ RERR_PROTOCOL , "protocol incompatibility" },
@@ -85,15 +94,15 @@ struct {
{ RERR_SIGNAL , "received SIGINT, SIGTERM, or SIGHUP" },
{ RERR_WAITCHILD , "waitpid() failed" },
{ RERR_MALLOC , "error allocating core memory buffers" },
{ RERR_PARTIAL , "some files could not be transferred" },
{ RERR_PARTIAL , "some files/attrs were not transferred (see previous errors)" },
{ RERR_VANISHED , "some files vanished before they could be transferred" },
{ RERR_DEL_LIMIT , "the --max-delete limit stopped deletions" },
{ RERR_TIMEOUT , "timeout in data send/receive" },
{ RERR_CONTIMEOUT , "timeout waiting for daemon connection" },
{ RERR_CMD_FAILED , "remote shell failed" },
{ RERR_CMD_KILLED , "remote shell killed" },
{ RERR_CMD_RUN , "remote command could not be run" },
{ RERR_CMD_NOTFOUND,"remote command not found" },
{ RERR_DEL_LIMIT , "the --max-delete limit stopped deletions" },
{ 0, NULL }
};
@@ -110,13 +119,20 @@ static char const *rerr_name(int code)
return NULL;
}
static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isprint, int escape_c1, char end_char);
static void logit(int priority, const char *buf)
{
if (logfile_was_closed)
logfile_reopen();
if (logfile_fp) {
fprintf(logfile_fp, "%s [%d] %s",
timestring(time(NULL)), (int)getpid(), buf);
/* Escape control chars in the message so an attacker-controlled
* filename can't inject terminal escapes into the log an admin later
* cat's (CWE-117); keep the trailing newline raw via end_char. */
int len = strlen(buf);
char trailing = len && (buf[len-1] == '\n' || buf[len-1] == '\r') ? buf[--len] : '\0';
fprintf(logfile_fp, "%s [%d] ", timestring(time(NULL)), (int)getpid());
filtered_fwrite(logfile_fp, buf, len, 0, 1, trailing);
fflush(logfile_fp);
} else {
syslog(priority, "%s", buf);
@@ -125,21 +141,16 @@ static void logit(int priority, const char *buf)
static void syslog_init()
{
static int been_here = 0;
int options = LOG_PID;
if (been_here)
return;
been_here = 1;
#ifdef LOG_NDELAY
options |= LOG_NDELAY;
#endif
#ifdef LOG_DAEMON
openlog("rsyncd", options, lp_syslog_facility(module_id));
openlog(lp_syslog_tag(module_id), options, lp_syslog_facility(module_id));
#else
openlog("rsyncd", options);
openlog(lp_syslog_tag(module_id), options);
#endif
#ifndef LOG_NDELAY
@@ -150,7 +161,15 @@ static void syslog_init()
static void logfile_open(void)
{
mode_t old_umask = umask(022 | orig_umask);
logfile_fp = fopen(logfile_name, "a");
/* --log-file/`log file =` are operator-supplied paths that may transit
* attacker-writable dirs; a planted symlink could redirect root's log
* into e.g. /root/.ssh/authorized_keys. Refuse symlinks not owned by
* uid 0 or our euid. */
int fd = vfs_open_owner_walk(logfile_name,
O_WRONLY | O_APPEND | O_CREAT, 0644, 0);
logfile_fp = fd >= 0 ? fdopen(fd, "a") : NULL;
if (!logfile_fp && fd >= 0)
close(fd);
umask(old_umask);
if (!logfile_fp) {
int fopen_errno = errno;
@@ -159,14 +178,16 @@ static void logfile_open(void)
rsyserr(FERROR, fopen_errno,
"failed to open log-file %s", logfile_name);
rprintf(FINFO, "Ignoring \"log file\" setting.\n");
logfile_name = "";
}
}
void log_init(int restart)
{
if (log_initialised) {
if (!restart)
if (!restart) /* Note: a restart only happens with am_daemon */
return;
assert(logfile_name); /* all am_daemon procs got at least an empty string */
if (strcmp(logfile_name, lp_log_file(module_id)) != 0) {
if (logfile_fp) {
fclose(logfile_fp);
@@ -176,7 +197,8 @@ void log_init(int restart)
logfile_name = NULL;
} else if (*logfile_name)
return; /* unchanged, non-empty "log file" names */
else if (lp_syslog_facility(-1) != lp_syslog_facility(module_id))
else if (lp_syslog_facility(-1) != lp_syslog_facility(module_id)
|| strcmp(lp_syslog_tag(-1), lp_syslog_tag(module_id)) != 0)
closelog();
else
return; /* unchanged syslog settings */
@@ -198,6 +220,7 @@ void log_init(int restart)
syslog_init();
}
/* Note that this close & reopen idiom intentionally ignores syslog logging. */
void logfile_close(void)
{
if (logfile_fp) {
@@ -215,25 +238,27 @@ void logfile_reopen(void)
}
}
static void filtered_fwrite(FILE *f, const char *buf, int len, int use_isprint)
static void filtered_fwrite(FILE *f, const char *in_buf, int in_len, int use_isprint, int escape_c1, char end_char)
{
const char *s, *end = buf + len;
for (s = buf; s < end; s++) {
if ((s < end - 4
&& *s == '\\' && s[1] == '#'
&& isDigit(s + 2)
&& isDigit(s + 3)
&& isDigit(s + 4))
|| (*s != '\t'
&& ((use_isprint && !isPrint(s))
|| *(uchar*)s < ' '))) {
if (s != buf && fwrite(buf, s - buf, 1, f) != 1)
char outbuf[1024], *ob = outbuf;
const char *end = in_buf + in_len;
while (in_buf < end) {
if (ob - outbuf >= (int)sizeof outbuf - 10) {
if (fwrite(outbuf, ob - outbuf, 1, f) != 1)
exit_cleanup(RERR_MESSAGEIO);
fprintf(f, "\\#%03o", *(uchar*)s);
buf = s + 1;
ob = outbuf;
}
if ((in_buf < end - 4 && *in_buf == '\\' && in_buf[1] == '#'
&& isDigit(in_buf + 2) && isDigit(in_buf + 3) && isDigit(in_buf + 4))
|| (*in_buf != '\t' && ((use_isprint && !isPrint(in_buf)) || *(uchar*)in_buf < ' '
|| (escape_c1 && *(uchar*)in_buf >= 0x80 && *(uchar*)in_buf <= 0x9f))))
ob += snprintf(ob, 6, "\\#%03o", *(uchar*)in_buf++);
else
*ob++ = *in_buf++;
}
if (buf != end && fwrite(buf, end - buf, 1, f) != 1)
if (end_char) /* The "- 10" above means that there is always room for one more char here. */
*ob++ = end_char;
if (ob != outbuf && fwrite(outbuf, ob - outbuf, 1, f) != 1)
exit_cleanup(RERR_MESSAGEIO);
}
@@ -242,8 +267,8 @@ static void filtered_fwrite(FILE *f, const char *buf, int len, int use_isprint)
* can happen with certain fatal conditions. */
void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
{
int trailing_CR_or_NL;
FILE *f = NULL;
char trailing_CR_or_NL;
FILE *f = msgs2stderr == 1 ? stderr : stdout;
#ifdef ICONV_OPTION
iconv_t ic = is_utf8 && ic_recv != (iconv_t)-1 ? ic_recv : ic_chck;
#else
@@ -255,15 +280,31 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
if (len < 0)
exit_cleanup(RERR_MESSAGEIO);
if (am_server && msg_fd_out >= 0) {
assert(!is_utf8);
/* Pass the message to our sibling. */
if (msgs2stderr == 1) {
/* A normal daemon can get msgs2stderr set if the socket is busted, so we
* change the message destination into an FLOG message in order to try to
* get some info about an abnormal-exit into the log file. An rsh daemon
* can have this set via user request, so we'll leave the code alone so
* that the msg gets logged and then sent to stderr after that. */
if (am_daemon > 0 && code != FCLIENT)
code = FLOG;
} else if (send_msgs_to_gen) {
/* Pass the message to our sibling in native charset. is_utf8
* may be set here if a malicious peer sends MSG_INFO/MSG_ERROR
* to a daemon receiver (read_a_msg passes !am_generator); the
* old assert(!is_utf8) made that a remotely-reachable abort.
* Forwarding the bytes raw is safe -- the generator's rwrite()
* gets is_utf8=0 and filtered_fwrite escapes non-printables. */
send_msg((enum msgcode)code, buf, len, 0);
return;
}
if (code == FERROR_SOCKET) /* This gets simplified for a non-sibling. */
code = FERROR;
else if (code == FERROR_UTF8) {
is_utf8 = 1;
code = FERROR;
}
if (code == FCLIENT)
code = FINFO;
@@ -277,7 +318,12 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
in_block = 1;
if (!log_initialised)
log_init(0);
strlcpy(msg, buf, MIN((int)sizeof msg, len + 1));
/* buf holds exactly len bytes and is not necessarily NUL-terminated
* (e.g. a forwarded MSG_* payload from read_a_msg), so copy by length
* rather than strlcpy(), which would strlen() past the end of buf. */
int mlen = MIN((int)sizeof msg - 1, len);
memcpy(msg, buf, mlen);
msg[mlen] = '\0';
logit(priority, msg);
in_block = 0;
@@ -286,26 +332,6 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
} else if (code == FLOG)
return;
if (quiet && code == FINFO)
return;
if (am_server) {
enum msgcode msg = (enum msgcode)code;
if (protocol_version < 30) {
if (msg == MSG_ERROR)
msg = MSG_ERROR_XFER;
else if (msg == MSG_WARNING)
msg = MSG_INFO;
}
/* Pass the message to the non-server side. */
if (send_msg(msg, buf, len, !is_utf8))
return;
if (am_daemon) {
/* TODO: can we send the error to the user somehow? */
return;
}
}
switch (code) {
case FERROR_XFER:
got_xfer_error = 1;
@@ -315,19 +341,48 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
f = stderr;
break;
case FINFO:
f = am_server ? stderr : stdout;
if (quiet)
return;
break;
/*case FLOG:*/
/*case FCLIENT:*/
/*case FERROR_UTF8:*/
/*case FERROR_SOCKET:*/
default:
fprintf(stderr, "Bad logcode in rwrite(): %d [%s]\n", (int)code, who_am_i());
exit_cleanup(RERR_MESSAGEIO);
}
if (progress_is_active && !am_server) {
fputc('\n', f);
progress_is_active = 0;
if (am_server && msgs2stderr != 1 && (msgs2stderr != 2 || f != stderr)) {
enum msgcode msg = (enum msgcode)code;
if (protocol_version < 30) {
if (msg == MSG_ERROR)
msg = MSG_ERROR_XFER;
else if (msg == MSG_WARNING)
msg = MSG_INFO;
}
/* Pass the message to the non-server side. */
if (send_msg(msg, buf, len, !is_utf8))
return;
if (am_daemon > 0) {
/* TODO: can we send the error to the user somehow? */
return;
}
f = stderr;
}
trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r')
? buf[--len] : 0;
if (output_needs_newline) {
fputc('\n', f);
output_needs_newline = 0;
}
trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r') ? buf[--len] : '\0';
if (len && buf[0] == '\r') {
fputc('\r', f);
buf++;
len--;
}
#ifdef ICONV_CONST
if (ic != (iconv_t)-1) {
@@ -336,27 +391,39 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8)
int ierrno;
INIT_CONST_XBUF(outbuf, convbuf);
INIT_XBUF(inbuf, (char*)buf, len, -1);
INIT_XBUF(inbuf, (char*)buf, len, (size_t)-1);
while (inbuf.len) {
iconvbufs(ic, &inbuf, &outbuf, 0);
iconvbufs(ic, &inbuf, &outbuf, inbuf.pos ? 0 : ICB_INIT);
ierrno = errno;
if (outbuf.len) {
filtered_fwrite(f, convbuf, outbuf.len, 0);
char trailing = inbuf.len ? '\0' : trailing_CR_or_NL;
filtered_fwrite(f, convbuf, outbuf.len, 0, 0, trailing);
if (trailing) {
trailing_CR_or_NL = '\0';
fflush(f);
}
outbuf.len = 0;
}
if (!ierrno || ierrno == E2BIG)
continue;
fprintf(f, "\\#%03o", CVAL(inbuf.buf, inbuf.pos++));
inbuf.len--;
/* Log one byte of illegal/incomplete sequence and continue with
* the next character. Check that the buffer is non-empty for the
* sake of robustness. */
if ((ierrno == EILSEQ || ierrno == EINVAL) && inbuf.len) {
fprintf(f, "\\#%03o", CVAL(inbuf.buf, inbuf.pos++));
inbuf.len--;
}
}
if (trailing_CR_or_NL) {
fputc(trailing_CR_or_NL, f);
fflush(f);
}
} else
#endif
filtered_fwrite(f, buf, len, !allow_8bit_chars);
if (trailing_CR_or_NL) {
fputc(trailing_CR_or_NL, f);
fflush(f);
{
filtered_fwrite(f, buf, len, !allow_8bit_chars, 0, trailing_CR_or_NL);
if (trailing_CR_or_NL)
fflush(f);
}
}
@@ -415,12 +482,17 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...)
char buf[BIGPATHBUFLEN];
size_t len;
strlcpy(buf, RSYNC_NAME ": ", sizeof buf);
len = (sizeof RSYNC_NAME ": ") - 1;
/* 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,
@@ -434,12 +506,12 @@ void rsyserr(enum logcode code, int errcode, const char *format, ...)
void rflush(enum logcode code)
{
FILE *f = NULL;
FILE *f;
if (am_daemon || code == FLOG)
return;
if (code == FINFO && !am_server)
if (!am_server && (code == FINFO || code == FCLIENT))
f = stdout;
else
f = stderr;
@@ -447,13 +519,23 @@ void rflush(enum logcode code)
fflush(f);
}
void remember_initial_stats(void)
{
initial_data_read = total_data_read;
initial_data_written = total_data_written;
}
/* Size of log_formatted()'s per-escape "fmt" scratch buffer. log_format_has()
* must bound its width-digit scan to the same limit so the two parsers agree on
* where an escape letter falls (see the digit loop in each). */
#define LOG_FMT_SIZE 32
/* A generic logging routine for send/recv, with parameter substitiution. */
static void log_formatted(enum logcode code, const char *format, const char *op,
struct file_struct *file, const char *fname,
struct stats *initial_stats, int iflags,
struct file_struct *file, const char *fname, int iflags,
const char *hlink)
{
char buf[MAXPATHLEN+1024], buf2[MAXPATHLEN], fmt[32];
char buf[MAXPATHLEN+1024], buf2[MAXPATHLEN], fmt[LOG_FMT_SIZE];
char *p, *s, *c;
const char *n;
size_t len, total;
@@ -473,30 +555,45 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
buf[total] = '\0';
for (p = buf; (p = strchr(p, '%')) != NULL; ) {
int humanize = 0;
s = p++;
c = fmt + 1;
while (*p == '\'') {
humanize++;
p++;
}
if (*p == '-')
*c++ = *p++;
while (isDigit(p) && c - fmt < (int)(sizeof fmt) - 8)
*c++ = *p++;
while (*p == '\'') {
humanize++;
p++;
}
if (!*p)
break;
*c = '\0';
n = NULL;
/* Note for %h and %a: it doesn't matter what fd we pass to
* client_{name,addr} because rsync_module will already have
* forced the answer to be cached (assuming, of course, for %h
* that lp_reverse_lookup(module_id) is true). */
switch (*p) {
case 'h':
if (am_daemon)
n = client_name(0);
if (am_daemon) {
n = lp_reverse_lookup(module_id)
? client_name(0) : undetermined_hostname;
}
break;
case 'a':
if (am_daemon)
n = client_addr(0);
break;
case 'l':
strlcat(fmt, ".0f", sizeof fmt);
strlcat(fmt, "s", sizeof fmt);
snprintf(buf2, sizeof buf2, fmt,
(double)F_LENGTH(file));
do_big_num(F_LENGTH(file), humanize, NULL));
n = buf2;
break;
case 'U':
@@ -516,9 +613,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
}
break;
case 'p':
strlcat(fmt, "ld", sizeof fmt);
snprintf(buf2, sizeof buf2, fmt,
(long)getpid());
strlcat(fmt, "d", sizeof fmt);
snprintf(buf2, sizeof buf2, fmt, (int)getpid());
n = buf2;
break;
case 'M':
@@ -551,7 +647,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
n = buf2;
} else if (am_daemon && *c != '/') {
pathjoin(buf2, sizeof buf2,
curr_dir + module_dirlen, c);
vfs.curr_dir + module_dirlen, c);
clean_fname(buf2, 0);
if (fmt[1]) {
strlcpy(c, buf2, MAXPATHLEN);
@@ -599,34 +695,39 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
n = timestring(time(NULL));
break;
case 'P':
n = module_dir;
n = full_module_path;
break;
case 'u':
n = auth_user;
break;
case 'b':
if (am_sender) {
b = stats.total_written -
initial_stats->total_written;
} else {
b = stats.total_read -
initial_stats->total_read;
}
strlcat(fmt, ".0f", sizeof fmt);
snprintf(buf2, sizeof buf2, fmt, (double)b);
case 'c':
if (!(iflags & ITEM_TRANSFER))
b = 0;
else if ((!!am_sender) ^ (*p == 'c'))
b = total_data_written - initial_data_written;
else
b = total_data_read - initial_data_read;
strlcat(fmt, "s", sizeof fmt);
snprintf(buf2, sizeof buf2, fmt,
do_big_num(b, humanize, NULL));
n = buf2;
break;
case 'c':
if (!am_sender) {
b = stats.total_written -
initial_stats->total_written;
} else {
b = stats.total_read -
initial_stats->total_read;
case 'C':
n = NULL;
if (S_ISREG(file->mode)) {
if (always_checksum && !(iflags & ITEM_DELETED))
n = sum_as_hex(file_sum_nni->num, F_SUM(file), 1);
else if (iflags & ITEM_TRANSFER)
n = sum_as_hex(xfer_sum_nni->num, sender_file_sum, 0);
}
if (!n) {
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';
n = buf2;
}
strlcat(fmt, ".0f", sizeof fmt);
snprintf(buf2, sizeof buf2, fmt, (double)b);
n = buf2;
break;
case 'i':
if (iflags & ITEM_DELETED) {
@@ -635,14 +736,14 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
}
n = c = buf2 + MAXPATHLEN - 32;
c[0] = iflags & ITEM_LOCAL_CHANGE
? iflags & ITEM_XNAME_FOLLOWS ? 'h' : 'c'
? iflags & ITEM_XNAME_FOLLOWS ? 'h' : 'c'
: !(iflags & ITEM_TRANSFER) ? '.'
: !local_server && *op == 's' ? '<' : '>';
if (S_ISLNK(file->mode)) {
c[1] = 'L';
c[3] = '.';
c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
: !preserve_times || !receiver_symlink_times
: !preserve_mtimes || !receiver_symlink_times
|| (iflags & ITEM_REPORT_TIMEFAIL) ? 'T' : 't';
} else {
c[1] = S_ISDIR(file->mode) ? 'd'
@@ -650,13 +751,15 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
: IS_DEVICE(file->mode) ? 'D' : 'f';
c[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's';
c[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
: !preserve_times ? 'T' : 't';
: !preserve_mtimes ? 'T' : 't';
}
c[2] = !(iflags & ITEM_REPORT_CHANGE) ? '.' : 'c';
c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
c[8] = !(iflags & (ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME)) ? '.'
: BITS_SET(iflags, ITEM_REPORT_ATIME|ITEM_REPORT_CRTIME) ? 'b'
: iflags & ITEM_REPORT_ATIME ? 'u' : 'n';
c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
c[11] = '\0';
@@ -678,6 +781,9 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
}
}
break;
case '%':
n = "%";
break;
}
/* "n" is the string to be inserted in place of this % code. */
@@ -721,19 +827,33 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
int log_format_has(const char *format, char esc)
{
const char *p;
int width;
if (!format)
return 0;
for (p = format; (p = strchr(p, '%')) != NULL; ) {
if (*++p == '-')
for (p++; *p == '\''; p++) {} /*SHARED ITERATOR*/
/* Mirror log_formatted()'s width-digit scan exactly (c starts at
* fmt+1, so width starts at 1): both must stop at the same digit
* or they disagree on where the escape letter is, which for %C
* can leave sender_keeps_checksum unset and over-read F_SUM. */
width = 1;
if (*p == '-') {
p++;
while (isDigit(p))
width++;
}
while (isDigit(p) && width < LOG_FMT_SIZE - 8) {
p++;
width++;
}
while (*p == '\'') p++;
if (!*p)
break;
if (*p == esc)
return 1;
if (*p == '%') /* %% is a literal '%', not the start of an escape */
p++;
}
return 0;
}
@@ -741,67 +861,70 @@ int log_format_has(const char *format, char esc)
/* Log the transfer of a file. If the code is FCLIENT, the output just goes
* to stdout. If it is FLOG, it just goes to the log file. Otherwise we
* output to both. */
void log_item(enum logcode code, struct file_struct *file,
struct stats *initial_stats, int iflags, const char *hlink)
void log_item(enum logcode code, struct file_struct *file, int iflags, const char *hlink)
{
const char *s_or_r = am_sender ? "send" : "recv";
if (code != FLOG && stdout_format && !am_server) {
log_formatted(FCLIENT, stdout_format, s_or_r,
file, NULL, initial_stats, iflags, hlink);
}
if (code != FCLIENT && logfile_format && *logfile_format) {
log_formatted(FLOG, logfile_format, s_or_r,
file, NULL, initial_stats, iflags, hlink);
}
if (code != FLOG && stdout_format && !am_server)
log_formatted(FCLIENT, stdout_format, s_or_r, file, NULL, iflags, hlink);
if (code != FCLIENT && logfile_format && *logfile_format)
log_formatted(FLOG, logfile_format, s_or_r, file, NULL, iflags, hlink);
}
void maybe_log_item(struct file_struct *file, int iflags, int itemizing,
const char *buf)
void maybe_log_item(struct file_struct *file, int iflags, int itemizing, const char *buf)
{
int significant_flags = iflags & SIGNIFICANT_ITEM_FLAGS;
int see_item = itemizing && (significant_flags || *buf
|| stdout_format_has_i > 1 || (verbose > 1 && stdout_format_has_i));
|| stdout_format_has_i > 1 || (INFO_GTE(NAME, 2) && stdout_format_has_i));
int local_change = iflags & ITEM_LOCAL_CHANGE && significant_flags;
if (am_server) {
if (logfile_name && !dry_run && see_item
&& (significant_flags || logfile_format_has_i))
log_item(FLOG, file, &stats, iflags, buf);
log_item(FLOG, file, iflags, buf);
} else if (see_item || local_change || *buf
|| (S_ISDIR(file->mode) && significant_flags)) {
enum logcode code = significant_flags || logfile_format_has_i ? FINFO : FCLIENT;
log_item(code, file, &stats, iflags, buf);
log_item(code, file, iflags, buf);
}
}
void log_delete(const char *fname, int mode)
{
static struct {
union file_extras ex[4]; /* just in case... */
struct file_struct file;
} x;
static struct file_struct *file = NULL;
int len = strlen(fname);
const char *fmt;
x.file.mode = mode;
if (!file) {
int extra_len = (file_extra_cnt + 2) * EXTRA_LEN;
char *bp;
#if EXTRA_ROUNDING > 0
if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
#endif
if (!verbose && !stdout_format)
;
else if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
bp = new_array0(char, FILE_STRUCT_LEN + extra_len + 1);
bp += extra_len;
file = (struct file_struct *)bp;
}
file->mode = mode;
if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
if (S_ISDIR(mode))
len++; /* directories include trailing null */
send_msg(MSG_DELETED, fname, len, am_generator);
} else {
} else if (!INFO_GTE(DEL, 1) && !stdout_format)
;
else {
fmt = stdout_format_has_o_or_i ? stdout_format : "deleting %n";
log_formatted(FCLIENT, fmt, "del.", &x.file, fname, &stats,
ITEM_DELETED, NULL);
log_formatted(FCLIENT, fmt, "del.", file, fname, ITEM_DELETED, NULL);
}
if (!logfile_name || dry_run || !logfile_format)
return;
fmt = logfile_format_has_o_or_i ? logfile_format : "deleting %n";
log_formatted(FLOG, fmt, "del.", &x.file, fname, &stats, ITEM_DELETED, NULL);
log_formatted(FLOG, fmt, "del.", file, fname, ITEM_DELETED, NULL);
}
/*
@@ -812,12 +935,15 @@ void log_delete(const char *fname, int mode)
*/
void log_exit(int code, const char *file, int line)
{
if (code == 0) {
rprintf(FLOG,"sent %.0f bytes received %.0f bytes total size %.0f\n",
(double)stats.total_written,
(double)stats.total_read,
(double)stats.total_size);
} else if (am_server != 2) {
/* The receiving side's stats are split between 2 procs until the
* end of the run, so only the sender can output non-final info. */
if (code == 0 || am_sender) {
rprintf(FLOG,"sent %s bytes received %s bytes total size %s\n",
big_num(stats.total_written),
big_num(stats.total_read),
big_num(stats.total_size));
}
if (code != 0 && am_server != 2) {
const char *name;
name = rerr_name(code);
@@ -827,10 +953,10 @@ void log_exit(int code, const char *file, int line)
/* VANISHED is not an error, only a warning */
if (code == RERR_VANISHED) {
rprintf(FWARNING, "rsync warning: %s (code %d) at %s(%d) [%s=%s]\n",
name, code, file, line, who_am_i(), RSYNC_VERSION);
name, code, src_file(file), line, who_am_i(), rsync_version());
} else {
rprintf(FERROR, "rsync error: %s (code %d) at %s(%d) [%s=%s]\n",
name, code, file, line, who_am_i(), RSYNC_VERSION);
name, code, src_file(file), line, who_am_i(), rsync_version());
}
}
}
+21
View File
@@ -0,0 +1,21 @@
dnl AC_HAVE_TYPE(TYPE,INCLUDES)
AC_DEFUN([AC_HAVE_TYPE], [
cv=`echo "$1" | sed 'y%./+- %__p__%'`
AC_MSG_CHECKING(for $1)
AC_CACHE_VAL([ac_cv_type_$cv],
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
AC_INCLUDES_DEFAULT
$2]],
[[$1 foo;]])],
[eval "ac_cv_type_$cv=yes"],
[eval "ac_cv_type_$cv=no"]))dnl
ac_foo=`eval echo \\$ac_cv_type_$cv`
AC_MSG_RESULT($ac_foo)
if test "$ac_foo" = yes; then
ac_tr_hdr=HAVE_`echo $1 | sed 'y%abcdefghijklmnopqrstuvwxyz./- %ABCDEFGHIJKLMNOPQRSTUVWXYZ____%'`
if false; then
AC_CHECK_TYPES($1)
fi
AC_DEFINE_UNQUOTED($ac_tr_hdr, 1, [Define if you have type `$1'])
fi
])
+27
View File
@@ -0,0 +1,27 @@
AC_DEFUN([AC_HEADER_MAJOR_FIXED],
[AC_CACHE_CHECK(whether sys/types.h defines makedev,
ac_cv_header_sys_types_h_makedev,
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[@%:@include <sys/types.h>]],
[[return makedev(0, 0);]])],
[if grep sys/sysmacros.h conftest.err >/dev/null; then
ac_cv_header_sys_types_h_makedev=no
else
ac_cv_header_sys_types_h_makedev=yes
fi],
[ac_cv_header_sys_types_h_makedev=no])
])
if test $ac_cv_header_sys_types_h_makedev = no; then
AC_CHECK_HEADER(sys/mkdev.h,
[AC_DEFINE(MAJOR_IN_MKDEV, 1,
[Define to 1 if `major', `minor', and `makedev' are
declared in <mkdev.h>.])])
if test $ac_cv_header_sys_mkdev_h = no; then
AC_CHECK_HEADER(sys/sysmacros.h,
[AC_DEFINE(MAJOR_IN_SYSMACROS, 1,
[Define to 1 if `major', `minor', and `makedev'
are declared in <sysmacros.h>.])])
fi
fi
])
Loaded 100 of 755 files, more files were not shown because too many files have changed in this diff. Show more