Files
rsync/testsuite/safe-links_test.py
Andrew Tridgell 34f40b9ea7 testsuite: tighten metadata-precision and symlink-target assertions
Replace loose/partial oracles with exact ones:

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

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 07:43:00 +10:00

52 lines
1.5 KiB
Python

#!/usr/bin/env python3
# Python rewrite of testsuite/safe-links.test.
#
# --safe-links must drop symlinks whose target escapes the transfer root.
# In-tree symlinks survive; escape-attempt symlinks (../../..., a/a/../../...)
# must NOT appear at the destination at all.
import os
from rsyncfns import TMPDIR, is_a_link, run_rsync, test_fail
def assert_symlink(path, target):
if not is_a_link(path):
test_fail(f"File {path} is not a symlink")
actual = os.readlink(path)
if actual != target:
test_fail(f"symlink {path} target is {actual!r}, expected {target!r}")
def assert_notexist(path):
if os.path.exists(path):
test_fail(f"File {path} exists")
if os.path.islink(path):
test_fail(f"File {path} exists as a symlink")
os.chdir(TMPDIR)
os.mkdir("from")
os.mkdir("from/safe")
os.mkdir("from/unsafe")
os.mkdir("from/safe/files")
os.mkdir("from/safe/links")
open("from/safe/files/file1", "w").close()
open("from/safe/files/file2", "w").close()
open("from/unsafe/unsafefile", "w").close()
os.symlink("../files/file1", "from/safe/links/file1")
os.symlink("../files/file2", "from/safe/links/file2")
os.symlink("../../unsafe/unsafefile", "from/safe/links/unsafefile")
os.symlink("a/a/a/../../../unsafe2", "from/safe/links/unsafe2")
print("rsync with relative path and --safe-links")
run_rsync('-avv', '--safe-links', 'from/safe/', 'to')
assert_symlink("to/links/file1", "../files/file1")
assert_symlink("to/links/file2", "../files/file2")
assert_notexist("to/links/unsafefile")
assert_notexist("to/links/unsafe2")