Deletion data-loss and semantics: files-from over-delete, delete-excluded, symlinks, max-delete #290

Closed
opened 2026-09-15 19:33:34 +02:00 by TapTap · 1 comment
Owner

Audit findings (src/shared/file_receive.c, src/shared/utils.c, src/client/client_send.c). Several are data-loss capable.

  1. --delete + --files-from: the keep-set manifest contains only sent paths and the walk covers the whole receive root, so untransmitted paths are treated as extras and deleted; rsync restricts deletion to synchronized directories. Verified: FastSync deleted unlisted.txt/other/c.txt; rsync deleted only the in-scope extra.
  2. --delete-excluded empties one protected list that also carries --max-size/--min-size prunes, so size-pruned files get deleted; rsync keeps them.
  3. --delete never removes extraneous destination symlinks (count_extras_fd/delete_extras_fd skip S_ISLNK); rsync unlinks them.
  4. --max-delete=N: FastSync is all-or-nothing (exceeds N -> delete nothing, exit 1) and rejects -1; rsync deletes up to N, skips the rest, exits 25, and accepts -1. The doc's "rsync all-or-nothing semantics" claim is false.
  5. --delete-missing-args deletions bypass the --max-delete budget.
  6. --force is inert under --delay-updates.

Fix per item; add regression tests for (1)-(3) especially.

Audit findings (`src/shared/file_receive.c`, `src/shared/utils.c`, `src/client/client_send.c`). Several are data-loss capable. 1) `--delete` + `--files-from`: the keep-set manifest contains only sent paths and the walk covers the whole receive root, so untransmitted paths are treated as extras and deleted; rsync restricts deletion to synchronized directories. Verified: FastSync deleted `unlisted.txt`/`other/c.txt`; rsync deleted only the in-scope extra. 2) `--delete-excluded` empties one protected list that also carries `--max-size`/`--min-size` prunes, so size-pruned files get deleted; rsync keeps them. 3) `--delete` never removes extraneous destination symlinks (`count_extras_fd`/`delete_extras_fd` skip `S_ISLNK`); rsync unlinks them. 4) `--max-delete=N`: FastSync is all-or-nothing (exceeds N -> delete nothing, exit 1) and rejects `-1`; rsync deletes up to N, skips the rest, exits 25, and accepts `-1`. The doc's "rsync all-or-nothing semantics" claim is false. 5) `--delete-missing-args` deletions bypass the `--max-delete` budget. 6) `--force` is inert under `--delay-updates`. Fix per item; add regression tests for (1)-(3) especially.
TapTap added the needs-triage label 2026-09-15 19:33:34 +02:00
Author
Owner

Fixed (all 6). (1) --delete + --files-from deletion is scoped to synchronized directories; (2) --delete-excluded no longer empties the size-prune protected list; (3) extraneous destination symlinks are removed; (4) --max-delete=N deletes up to N, skips the rest and exits 25, and accepts -1; (5) --delete-missing-args draws from the same budget; (6) --force is honored under --delay-updates. Regression tests added. Closing as completed.

Fixed (all 6). (1) `--delete` + `--files-from` deletion is scoped to synchronized directories; (2) `--delete-excluded` no longer empties the size-prune protected list; (3) extraneous destination symlinks are removed; (4) `--max-delete=N` deletes up to N, skips the rest and exits 25, and accepts `-1`; (5) `--delete-missing-args` draws from the same budget; (6) `--force` is honored under `--delay-updates`. Regression tests added. Closing as completed.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: TapTap/FastSync#290