mirror of
https://github.com/meshtastic/firmware.git
synced 2026-03-25 02:23:22 -04:00
* 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 Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
99
src/SafeFile.cpp
Normal file
99
src/SafeFile.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#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";
|
||||
|
||||
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()
|
||||
{
|
||||
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 true;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user