mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-29 02:51:17 +00:00

* bug #4184: fix config file loss due to filesystem write errors
* Use SafeFile for atomic file writing (with xor checksum readback)
* Write db.proto last because it could be the largest file on the FS (and less critical)
* Don't keep a tmp file around while writing db.proto (because too big to fit two files in the filesystem)
* generate a new critial fault if we encounter errors writing to flash
either CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE or CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE
(depending on if the second write attempt worked)
* reformat the filesystem if we detect it is corrupted (then rewrite our config files) (only on nrf52 - not sure
yet if we should bother on ESP32)
* If we have to format the FS, make sure to preserve the oem.proto if it exists
* add logLegacy() so old C code in libs can log via our logging
* move filesList() to a better location (used only in developer builds)
* Reformat with "trunk fmt" to match our coding conventions
* for #4395: don't use .exists() to before attempting file open
If a LFS filesystem is corrupted, .exists() can fail when a mere .open()
attempt would have succeeded. Therefore better to do the .open() in hopes that
we can read the file (in case we need to reformat to fix the FS).
(Seen and confirmed in stress testing)
* for #4395 more fixes, see below for details:
* check for LFS assertion failures during file operations (needs customized lfs_util.h to provide suitable hooks)
* Remove fsCheck() because checking filesystem by writing to it is very high risk, it makes likelyhood that we will
be able to read the config protobufs quite low.
* Update the LFS inside of adafruitnrf52 to 1.7.2 (from their old 1.6.1) to get the following fix:
97d8d5e96a
* use disable_adafruit_usb.py now that we are (temporarily?) using a forked adafruit lib
We need to reach inside the adafruit project and turn off USE_TINYUSB, just doing that
from platformio.ini is no longer sufficient.
Tested on a wio-sdk-wm1110 board (which is the only board that had this problem)
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
105 lines
2.4 KiB
C++
105 lines
2.4 KiB
C++
#include "SafeFile.h"
|
|
|
|
#ifdef FSCom
|
|
|
|
// Only way to work on both esp32 and nrf52
|
|
static File openFile(const char *filename, bool fullAtomic)
|
|
{
|
|
if (!fullAtomic)
|
|
FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists)
|
|
|
|
String filenameTmp = filename;
|
|
filenameTmp += ".tmp";
|
|
|
|
// clear any previous LFS errors
|
|
lfs_assert_failed = false;
|
|
|
|
return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE);
|
|
}
|
|
|
|
SafeFile::SafeFile(const char *_filename, bool fullAtomic)
|
|
: filename(_filename), f(openFile(_filename, fullAtomic)), fullAtomic(fullAtomic)
|
|
{
|
|
}
|
|
|
|
size_t SafeFile::write(uint8_t ch)
|
|
{
|
|
if (!f)
|
|
return 0;
|
|
|
|
hash ^= ch;
|
|
return f.write(ch);
|
|
}
|
|
|
|
size_t SafeFile::write(const uint8_t *buffer, size_t size)
|
|
{
|
|
if (!f)
|
|
return 0;
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
hash ^= buffer[i];
|
|
}
|
|
return f.write((uint8_t const *)buffer, size); // This nasty cast is _IMPORTANT_ otherwise the correct adafruit method does
|
|
// not get used (they made a mistake in their typing)
|
|
}
|
|
|
|
/**
|
|
* Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches
|
|
*
|
|
* @return false for failure
|
|
*/
|
|
bool SafeFile::close()
|
|
{
|
|
if (!f)
|
|
return false;
|
|
|
|
f.close();
|
|
if (!testReadback())
|
|
return false;
|
|
|
|
// brief window of risk here ;-)
|
|
if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) {
|
|
LOG_ERROR("Can't remove old pref file\n");
|
|
return false;
|
|
}
|
|
|
|
String filenameTmp = filename;
|
|
filenameTmp += ".tmp";
|
|
if (!renameFile(filenameTmp.c_str(), filename.c_str())) {
|
|
LOG_ERROR("Error: can't rename new pref file\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Read our (closed) tempfile back in and compare the hash
|
|
bool SafeFile::testReadback()
|
|
{
|
|
bool lfs_failed = lfs_assert_failed;
|
|
lfs_assert_failed = false;
|
|
|
|
String filenameTmp = filename;
|
|
filenameTmp += ".tmp";
|
|
auto f2 = FSCom.open(filenameTmp.c_str(), FILE_O_READ);
|
|
if (!f2) {
|
|
LOG_ERROR("Can't open tmp file for readback\n");
|
|
return false;
|
|
}
|
|
|
|
int c = 0;
|
|
uint8_t test_hash = 0;
|
|
while ((c = f2.read()) >= 0) {
|
|
test_hash ^= (uint8_t)c;
|
|
}
|
|
f2.close();
|
|
|
|
if (test_hash != hash) {
|
|
LOG_ERROR("Readback failed hash mismatch\n");
|
|
return false;
|
|
}
|
|
|
|
return !lfs_failed;
|
|
}
|
|
|
|
#endif |