// shell/package/package_rollback — the files ONE import call has landed, as a // journal: writes record themselves on success, and rollback() deletes exactly what // is recorded. The deletion carve-out this satisfies, and the half of it the caller // still owns, are at package_rollback.cpp's header. #pragma once #include #include #include "shell/package/package_io.h" namespace reasampler { struct RollbackResult { int deletedCount = 0; int alreadyAbsentCount = 0; // vanished between land and rollback — not a failure int failedCount = 0; // locked / permission — recorded, never thrown bool refused = false; // markIndexCommitted() ran: nothing was deleted }; // Destroying an armed (uncommitted, un-rolled-back) journal is NOT an implicit // rollback — the caller must call rollback() itself on the failure path it wants // to undo. That's the fail-safe direction: a journal dropped by an unrelated early // return leaves the landed files in place rather than silently deleting them. class LandedFileJournal { public: // Lands one payload at destPath through the exclusive create (which refuses an // occupied path outright — a bank-folder file is never overwritten, and collision // handling is the import plan's job upstream) and records it on success. An empty // payload is refused, per writeFileExclusive. Relative paths are resolved against // the process CWD before the write, so the journal's record is always absolute // and a later CWD change cannot re-aim the delete. Refused once // markIndexCommitted() has run. bool writeLandedFile(const std::string& destPath, const PayloadBuffer& payload); // Disarms the journal: the index mutation these files back is committed, so they // are now referenced bytes and the carve-out no longer covers them. This is the // half of prune's discriminator the journal cannot make structural on its own — // the import verb MUST call this only AFTER the index write has returned success. // Calling it before, then having that write fail, strands the landed files with // no index entry and a journal that now refuses to roll them back. void markIndexCommitted() { indexCommitted_ = true; } bool indexCommitted() const { return indexCommitted_; } // Deletes exactly the recorded files and clears the journal, so a second call is // a no-op. Hard unlink, not trash: nothing ever referenced these bytes. Refuses // (deleting nothing, keeping the record) once markIndexCommitted() has run. RollbackResult rollback(); const std::vector& landedPaths() const { return paths_; } bool empty() const { return paths_.empty(); } private: std::vector paths_; bool indexCommitted_ = false; }; } // namespace reasampler