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
nameis one of ours. - target_
may_ have_ been_ shortened - Whether the target
strip_temporary_suffixreconstructed may be cut. - write
- Writes
contentstopath, replacing it in one step. - write_
owner_ only - Writes
contentstopathwith owner-only permissions, in one step.