Skip to content

Commit a6e86f2

Browse files
committed
fix: reject incomplete Files text previews
1 parent e9ddfd0 commit a6e86f2

8 files changed

Lines changed: 336 additions & 4 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ jobs:
3939
run: python3 tools/test_ring_history.py --compiler cc
4040
- name: Test ebook pages
4141
run: python3 tools/test_ebook_pages.py --compiler cc
42+
- name: Test Files text previews
43+
run: python3 tools/test_file_preview.py --compiler cc
4244
- name: Test capture reader
4345
run: |
4446
cc -std=c11 -Wall -Wextra -Werror -pedantic -I main main/capture_data.c tests/capture_data_test.c -o "$RUNNER_TEMP/capture_data_test"
@@ -180,6 +182,20 @@ jobs:
180182
if-no-files-found: warn
181183
retention-days: 14
182184

185+
- name: Retain Files text preview evidence
186+
uses: actions/upload-artifact@v4
187+
if: always()
188+
with:
189+
name: file-preview-logs
190+
path: |
191+
build/file-preview/compile.log
192+
build/file-preview/result.log
193+
build/file-preview/source.json
194+
build/file-preview/file_preview.inc
195+
build/file-preview/file_preview_config.inc
196+
if-no-files-found: warn
197+
retention-days: 14
198+
183199
firmware:
184200
runs-on: ubuntu-latest
185201
container: espressif/idf:v5.4.2

‎ROADMAP.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Phase 1 software progress is also underway:
6767
- [x] Ring logging commits its saved timestamp only after tail recovery and a complete, successfully closed read. Failed loads retain queued samples and defer history requests until a successful retry; known SD unavailability also blocks a cached checkpoint. Appends check directory errors and preserve the first seek/tell/header/row error through close and repair. All 59 native file/queue/fault cases pass; the preceding callbacks fail 32 controls. Checks verify exact bytes, retry/deduplication, timeout handling while blocked and 25 checkpoint reopens with closed modeled handles. Linux CI retains source hashes and logs. Physical SD, BLE, panel and power cuts remain open.
6868
- [x] Ebook page reads publish their text and byte positions only after read/close success. Failed navigation retains the previous page for retry, including zero-byte and full-count read errors. Initial navigation is disabled until a page loads; callbacks reject disabled/offscreen actions and invalid positions. All 44 native file/state/fault checks pass, including 25 navigation cycles with one reused buffer and closed handles. Thirty-one preceding-callback controls fail; three new unsafe-boundary guards are tested only with the new callbacks. Linux CI retains source hashes, initialization source and logs. Physical SD/panel and power behavior remain open.
6969
- [x] Recognized Scope/I2C and saved serial CSV loads require a successful close before showing a graph or row-copy controls. Failed or empty capture loads release their row buffer immediately. All 36 real LVGL/native-file checks pass; the preceding viewers fail 15 controls. Checks cover both row limits, I2C gaps, read/seek/close and allocation faults, unchanged file/clipboard bytes and 25 success/error/reopen cycles per viewer with stable memory in the unchanged 96 KiB pool. Existing serial-copy/inspector checks pass. Windows CI retains complete viewer/parser sources and logs; physical SD/panel behavior remains open.
70+
- [x] Files text previews require a successful read and close before showing their existing 4,095-byte prefix. Failed operations show the first error, using EIO when errno is absent; SD-path failures reach the existing error callback. All 36 native file/fault cases pass with unchanged bytes and closed handles, including read/close errors, retry, routing and 25 cycles. The preceding callback fails 29 controls: 23 reproduce ignored read/close failures or retry cycles, and six concern open-error details/reporting. UI, CSV probes and logical mount paths are controlled; Linux CI retains source hashes and logs. Physical storage/panel checks remain open.
7071
- [x] Shared CSV tail recovery now scans backwards in 512-byte blocks. Eight previous-source controls exceed stdio seek budgets; a 64 KiB partial tail drops from 65,538 seeks to 130 with the same retained prefix. All 32 native byte/boundary/fault cases, original storage checks and five MQTT/HTTP logging cases pass with closed modeled handles. The unflashed firmware builds with unchanged configuration. Linux CI retains source hashes and compilation/result logs; physical SD timing and power cuts remain open.
7172
- [x] Built-in SD paths, timestamps, units, 8.3-safe collision names, `.TMP`, and `.BAK` behavior are documented.
7273
- [x] Persisted brightness choices, a dimmed two-minute screen, and configurable 5/10/30-minute or Never screen-off behavior are implemented.

‎docs/architecture.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ Ebooks reads up to 8 KiB into its existing cached PSRAM buffer. Page text, curre
9393

9494
The Scope/I2C capture and saved serial CSV viewers require a successful close before constructing their graph or row-copy controls. A read or close failure after format recognition shows an error and releases the viewer buffers immediately. Empty recognized captures also release their row buffer. Real LVGL checks cover error/reopen transitions and stable memory in the unchanged 96 KiB pool; filesystem faults and PSRAM allocation are controlled host boundaries, with physical SD and panel checks still open.
9595

96+
Files dispatches supported CSVs to those viewers before its text fallback. The fallback reads at most 4,095 bytes into its existing static buffer and publishes them only after checking the stream and closing successfully. Failed open/read/close operations display the first error; missing errno becomes EIO. Error reporting to the shared SD callback is scoped to the exact `/sdcard/` prefix. Native files and controlled UI/probe/path boundaries check this callback; physical storage and panel behavior remain open.
97+
9698
See [Storage and compatibility contract](compatibility.md) and [Data formats](data-formats.md).
9799

98100
## Network and radio

‎docs/data-formats.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Files also opens published UART and RS-485 `.CSV` logs as a timestamped RX/TX ti
3131

3232
After a supported capture or serial header is recognized, seek/read/close failures show an error instead of a graph or copyable timeline. The viewers release their row/text buffers immediately on failed or empty loads. The original file remains unchanged; a successful reopen can display it again. These boundaries are checked with real LVGL and native files under controlled I/O failures; physical SD behavior remains open.
3333

34+
The fallback text preview displays up to the first 4,095 source bytes after a successful read and close. A failed open/read/close displays the first error instead of an unconfirmed prefix, including zero-byte or full-count reads with the stream error flag set. An error-free empty read remains an empty preview. Paths beginning `/sdcard/` forward failures to the existing SD error callback. The preview keeps its existing text/NUL behavior and never writes the file. Native checks use controlled UI/probes and logical mount paths; physical storage and panel checks remain open.
35+
3436
Choose a record from the current page and tap **COPY ROW** to put its original 1-128 bytes on the shared RAM clipboard. The selector and timeline use the same record numbers: positions among loaded valid rows, not physical CSV line numbers. Page/filter changes select the first matching record on the new page; changing Hex/ASCII preserves the selection. Rows of 129-256 bytes remain viewable but cannot be partially copied. Empty filters and failed copies preserve the clipboard. Record bytes can then be pasted into Byte Lab or RTU Frames; a logged RX/TX chunk may split or combine protocol frames, so a record is not a guarantee of a complete message. Copy changes neither the CSV nor the displayed byte values.
3537

3638
## CSV conventions

‎docs/testing.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Python 3 and Node.js are needed for the quick checks. The configuration checks a
1616
| Ring heart-rate logging, saved timestamps or queue processing | [Ring history storage](#ring-history-storage) and a firmware build. |
1717
| Ebook page loading or navigation | [Ebook pages](#ebook-pages) and a firmware build. |
1818
| Scope/I2C capture or saved serial CSV viewers | [CSV viewer storage and UI](#csv-viewer-storage-and-ui), existing serial-copy checks and a firmware build. |
19+
| Files fallback text previews | [Files text previews](#files-text-previews) and a firmware build. |
1920
| Offline apps, SPI or I2C editor/result flows | [Offline and peripheral UI](#offline-and-peripheral-ui). |
2021
| Serial, saved serial logs or RTU Frames | [Serial and RTU UI](#serial-and-rtu-ui). |
2122
| Modbus TCP, NTP, Wake-on-LAN, UDP or HTTP fixtures | [Network fixtures and UI](#network-fixtures-and-ui). |
@@ -163,6 +164,18 @@ Every case checks the original file fingerprint and retained clipboard bytes. Fa
163164

164165
Complete viewer/parser/clipboard snapshots, their source/header hashes, the I/O adapter/test hashes and compiler/runtime logs are retained under `build/csv-viewer/`. Windows CI runs these 36 checks after the original 820 native checks and retains `csv-viewer-logs`; this is separate from the Linux fixtures. Use `tools/test_serial_log_ui.ps1` for the existing row-copy/filter/page/inspector checks.
165166

167+
## Files text previews
168+
169+
```powershell
170+
python tools/test_file_preview.py --compiler 'C:/path/to/clang.exe'
171+
```
172+
173+
This portable runner compiles the actual `open_file` callback from `main/main.c`. Its 36 native-file cases check empty/small/exact/capped previews, missing files, open/read/close errors, zero-byte/full-count error reads, missing/stale errno, first-error preservation, SD prefix boundaries, reopen recovery, modeled CSV routing and 25 success/error/success cycles. Every case independently checks unchanged file bytes and closed handles. The preceding callback fails 29 controls; 23 reproduce ignored read/close errors or their retry cycles, while six check added details/reporting on existing open-error screens.
174+
175+
UI, CSV viewer probes and the SD error callback are controlled. Logical internal/SD paths map to one owned native fixture file. These checks verify callback dispatch, exact preview/error text and SD callback arguments; they do not run the shared SD callback's filtering, actual LVGL, mount handling or physical storage/panel behavior. The real CSV viewer checks above cover the delegated viewers separately. The 4,095-byte cap and existing NUL/text behavior remain in place.
176+
177+
Generated callback/config source, full main/function/test hashes and compile/runtime logs stay under `build/file-preview/`. Linux CI retains `file-preview-logs`, including available compiler/test failure diagnostics. These 36 cases are separate from the Windows native total.
178+
166179
## OTA manifests
167180

168181
```powershell

‎main/main.c‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4233,11 +4233,23 @@ static void open_file(const char *path)
42334233
lv_label_set_long_mode(title, LV_LABEL_LONG_DOT);
42344234

42354235
static char text[4096];
4236+
errno = 0;
42364237
FILE *file = fopen(path, "rb");
4237-
size_t read = file ? fread(text, 1, sizeof(text) - 1, file) : 0;
4238-
if (file) fclose(file);
4239-
text[read] = '\0';
4240-
if (!file) snprintf(text, sizeof(text), "Could not open this file.");
4238+
int error = file ? 0 : (errno ? errno : EIO);
4239+
size_t read = 0;
4240+
if (file) {
4241+
errno = 0;
4242+
read = fread(text, 1, sizeof(text) - 1, file);
4243+
if (ferror(file)) error = errno ? errno : EIO;
4244+
errno = 0;
4245+
if (fclose(file) != 0 && !error) error = errno ? errno : EIO;
4246+
}
4247+
if (error) {
4248+
if (!strncmp(path, SD_PATH "/", sizeof(SD_PATH))) sd_record_error(error);
4249+
snprintf(text, sizeof(text), "Could not read this file: %s", strerror(error));
4250+
} else {
4251+
text[read] = '\0';
4252+
}
42414253

42424254
lv_obj_t *viewer = lv_textarea_create(content);
42434255
lv_obj_set_size(viewer, 640, 900);

0 commit comments

Comments
 (0)