Repository navigation
Keep init's key once publication was attempted, and share one durable write - #27
Merged
Merged
Conversation
… write
git-e2ee init deleted the key it generated whenever a later step failed,
including after the genesis compare-and-swap had published. A failure in the
floor step then left initialized storage whose only owner key was gone.
initialize now returns InitializeError::{BeforePublication,
PublicationUncertain, AfterPublication}, decided by where the error arose,
and init deletes the generated key only before publication. Otherwise it
keeps the key and says where it is and whether storage is or may be
initialized. A carrier branch that is already occupied is reported as a
typed StorageOccupied and counts as before publication.
The "write temporary, flush, rename, sync directory" sequence was written by
hand in several places with different cleanup. persist::replace_file_durably
now owns it: an exclusive temporary in the destination directory, file sync,
the durability checkpoints, rename and directory sync, and removal of the
temporary on every failure before the rename. The continuity floor, checkout
progress, rejected-heads evidence, filesystem HEAD, FORMAT, README and the
carrier cache pointer use it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 3 of the refactoring plan (Opus 1.4; Astra Category 1 medium and Category 2 low).
A.
initkeeps its key once publication was attemptedBug.
git-e2ee initdeleted the key it generated whenever a later step failed. That included failures after the genesis compare-and-swap had already published. When the floor step failed after publication, the storage was left initialized with its only owner key deleted. Reproduced onmain: the new test fails there with the key deleted, after an "Is a directory" error from the floor step.Fix.
EncryptedRepository::initializereturnsInitializeError. The variant is decided by where the error came from, not by its text:BeforePublicationprepare_genesisfailed: floor lock, already-initialized checks, policy genesis, sealing and storing the manifestPublicationUncertainAfterPublication { id }When the key is kept,
initfails with "kept key ; it may be the only owner key for ". The cause says either that storage is initialized, or that it may already be.A carrier branch that already exists (
refuse_foreign_branch) is now a typed, backend-neutralStorageOccupiederror. It is refused before any push, so it counts asBeforePublication. ACasConflicton genesis still keeps the key; that choice is conservative.Tests in
tests/cli_targets.rs:failed_init_removes_the_key_it_createdstill passes.init_keeps_its_key_when_the_floor_fails_after_publication(unix). A carrierpost-receivehook breaks the floor after the push lands. The key remains, stderr names it, andverify --keyreports generation 0. This test fails onmain.init_keeps_its_key_when_genesis_publication_reports_an_errorinit_refused_by_an_occupied_carrier_branch_removes_its_keyB. One durable-replacement primitive
persist::replace_file_durably(directory, name, contents) -> Result<(), ReplaceError>owns the whole sequence:.{name}.{hex}.tmpin the destination directory;ReplaceError::NotPublishedmeans the destination is untouched.ReplaceError::Unacknowledgedmeans the rename ran but the directory sync failed. A name with a path separator is rejected.Ported:
continuity::write_json(this covers the floor, checkout progress and rejected-heads evidence), filesystemHEAD,FORMAT,README.mdand the carrier cachecurrentpointer. The pointer also gains the directory sync it lacked.write_filesystem_head_lockedandwrite_file_durableare deleted.Left unchanged, because they have a different shape:
KeyFile::write_new, which creates the final file in place.Tests:
replace_publishes_new_bytes_and_leaves_no_temporary, which also checks the stage orderfailed_rename_removes_the_temporary_and_keeps_the_destination. A mutation check confirmed it: removing the cleanup makes this test fail.replace_into_a_missing_directory_creates_nothingreplace_rejects_a_name_with_a_separatorA write failure is not tested; I found no portable way to inject one.
Verification
cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo test --all-targets(175 passed).~/.local/state/git-e2eeand~/.config/git-e2eedid not exist before or after the suite.