Skip to content

Make Safe File actually safe#10949

Open
jp-bennett wants to merge 1 commit into
developfrom
safeFile
Open

Make Safe File actually safe#10949
jp-bennett wants to merge 1 commit into
developfrom
safeFile

Conversation

@jp-bennett

@jp-bennett jp-bennett commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

SafeFile has been something of a landmine, and several uses of it were failing to manage the spilock correctly. These changes eliminate the need to touch FSCom or spiLock directly for users of SafeFile.

These are pretty core changes, so lots of testing needed.

If we like this approach, I'd recommend adding file read routines to safe file, and work to eliminate raw FSCom access everywhere.

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of saving settings, messages, caches, and calibration data to flash.
    • Reduced the chance of storage write failures by handling file access locking more consistently.
    • Persisted data now creates needed folders automatically in more cases, avoiding missing-directory errors.
    • Updated several save flows to avoid lock conflicts during file operations.

@jp-bennett jp-bennett added the cleanup Code cleanup or refactor label Jul 9, 2026
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

SafeFile now internally acquires spiLock during write operations and creates parent directories in openFile(). Numerous callers (MessageStore, GPS, NodeDB, WarmNodeStore, mesh-pb-constants, CannedMessageModule/Store, HopScalingModule, MotionSensor, FlashData) remove their own explicit spiLock guards and directory-creation calls, relying on SafeFile's internal handling.

Changes

SPI Lock and Directory Creation Consolidation into SafeFile

Layer / File(s) Summary
SafeFile internal locking and directory creation
src/SafeFile.cpp
openFile() builds the temp filename via std::string and creates the parent directory via FSCom.mkdir when needed; write(uint8_t) and write(const uint8_t*, size_t) now acquire spiLock internally via concurrency::LockGuard.
MessageStore save/clear paths
src/MessageStore.cpp
saveToFlash() and clearAllMessages() drop the manual root-directory setup and spiLock guards around count/record writes, relying on SafeFile.
Other SafeFile/saveProto callers
src/gps/GPS.cpp, src/mesh/WarmNodeStore.cpp, src/mesh/mesh-pb-constants.cpp, src/graphics/niche/Utils/FlashData.h, src/mesh/NodeDB.cpp, src/modules/CannedMessageModule.cpp, src/graphics/niche/Utils/CannedMessageStore.cpp, src/modules/HopScalingModule.cpp, src/motion/MotionSensor.cpp
Explicit spiLock guards and FSCom.mkdir calls are removed from these write paths, with comments added noting SafeFile now handles locking and directory creation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant SafeFile
  participant spiLock
  participant FSCom

  Caller->>SafeFile: openFile(path)
  SafeFile->>FSCom: mkdir(parentDir) if path contains '/'
  Caller->>SafeFile: write(data)
  SafeFile->>spiLock: acquire LockGuard
  SafeFile->>FSCom: write bytes to file
  spiLock-->>SafeFile: release on scope exit
  SafeFile-->>Caller: write complete
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is too brief and misses the required template sections, including the attestations and device/regression testing checklist. Replace the placeholder text with the repository template, fill the testing attestations, and note which devices or regressions were checked.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches the main change: hardening SafeFile and removing manual spiLock/FSCom handling.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch safeFile

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (27)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-08. Updated for 437da81.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/SafeFile.cpp (2)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the redundant directory-creation comment.

The block is self-explanatory, so this can be dropped. As per coding guidelines, keep code comments minimal and avoid comments that restate the next line.

Suggested cleanup
-    // Create the containing directory if it doesn't already exist
     size_t slashIndex = filenameTmp.find_last_of('/');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/SafeFile.cpp` at line 13, Remove the redundant directory-creation comment
in SafeFile.cpp; the surrounding logic is already self-explanatory, so delete
that standalone comment and keep the nearby code unchanged. Focus on the comment
immediately before the directory existence check/creation block and leave the
implementation intact.

Source: Coding guidelines


8-9: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add an explicit <string> include

src/SafeFile.cpp uses std::string, but src/SafeFile.h only pulls in FSCommon.h, SPILock.h, and configuration.h. Include <string> directly to avoid depending on indirect includes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/SafeFile.cpp` around lines 8 - 9, Add an explicit <string> include in
SafeFile.cpp because std::string is used there but only indirect headers are
currently relied on. Update the includes near SafeFile.cpp’s top so the
translation unit does not depend on SafeFile.h or FSCommon.h to provide
std::string.
src/graphics/niche/Utils/FlashData.h (1)

110-134: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Trim the 3-line comment block to comply with the comment-length guideline.

Lines 110-112 form a 3-line comment; the applicable guideline caps explanatory comments at one or two lines. The rest of the save() change (dropping the manual mkdir/spiLock, two sequential f.write() calls) is consistent with SafeFile's internal locking/mkdir contract.

As per coding guidelines, **/*.{c,cc,cpp,h,hpp}: "Keep code comments minimal: use at most one or two lines, only when the reason is not obvious; do not add multi-paragraph explanatory comments or restate what the next line does."

✏️ Proposed trim
-    // Save module's custom data (settings?) to flash. Doesn't use protobufs.
-    // SafeFile (constructor/write/close) takes the firmware's SPI lock for itself internally
-    // (relevant when files are on an SD card), so we must not hold it across those calls.
+    // Save module's custom data (settings?) to flash. Doesn't use protobufs.
+    // SafeFile takes the SPI lock itself internally; don't hold it across these calls.
     static void save(T *data, const char *label)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/graphics/niche/Utils/FlashData.h` around lines 110 - 134, The comment
block above FlashData::save is too long for the guideline; trim the explanatory
comments around the SafeFile usage to at most one or two concise lines. Keep the
essential note about SafeFile handling atomic write, mkdir, and SPI locking
internally, and remove the rest of the restated implementation details near
save(), getFilename(), and the FSCom block.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/graphics/niche/Utils/FlashData.h`:
- Around line 110-134: The comment block above FlashData::save is too long for
the guideline; trim the explanatory comments around the SafeFile usage to at
most one or two concise lines. Keep the essential note about SafeFile handling
atomic write, mkdir, and SPI locking internally, and remove the rest of the
restated implementation details near save(), getFilename(), and the FSCom block.

In `@src/SafeFile.cpp`:
- Line 13: Remove the redundant directory-creation comment in SafeFile.cpp; the
surrounding logic is already self-explanatory, so delete that standalone comment
and keep the nearby code unchanged. Focus on the comment immediately before the
directory existence check/creation block and leave the implementation intact.
- Around line 8-9: Add an explicit <string> include in SafeFile.cpp because
std::string is used there but only indirect headers are currently relied on.
Update the includes near SafeFile.cpp’s top so the translation unit does not
depend on SafeFile.h or FSCommon.h to provide std::string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d6ebe0a-cd3d-4779-9ff7-7daf927eac7f

📥 Commits

Reviewing files that changed from the base of the PR and between b4b1ece and 437da81.

📒 Files selected for processing (11)
  • src/MessageStore.cpp
  • src/SafeFile.cpp
  • src/gps/GPS.cpp
  • src/graphics/niche/Utils/CannedMessageStore.cpp
  • src/graphics/niche/Utils/FlashData.h
  • src/mesh/NodeDB.cpp
  • src/mesh/WarmNodeStore.cpp
  • src/mesh/mesh-pb-constants.cpp
  • src/modules/CannedMessageModule.cpp
  • src/modules/HopScalingModule.cpp
  • src/motion/MotionSensor.cpp
💤 Files with no reviewable changes (1)
  • src/modules/HopScalingModule.cpp

@thebentern

Copy link
Copy Markdown
Contributor

In theory I agree with all of this. It's a risky 11th hour change I'd like to hold off on for now though

@jp-bennett

Copy link
Copy Markdown
Collaborator Author

In theory I agree with all of this. It's a risky 11th hour change I'd like to hold off on for now though

Yes, agreed. It is worth noting that there are a couple instances where FSCom is currently getting called without taking the spiLock first.

@thebentern

Copy link
Copy Markdown
Contributor

In theory I agree with all of this. It's a risky 11th hour change I'd like to hold off on for now though

Yes, agreed. It is worth noting that there are a couple instances where FSCom is currently getting called without taking the spiLock first.

Maybe we can peel those off into a separate PR

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Firmware Size Report

42 targets | vs develop: 21 increased, 1 decreased, net +431,216 (+421.1 KB)

Target Flash vs develop RAM RAM vs develop
picow 1,274,344 📈 +29,464 (+28.8 KB) 152,508 n/a
pico2w 1,248,800 📈 +28,284 (+27.6 KB) 158,988 n/a
rak11310 833,384 📈 +27,960 (+27.3 KB) 86,320 n/a
pico 810,632 📈 +27,944 (+27.3 KB) 85,876 n/a
seeed_xiao_rp2040 808,832 📈 +27,928 (+27.3 KB) 85,868 n/a
Show 37 more target(s)
Target Flash vs develop RAM RAM vs develop
seeed_xiao_rp2350 794,904 📈 +26,968 (+26.3 KB) 92,368 n/a
pico2 796,736 📈 +26,960 (+26.3 KB) 92,376 n/a
elecrow-adv-35-tft 3,439,024 📈 +22,400 (+21.9 KB) 94,632 n/a
heltec-vision-master-e213-inkhud 2,249,360 📈 +22,000 (+21.5 KB) 92,392 n/a
t-deck-tft 3,832,448 📈 +21,776 (+21.3 KB) 108,008 n/a
rak3312 2,290,448 📈 +18,208 (+17.8 KB) 102,040 n/a
heltec-ht62-esp32c3-sx1262 2,153,056 📈 +18,064 (+17.6 KB) 112,004 n/a
seeed-xiao-s3 2,293,792 📈 +17,648 (+17.2 KB) 102,064 n/a
heltec-v3 2,281,328 📈 +17,248 (+16.8 KB) 130,232 n/a
rak11200 1,877,824 📈 +17,216 (+16.8 KB) 87,044 n/a
station-g3 2,283,152 📈 +16,800 (+16.4 KB) 101,848 n/a
station-g2 2,283,136 📈 +16,784 (+16.4 KB) 101,848 n/a
tlora-c6 2,384,736 📈 +16,432 (+16.0 KB) 102,772 n/a
t-eth-elite 2,507,888 📈 +16,400 (+16.0 KB) 103,068 n/a
heltec-v4 2,293,248 📈 +15,952 (+15.6 KB) 101,112 n/a
rak3172 183,308 📉 -2,988 (-2.9 KB) 25,016 n/a
wio-e5 240,396 📈 +1,768 (+1.7 KB) 26,640 n/a
heltec-mesh-node-t096 750,224 110,492 n/a
heltec-mesh-node-t1 748,072 102,236 n/a
heltec-mesh-node-t114 748,016 110,428 n/a
heltec-mesh-solar 745,088 110,596 n/a
heltec-mesh-solar-eink 737,080 109,916 n/a
heltec-mesh-solar-inkhud 717,600 101,372 n/a
heltec-mesh-solar-oled 745,184 110,596 n/a
heltec-mesh-solar-tft 751,496 110,692 n/a
heltec-mesh-tower-v2 736,192 101,452 n/a
meshnology_w10 2,464,032 136,212 n/a
rak4631 766,072 104,500 n/a
rak_wismeshtag 600,304 92,604 n/a
seeed_mesh_tracker_X1 507,368 87,316 n/a
seeed_wio_tracker_L1 739,680 110,340 n/a
seeed_xiao_nrf52840_kit 734,400 102,060 n/a
t-echo 753,208 93,236 n/a
t-echo-inkhud 713,024 83,956 n/a
t-echo-plus 750,936 93,236 n/a
t-impulse-plus 726,680 110,324 n/a
tracker-t1000-e 506,472 87,316 n/a

Size budgets

Env Metric Measured Budget Used
rak4631 RAM (.data+.bss) 104,500 113,000 92.5%
rak4631 flash 766,072 786,000 97.5%

Budgets live in bin/ram_budgets.json and are raised deliberately in the PR that needs the headroom - never automatically.


Updated for b9679eb

@oscgonfer

oscgonfer commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Hi @jp-bennett ,

Is this the right approach? If not I can change it for this (and any other sensor).

https://github.com/meshtastic/firmware/blob/develop/src/modules/Telemetry/Sensor/SEN5XSensor.cpp#L390

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup Code cleanup or refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants