655159ceac
Cap readRange's allocation and reject size_t overflow instead of truncating; re-append .rsbank when the export picker omits it; add cafe coverage for writeFileExclusive and writeLandedFile; loop write() on EINTR.
60 lines
2.8 KiB
C++
60 lines
2.8 KiB
C++
// 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 <string>
|
|
#include <vector>
|
|
|
|
#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<std::string>& landedPaths() const { return paths_; }
|
|
bool empty() const { return paths_.empty(); }
|
|
|
|
private:
|
|
std::vector<std::string> paths_;
|
|
bool indexCommitted_ = false;
|
|
};
|
|
|
|
} // namespace reasampler
|