A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: Close leaked file descriptors in SleepActivity and web server (#869)

## Summary

- **SleepActivity.cpp**: Add missing `file.close()` calls in 3 code
paths that open BMP files for sleep screen rendering but never close
them before returning. Affects random custom sleep images, the
`/sleep.bmp` fallback, and book cover sleep screens.
- **CrossPointWebServer.cpp**: Add missing `dir.close()` in the delete
handler when `Storage.open()` returns a valid `FsFile` that is not a
directory.

## Context

SdFat is configured with `DESTRUCTOR_CLOSES_FILE=0`, which means
`FsFile` objects are **not** automatically closed when they go out of
scope. Every opened file must be explicitly closed.

The SleepActivity leaks are particularly impactful because they occur on
every sleep cycle. While ESP32 deep sleep clears RAM on wake, these
leaks can still affect the current session if sleep screen rendering is
triggered multiple times (e.g., cover preview, or if deep sleep fails to
engage).

The web server leak in `handleDelete()` is a minor edge case (directory
path that opens successfully but `isDirectory()` returns false), but
it's still worth fixing for correctness.

## Test plan

- [x] Verify sleep screen still renders correctly (custom BMP, fallback,
cover modes)
- [x] Verify folder deletion still works via the web UI
- [ ] Monitor free heap before/after sleep screen rendering to confirm
no leak

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Jan Bažant <janbazant@Jan--Mac-mini.local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Dave Allie <dave@daveallie.com>

authored by

Jan Bažant
Jan Bažant
Claude Opus 4.6
Dave Allie
and committed by
GitHub
3cb60aa2 786b438e

+6
+6
src/activities/boot_sleep/SleepActivity.cpp
··· 82 82 Bitmap bitmap(file, true); 83 83 if (bitmap.parseHeaders() == BmpReaderError::Ok) { 84 84 renderBitmapSleepScreen(bitmap); 85 + file.close(); 85 86 dir.close(); 86 87 return; 87 88 } 89 + file.close(); 88 90 } 89 91 } 90 92 } ··· 98 100 if (bitmap.parseHeaders() == BmpReaderError::Ok) { 99 101 LOG_DBG("SLP", "Loading: /sleep.bmp"); 100 102 renderBitmapSleepScreen(bitmap); 103 + file.close(); 101 104 return; 102 105 } 106 + file.close(); 103 107 } 104 108 105 109 renderDefaultSleepScreen(); ··· 267 271 if (bitmap.parseHeaders() == BmpReaderError::Ok) { 268 272 LOG_DBG("SLP", "Rendering sleep cover: %s", coverBmpPath.c_str()); 269 273 renderBitmapSleepScreen(bitmap); 274 + file.close(); 270 275 return; 271 276 } 277 + file.close(); 272 278 } 273 279 274 280 return (this->*renderNoCoverSleepScreen)();