Skip to content

Protect file writes against power-loss corruption#348

Open
voidptr wants to merge 2 commits into
decentespresso:mainfrom
voidptr:feature/safe-write-recovery
Open

Protect file writes against power-loss corruption#348
voidptr wants to merge 2 commits into
decentespresso:mainfrom
voidptr:feature/safe-write-recovery

Conversation

@voidptr
Copy link
Copy Markdown

@voidptr voidptr commented May 5, 2026

Protect file writes against power-loss corruption

Problem

The app writes settings (and profiles, shots, etc.) by truncating the file and writing new content directly. If power is lost mid-write, the file is left empty or partially written. On next boot, load_settings reads the corrupt file and either crashes or silently loses all user settings.

This is a known issue on battery-free tablets that lose power unexpectedly.

Solution

All file writes already funnel through a single write_file proc. This PR rewrites that proc to use an atomic write pattern:

  1. Write data to <filename>.tmp
  2. Copy existing file to <filename>.bak (original stays in place)
  3. Rename .tmp over the target (atomic on POSIX/Android)

If power is lost at any step, the original file remains intact. One .bak generation is always retained.

Additionally, load_settings now detects corruption (empty file or malformed content) and automatically recovers from the .bak file. If both are bad, it falls through to the existing fresh-defaults path (same as a clean install). A toast notifies the user when recovery occurs.

A minor hardening: translation.tcl loading is wrapped in catch so a corrupt translation file degrades to English-only instead of crashing the app.

Changes

File Change
safe_write.tcl NEW — fast_write_open + write_file with safe write-to-tmp → backup → rename pattern
safe_load.tcl NEW — load_settings_recover proc: corruption detection + .bak recovery
updater.tcl Sources safe_write.tcl (procs extracted for testability)
utils.tcl Sources safe_load.tcl, calls load_settings_recover
machine.tcl translation.tcl loading wrapped in catch

Scope

  • All callers of write_file benefit automatically — no per-caller changes needed
  • write_file signature is unchanged
  • append_file is unchanged (appends don't truncate, so risk is only a partial trailing entry)
  • Clean-install path (file doesn't exist) is unchanged
  • .tdb format is unchanged

Testing

The second commit adds a test suite (tests/test_safe_write.tcl) using Tcl's standard
tcltest package. 13 test cases covering normal writes, backup
creation, failure recovery, corruption detection, and the translation catch wrapper.
This commit is optional and can be dropped if you'd prefer not to introduce a test directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant