Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions examples/companion_radio/DataStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,48 @@ static File openWrite(FILESYSTEM* fs, const char* filename) {

#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
static uint32_t _ContactsChannelsTotalBlocks = 0;

/* Boot-time filesystem integrity check.
*
* A littlefs whose superblock is intact but whose interior metadata is damaged (e.g. flash
* region partially overwritten, or interrupted writes) mounts successfully, then wedges the
* device on the FIRST write after boot: lfs_deorphan() -- run lazily by lfs_remove()/
* lfs_file_open() once per power cycle -- walks every metadata block, and littlefs v1
* follows corrupt tail pointers into an unbounded loop, or (with LFS_NO_ASSERT) reads/writes
* a wild out-of-range block address, hard-faulting the MCU. Either way the node freezes
* until manually reset, and every reboot repeats the cycle.
*
* Walking the whole tree up-front detects this state safely: lfs_traverse() invokes the
* callback on each block number BEFORE reading it, so out-of-range pointers are caught
* without touching bad addresses, and a visited-block count exceeding block_count can only
* mean a metadata cycle. On failure the filesystem is reformatted -- losing its contents,
* but recovering the node without a chip erase.
*/
struct FsckState {
lfs_size_t visited;
lfs_size_t block_count;
};

static int _fsckBlock(void *p, lfs_block_t block) {
FsckState *s = (FsckState *) p;
if (block >= s->block_count) return LFS_ERR_CORRUPT; // pointer outside the filesystem
if (++s->visited > s->block_count) return LFS_ERR_CORRUPT; // more blocks than exist -> cycle
return 0;
}

static bool _isFilesystemIntact(FILESYSTEM* fs) {
FsckState s;
s.visited = 0;
s.block_count = fs->_getFS()->cfg->block_count;
return lfs_traverse(fs->_getFS(), _fsckBlock, &s) == 0;
}

static void _fsckAndRecover(FILESYSTEM* fs, const char* name) {
if (!_isFilesystemIntact(fs)) {
MESH_DEBUG_PRINTLN("ERROR: %s filesystem is corrupted - reformatting!", name);
fs->format();
}
}
#endif

void DataStore::begin() {
Expand All @@ -52,6 +94,12 @@ void DataStore::begin() {
#endif

#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
// must run before the first filesystem write (checkAdvBlobFile/migrateToSecondaryFS below)
_fsckAndRecover(_fs, "primary");
if (_fsExtra != nullptr) {
_fsckAndRecover(_fsExtra, "secondary");
}

_ContactsChannelsTotalBlocks = _getContactsChannelsFS()->_getFS()->cfg->block_count;
checkAdvBlobFile();
#if defined(EXTRAFS) || defined(QSPIFLASH)
Expand Down