Protect file writes against power-loss corruption#348
Open
voidptr wants to merge 2 commits into
Open
Conversation
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.
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_settingsreads 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_fileproc. This PR rewrites that proc to use an atomic write pattern:<filename>.tmp<filename>.bak(original stays in place).tmpover the target (atomic on POSIX/Android)If power is lost at any step, the original file remains intact. One
.bakgeneration is always retained.Additionally,
load_settingsnow detects corruption (empty file or malformed content) and automatically recovers from the.bakfile. 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.tclloading is wrapped incatchso a corrupt translation file degrades to English-only instead of crashing the app.Changes
safe_write.tclfast_write_open+write_filewith safe write-to-tmp → backup → rename patternsafe_load.tclload_settings_recoverproc: corruption detection +.bakrecoveryupdater.tclsafe_write.tcl(procs extracted for testability)utils.tclsafe_load.tcl, callsload_settings_recovermachine.tcltranslation.tclloading wrapped incatchScope
write_filebenefit automatically — no per-caller changes neededwrite_filesignature is unchangedappend_fileis unchanged (appends don't truncate, so risk is only a partial trailing entry).tdbformat is unchangedTesting
The second commit adds a test suite (
tests/test_safe_write.tcl) using Tcl's standardtcltestpackage. 13 test cases covering normal writes, backupcreation, 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.