516e6209fc
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.