Files
AWG2_harness/traffic_core
founder 516e6209fc fix(swap): make every swap transition reversible [P0]
The previous commit moved the slow part of a resize out of the swapless window but left
the window itself unrecoverable. Both remaining transitions could take a host from a
working state to no swap at all on a single failure.

1. Live resize destroyed the old file before the new one was active.

   The sequence ended: swapoff old, remove old, swapoff staging, rename staging into
   place, and only later swapon. A failure at the rename, at the ownership write, or at
   the activation left the old file already deleted and the staging file inactive - no
   swap, and nothing on disk to go back to. On a host given swap because memory is tight,
   this is the worst possible outcome of a routine size change.

   The old file is now moved aside as <path>.awg2-harness.previous instead of deleted, and
   is only removed once the replacement is confirmed present in the active swap list.
   Every step past that point rolls back: the staging file is discarded, the previous file
   is renamed back, ownership is rewritten and the original priority reactivated. The
   returned error carries both the original cause and the outcome of the rollback, so the
   operator can see whether the host still has swap.

   A related failure mode is closed with it: rename succeeding but the ownership write
   failing left a file with a new inode and a stale record, which the next run correctly
   classified as StaleOwnership and refused to touch. Ownership is written inside the
   rolled-back section, so that state cannot persist.

2. A priority change could leave the host swapless.

   swapoff followed by swapon is two steps and the second can fail. Changing one integer
   in the config had no recovery path. change_swap_priority now reactivates at the previous
   priority if the new one is rejected, and says explicitly in the error whether the
   restore worked.

3. A partially created swapfile wedged the module.

   In the create-from-nothing branch, a failure after the file existed but before ownership
   was recorded left an unowned regular file at the managed path. The next run classified
   it as Foreign - correctly - and refused to proceed, so a transient mkswap failure
   required manual intervention. The file is created by this run inside an allowed
   root-owned location, so it is now removed on failure, leaving the host as it was found.

Activation moved out of ensure_swap into the reconciliation branches. That is what makes
the above possible: each branch owns both the old and the new file for the whole of its
transition, so it can undo it. A transition also refuses to report success while the
expected swap is not in the active list.

Also in this commit, from the same audit: the legacy ownership sidecar was removed on the
basis of being a regular file with the right name, without checking its contents, so a
file an operator happened to create at that path would be deleted. It now goes through the
same marker check as every other managed file - foreign files are left alone with a
warning.

Two tests added: the suffixed path helpers, and that the sidecar is removed only when it
carries the old marker.

cargo test --workspace: 180 + 4 passed. clippy -D warnings clean.
2026-08-07 04:41:46 +05:00
..