Fix the package fs seam: UTF-8 paths, GetUserFileName pickers, exclusive-create landing, rollback arm/disarm
Both pickers now ride GetUserFileName (mode 0/1); the "no save picker" premise was false. Landing uses O_EXCL so the create is the existence check, not a TOCTOU pair.
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
// 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 — a path this import did not write is structurally impossible to hand
|
||||
// it. The deletion carve-out this satisfies is cited at package_rollback.cpp's
|
||||
// header.
|
||||
// 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
|
||||
|
||||
@@ -17,22 +16,30 @@ 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
|
||||
};
|
||||
|
||||
// The evidence for the rollback discriminator: only paths this journal's own
|
||||
// writeLandedFile successfully created are recorded, so rollback() can never touch a
|
||||
// byte this import did not write.
|
||||
class LandedFileJournal {
|
||||
public:
|
||||
// Lands one payload at absPath through the atomic temp+rename writer and records
|
||||
// the path on success. REFUSES an existing destination — a bank-folder file is
|
||||
// never overwritten; collision handling is the import plan's job, upstream. An
|
||||
// empty payload is refused too: it signals an upstream read failure, never a
|
||||
// real entry.
|
||||
bool writeLandedFile(const std::string& absPath, const PayloadBuffer& payload);
|
||||
// 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 it at the moment the index is committed.
|
||||
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.
|
||||
// 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_; }
|
||||
@@ -40,6 +47,7 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<std::string> paths_;
|
||||
bool indexCommitted_ = false;
|
||||
};
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
Reference in New Issue
Block a user