mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-06-08 06:05:57 -04:00
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>
55 lines
2.2 KiB
Python
55 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Coverage of -R implied directories and --no-implied-dirs at depth.
|
|
|
|
With -R the directories implied by a source path are recreated at the
|
|
destination AND have their attributes mirrored from the source. With
|
|
--no-implied-dirs those implied directories are still created as needed but get
|
|
default attributes instead of the source's. Verify the distinction on an
|
|
implied directory carrying a non-default mode, several levels deep.
|
|
"""
|
|
|
|
import os
|
|
|
|
from rsyncfns import (
|
|
SCRATCHDIR, TODIR,
|
|
assert_mode, assert_same, forced_protocol, makepath, rmtree, run_rsync,
|
|
)
|
|
|
|
os.umask(0o022) # make the default implied-dir mode deterministic (0o755)
|
|
|
|
base = SCRATCHDIR / 'rbase'
|
|
rmtree(base)
|
|
rmtree(TODIR)
|
|
makepath(base / 'a' / 'b' / 'c')
|
|
os.chmod(base / 'a' / 'b', 0o750) # distinctive mode on an implied dir
|
|
(base / 'a' / 'b' / 'c' / 'file').write_text("data\n")
|
|
|
|
os.chdir(base / 'a')
|
|
|
|
# -R: implied dirs b and c are recreated with the source's attributes.
|
|
run_rsync('-aR', 'b/c/file', f'{TODIR}/')
|
|
assert_mode(TODIR / 'b', 0o750, label='-R mirrors implied-dir mode')
|
|
assert_same(TODIR / 'b' / 'c' / 'file', base / 'a' / 'b' / 'c' / 'file',
|
|
label='-R deep file')
|
|
|
|
# --no-implied-dirs: implied dir b is created with default (not source) attrs.
|
|
# At protocol 29 the generator rejects a multi-component path that has no
|
|
# transmitted directory entries ("invalid path from sender"), so this half is
|
|
# protocol-30+.
|
|
proto = forced_protocol()
|
|
if proto is not None and proto < 30:
|
|
print(f"relative-implied: protocol {proto} -- skipping --no-implied-dirs "
|
|
"(the multi-component path is rejected by the proto-29 generator)")
|
|
else:
|
|
rmtree(TODIR)
|
|
run_rsync('-aR', '--no-implied-dirs', 'b/c/file', f'{TODIR}/')
|
|
# The implied dir must get the default mode (0o777 & ~umask == 0o755),
|
|
# not the source's distinctive 0750 -- assert the exact mode, not merely
|
|
# "different from 0750".
|
|
assert_mode(TODIR / 'b', 0o755,
|
|
label='--no-implied-dirs uses the default mode, not source 0750')
|
|
assert_same(TODIR / 'b' / 'c' / 'file', base / 'a' / 'b' / 'c' / 'file',
|
|
label='--no-implied-dirs deep file')
|
|
|
|
print("relative-implied: -R mirrors implied-dir attrs; --no-implied-dirs does not")
|