diff --git a/src/helpers/ClientACL.cpp b/src/helpers/ClientACL.cpp index 1282382737..04b9439155 100644 --- a/src/helpers/ClientACL.cpp +++ b/src/helpers/ClientACL.cpp @@ -1,9 +1,16 @@ #include "ClientACL.h" +static char* getTmpPath(const char* filename) { + static char tmp_filename[32]; + snprintf(tmp_filename, sizeof(tmp_filename), "%s.tmp", filename); + return tmp_filename; +} + static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove(filename); - return _fs->open(filename, FILE_O_WRITE); + char* tmp_filename = getTmpPath(filename); + _fs->remove(tmp_filename); + return _fs->open(tmp_filename, FILE_O_WRITE); #elif defined(RP2040_PLATFORM) return _fs->open(filename, "w"); #else @@ -54,7 +61,8 @@ void ClientACL::load(FILESYSTEM* fs, const mesh::LocalIdentity& self_id) { void ClientACL::save(FILESYSTEM* fs, bool (*filter)(ClientInfo*)) { _fs = fs; - File file = openWrite(_fs, "/s_contacts"); + const char* real_path = "/s_contacts"; + File file = openWrite(_fs, real_path); if (file) { uint8_t unused[2]; memset(unused, 0, sizeof(unused)); @@ -74,6 +82,14 @@ void ClientACL::save(FILESYSTEM* fs, bool (*filter)(ClientInfo*)) { if (!success) break; // write failed } file.close(); + +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + char* tmp_path = getTmpPath(real_path); + if (!_fs->rename(tmp_path, real_path)) { + _fs->remove(tmp_path); + MESH_DEBUG_PRINTLN("ERROR: ClientACL::save rename failed!"); + } +#endif } } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b71afc72e2..f68a6fc1eb 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -126,8 +126,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { void CommonCLI::savePrefs(FILESYSTEM* fs) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - fs->remove("/com_prefs"); - File file = fs->open("/com_prefs", FILE_O_WRITE); + // Atomic write: write to temp file, then rename over the real file. + // LittleFS rename() is a single metadata commit -- atomic even on power loss. + const char* tmp_path = "/com_prefs.tmp"; + const char* real_path = "/com_prefs"; + fs->remove(tmp_path); // clean up any stale temp from previous failed write + File file = fs->open(tmp_path, FILE_O_WRITE); #elif defined(RP2040_PLATFORM) File file = fs->open("/com_prefs", "w"); #else @@ -183,6 +187,13 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { // next: 291 file.close(); + +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + if (!fs->rename(tmp_path, real_path)) { + fs->remove(tmp_path); + MESH_DEBUG_PRINTLN("ERROR: savePrefs rename failed!"); + } +#endif } } diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 7b8399e260..7754e91d3f 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -1,6 +1,6 @@ #include "RegionMap.h" #include -#include +#include // helper class for region map exporter, we emulate Stream with a safe buffer writer. @@ -58,10 +58,17 @@ static const char* skip_hash(const char* name) { return *name == '#' ? name + 1 : name; } +static char* getTmpPath(const char* filename) { + static char tmp_filename[32]; + snprintf(tmp_filename, sizeof(tmp_filename), "%s.tmp", filename); + return tmp_filename; +} + static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) - _fs->remove(filename); - return _fs->open(filename, FILE_O_WRITE); + char* tmp_filename = getTmpPath(filename); + _fs->remove(tmp_filename); + return _fs->open(tmp_filename, FILE_O_WRITE); #elif defined(RP2040_PLATFORM) return _fs->open(filename, "w"); #else @@ -115,7 +122,8 @@ bool RegionMap::load(FILESYSTEM* _fs, const char* path) { } bool RegionMap::save(FILESYSTEM* _fs, const char* path) { - File file = openWrite(_fs, path ? path : "/regions2"); + const char* real_path = path ? path : "/regions2"; + File file = openWrite(_fs, real_path); if (file) { uint8_t pad[128]; memset(pad, 0, sizeof(pad)); @@ -139,6 +147,15 @@ bool RegionMap::save(FILESYSTEM* _fs, const char* path) { } } file.close(); + +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + char* tmp_path = getTmpPath(real_path); + if (!_fs->rename(tmp_path, real_path)) { + _fs->remove(tmp_path); + MESH_DEBUG_PRINTLN("ERROR: RegionMap::save rename failed!"); + return false; + } +#endif return true; } return false; // failed