fix: make edit.WriteFile atomic and mode-preserving #22

Merged
fiatcode merged 1 commit from fix/writefile-preserve-mode-atomic into main 2026-06-18 10:21:04 +07:00
Owner

edit.WriteFile did an in-place os.WriteFile, which can truncate a file if the process crashes mid-write. Now that linkify writes to files other than the page being edited, that risk applies to arbitrary source files in the graph.

Change

  • Write to a temp file in the destination directory, chmod it to the target's mode (the existing file's perms, or 0o644 for a new file — os.CreateTemp is born 0o600), then os.Rename into place. Rename is atomic against a process crash, so a botched write leaves the original intact rather than truncated.
  • A deferred os.Remove cleans up any stranded temp on failure.
  • internal/edit remains the sole disk-writing surface; both the editor save path and linkify go through here.

Notes

  • The pre-existing os.WriteFile did not actually reset an existing file's mode (the mode arg only applies on creation) — so the real defect was atomicity. Mode preservation is a new requirement the temp+rename fix introduces, since the temp is born 0o600.
  • No fsync: rename guarantees atomicity against a process crash (the failure this protects against); durability against power loss is out of scope for a local notes TUI. .weft-*.tmp temps carry a .tmp extension so the graph scan (.md-only) never indexes them.

Tests

  • Rewrote TestWriteFile in arrange/act/assert form with shared helpers.
  • Mode-preservation test uses 0o640 (distinct from both the 0o644 default and the 0o600 temp birth mode) so it genuinely guards the chmod step.
  • Added a failure-path test: a write into a read-only dir errors, the original survives, and no temp is stranded.

Closes the edit.WriteFile perms/atomicity TODO.

`edit.WriteFile` did an in-place `os.WriteFile`, which can truncate a file if the process crashes mid-write. Now that linkify writes to files *other* than the page being edited, that risk applies to arbitrary source files in the graph. ## Change - Write to a temp file in the destination directory, `chmod` it to the target's mode (the existing file's perms, or `0o644` for a new file — `os.CreateTemp` is born `0o600`), then `os.Rename` into place. Rename is atomic against a process crash, so a botched write leaves the original intact rather than truncated. - A deferred `os.Remove` cleans up any stranded temp on failure. - `internal/edit` remains the sole disk-writing surface; both the editor save path and linkify go through here. ## Notes - The pre-existing `os.WriteFile` did *not* actually reset an existing file's mode (the mode arg only applies on creation) — so the real defect was atomicity. Mode preservation is a *new* requirement the temp+rename fix introduces, since the temp is born `0o600`. - No `fsync`: rename guarantees atomicity against a process crash (the failure this protects against); durability against power loss is out of scope for a local notes TUI. `.weft-*.tmp` temps carry a `.tmp` extension so the graph scan (`.md`-only) never indexes them. ## Tests - Rewrote `TestWriteFile` in arrange/act/assert form with shared helpers. - Mode-preservation test uses `0o640` (distinct from both the `0o644` default and the `0o600` temp birth mode) so it genuinely guards the chmod step. - Added a failure-path test: a write into a read-only dir errors, the original survives, and no temp is stranded. Closes the `edit.WriteFile` perms/atomicity TODO.
fix: make edit.WriteFile atomic and mode-preserving
All checks were successful
CI / vet-and-test (pull_request) Successful in 29s
b47944a39f
WriteFile did an in-place os.WriteFile, which can truncate a file if the
process crashes mid-write. Now that linkify writes to files other than the
page being edited, that risk applies to arbitrary source files.

Write to a temp file in the destination dir, chmod it to the target's mode
(the existing file's perms, or 0o644 for a new file, since CreateTemp is
born 0o600), then os.Rename into place — atomic against a process crash. A
deferred Remove cleans up any stranded temp on failure.

Rewrites the WriteFile tests in arrange/act/assert form with shared helpers
and adds a failure-path test asserting the original survives and no temp is
stranded when a write fails.
fiatcode deleted branch fix/writefile-preserve-mode-atomic 2026-06-18 10:21:05 +07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
fiatcode/weft!22
No description provided.