Skip to main content

Module atomic

Module atomic 

Source
Expand description

Replacing a file without a window in which it is half-written.

Two files in this tool decide whether encryption happens at all: the managed section of .gitattributes, which carries * filter=git-xcrypt, and .git/config, which carries the driver registration. fs::write truncates first and writes second, so a failure between the two — a full disk, a crash, a power loss — leaves whichever file it hit short or empty. Git then sees no filter at all and treats every path as plain: git add on a secret succeeds with exit code 0 and stores the plaintext, with no signal to the user. Truncating .gitattributes also loses whatever the user wrote outside our markers, which the section-editing code promises to preserve.

Writing a sibling file and renaming it over the target closes that window: rename replaces the entry in one step on every platform this tool targets (MoveFileEx with MOVEFILE_REPLACE_EXISTING on Windows), so a reader sees either the old file or the new one.

The key file goes through write_owner_only rather than [write]: it has the same half-written failure — a truncated key file is a repository nobody can decrypt again — but the opposite permission rule, since inheriting a loose mode from whatever was there before is exactly what a key must not do.

The temporary file is created with O_EXCL and an unguessable name, which is not tidiness. Without O_EXCL the name is merely a name: anyone able to write the destination directory could pre-create it as a symlink, and File::create would follow the link, write the master key wherever it points and then rename the link over the destination. export-key ~/keys/repo.key is a private directory, but export-key /tmp/repo.key is not, and the command whose whole job is to hand over a key is the wrong place to rely on the user picking a safe directory.

Functions§

strip_temporary_suffix
The target a temporary file was named after, if name is one of ours.
target_may_have_been_shortened
Whether the target strip_temporary_suffix reconstructed may be cut.
write
Writes contents to path, replacing it in one step.
write_owner_only
Writes contents to path with owner-only permissions, in one step.