## Summary * **What is the goal of this PR?** Fixes #2275. A book could get stuck reopening on an old page, with progress no longer saving and neither "Delete Book Cache" nor "Clear Reading Cache" able to fix it. Root cause: `progress.bin` was written truncate-in-place, so an interrupted write (power loss, or a crash mid-SPI during sleep) left it with a broken FAT cluster chain that the firmware could neither rewrite nor delete — recovery required `fsck`/manual deletion on a host PC. Confirmed in the SDK: `SDCardManager::openFileForWrite` opens with `O_RDWR | O_CREAT | O_TRUNC`, so the canonical file is zeroed before the few progress bytes are rewritten — exactly the window that corrupts the FAT chain. * **What changes are included?** * New shared helper `ProgressFile::writeAtomic()` (`src/activities/reader/ProgressFile.h`): writes progress to `progress.bin.tmp`, flushes and closes it, then `remove`s the old `progress.bin` and `rename`s the temp into place. An interrupted write now only ever damages the throwaway temp; the canonical file is never torn. * All three readers route their progress saves through the helper: EPUB (`EpubReaderUtils.h`), `TxtReaderActivity`, `XtcReaderActivity` — they all shared the identical vulnerable pattern. * Minor: `EpubReaderUtils::saveProgress` now takes `const Epub&` (clears a cppcheck `constParameterReference` finding). ## Additional Context * **Crash-safe, not metadata-atomic.** On FAT the replace is `remove` + `rename` (two directory ops; SdFat's `rename` won't overwrite, hence remove-first). A crash between them leaves *neither* file, which reads as "no saved progress" on next launch — a harmless reset to an old page, never a corrupt/unclearable file. The guarantee is that `progress.bin` is never half-written. * **Prevents, does not repair.** This stops new corruption on healthy cards. It cannot fix an already-corrupted `progress.bin` (removing it may itself fail at the FAT level) — those still need `fsck`/manual deletion, as in the issue's workaround. * **Known follow-up (out of scope here):** a crash *while writing the temp* can leave an orphan `progress.bin.tmp`. It's harmless and self-healing (the next save overwrites it, and it never blocks reading progress), but a boot-time orphan-`.tmp` cleanup would be a tidy follow-up. * **Focus areas for review:** the close-before-rename ordering in `ProgressFile.h` and the remove-before-rename rationale. ## Verification * `./bin/clang-format-fix` — clean * `pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high` — no defects * `pio run` — SUCCESS (RAM 30.9%, Flash 78.8%; footprint essentially unchanged) * Tested on a **Xteink X4** device: open book, turn pages, sleep/exit, reopen — progress now restores to the navigated page across all three readers (EPUB / TXT / XTC). --- ### AI Usage Did you use AI tools to help write this code? _**YES**_ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ff1951c715
commit
5e990b3991
@@ -11,6 +11,7 @@
|
||||
#include "CrossPointSettings.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "ProgressFile.h"
|
||||
#include "ReaderUtils.h"
|
||||
#include "RecentBooksStore.h"
|
||||
#include "components/UITheme.h"
|
||||
@@ -421,14 +422,13 @@ void TxtReaderActivity::renderStatusBar() const {
|
||||
}
|
||||
|
||||
void TxtReaderActivity::saveProgress() const {
|
||||
HalFile f;
|
||||
if (Storage.openFileForWrite("TRS", txt->getCachePath() + "/progress.bin", f)) {
|
||||
uint8_t data[4];
|
||||
data[0] = currentPage & 0xFF;
|
||||
data[1] = (currentPage >> 8) & 0xFF;
|
||||
data[2] = 0;
|
||||
data[3] = 0;
|
||||
f.write(data, 4);
|
||||
uint8_t data[4];
|
||||
data[0] = currentPage & 0xFF;
|
||||
data[1] = (currentPage >> 8) & 0xFF;
|
||||
data[2] = 0;
|
||||
data[3] = 0;
|
||||
if (!ProgressFile::writeAtomic(txt->getCachePath(), data, sizeof(data))) {
|
||||
LOG_ERR("TRS", "Failed to save progress: page %d", currentPage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user