fix(ui): post-Trail review — alert visible everywhere + writeTo error checks

Four findings from a code review pass on the Trail feature:

- showAlert overlay was gated to `curr == home`, so feedback like
  "Tracking started", "Trail saved", "GPX N B (USB)" never rendered on
  the Trail screen (or QuickMsg / Settings / Auto-Advert, for that
  matter). Drop the gate — the overlay shows on whichever screen the
  caller invoked from.
- TrailStore::writeTo only verified the first write; subsequent point
  writes could partially fail (full filesystem, mid-write power loss)
  and the method would still return true. Check every write return
  value so handleSave's "Trail saved" only fires on actual success.
- gpxPoint: clamp snprintf's intended-length return against the buffer
  size and skip the point if gmtime returns null. Avoids any chance of
  out.write reading past buf[120].
- TrailScreen::_act_map sized exactly to today's 8 actions — bump to 12
  so adding another popup item later doesn't cause an OOB write into
  the surrounding members.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-26 08:15:53 +02:00
parent cea3c30f31
commit 755525761e
3 changed files with 12 additions and 9 deletions

View File

@@ -171,17 +171,17 @@ public:
template <typename F>
bool writeTo(F& file) {
uint32_t magic = SAVE_MAGIC;
if (file.write((uint8_t*)&magic, sizeof(magic)) != sizeof(magic)) return false;
uint8_t ver = SAVE_VERSION;
uint8_t res = 0;
uint16_t cnt = (uint16_t)_count;
file.write(&ver, 1);
file.write(&res, 1);
file.write((uint8_t*)&cnt, sizeof(cnt));
uint32_t accum = currentAccumulatedMs();
file.write((uint8_t*)&accum, sizeof(accum));
if (file.write((uint8_t*)&magic, sizeof(magic)) != sizeof(magic)) return false;
if (file.write(&ver, 1) != 1) return false;
if (file.write(&res, 1) != 1) return false;
if (file.write((uint8_t*)&cnt, sizeof(cnt)) != sizeof(cnt)) return false;
if (file.write((uint8_t*)&accum, sizeof(accum)) != sizeof(accum)) return false;
for (int i = 0; i < _count; i++) {
file.write((uint8_t*)&at(i), sizeof(TrailPoint));
if (file.write((uint8_t*)&at(i), sizeof(TrailPoint)) != sizeof(TrailPoint)) return false;
}
return true;
}
@@ -253,12 +253,15 @@ public:
char buf[120];
time_t t = (time_t)p.ts;
struct tm* gt = ::gmtime(&t);
if (!gt) return n; // defensive: skip malformed timestamps
int len = snprintf(buf, sizeof(buf),
"<trkpt lat=\"%.6f\" lon=\"%.6f\"><time>%04d-%02d-%02dT%02d:%02d:%02dZ</time></trkpt>\n",
p.lat_1e6 / 1.0e6, p.lon_1e6 / 1.0e6,
gt->tm_year + 1900, gt->tm_mon + 1, gt->tm_mday,
gt->tm_hour, gt->tm_min, gt->tm_sec);
n += out.write((const uint8_t*)buf, len);
if (len < 0) return n;
if ((size_t)len > sizeof(buf)) len = sizeof(buf); // snprintf returns intended size
n += out.write((const uint8_t*)buf, (size_t)len);
return n;
}