mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-09-12 21:28:25 -04:00
--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.