From e058ea8a41c6a0a0601797563bda1c48c6e4ddfe Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Sun, 9 Aug 2026 18:23:52 -0400 Subject: [PATCH] Fix FAT dashcam file deletion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- PLAN.md | 12 ++++++++++-- modules/app_dash.py | 20 +++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/PLAN.md b/PLAN.md index 5e16699..b8a70c2 100644 --- a/PLAN.md +++ b/PLAN.md @@ -845,8 +845,16 @@ Phase 15 shared list component: regression behavior, and cleanup-failure reporting against temporary filesystem fixtures. Deployed to the Pi; the live API correctly returned no storage devices while ignoring the attached PL2303 OBD adapter, and the responsive browser UI passed no-device and mocked populated - list/sort/confirmation/delete tests. Actual deletion from the physical dashcam remains a - hardware verification item for the next time it is connected in USB storage mode. + list/sort/confirmation/delete tests. +- **Real-hardware deletion verification (2026-08-09)**: connected the 29.7GB FAT dashcam and listed + all 87 original videos (29,615,111,758 bytes) through the live API. The first controlled delete + exposed that FAT files were root-owned despite the read-write remount, so unprivileged + `os.remove()` failed; added a privileged `sudo rm -- ` fallback only after the + existing extension, realpath, and mount-boundary checks pass. Created and deleted a tiny + disposable `YARIS_OBD_DELETE_TEST.MP4` twice through the real API. Both cycles returned exactly + to the original 87-file count and byte total, left every real video untouched, unmounted + `/mnt/dashcam`, and kept the service active. Bulk delete uses the same validated per-file removal + path but was not run against the real card because it would intentionally erase all footage. ### Phase 17: Local imported-video browser + player — 🚧 PLANNED Same file-browser treatment for videos already imported to the Pi's own disk diff --git a/modules/app_dash.py b/modules/app_dash.py index b128fd9..11a30b7 100644 --- a/modules/app_dash.py +++ b/modules/app_dash.py @@ -292,6 +292,24 @@ def _safe_video_path(source_folder, relative_path): return target_real +def _remove_video_file(path): + try: + os.remove(path) + return + except PermissionError: + pass + + try: + subprocess.run( + ["sudo", "rm", "--", path], + capture_output=True, + text=True, + check=True, + ) + except subprocess.CalledProcessError as e: + raise OSError(e.stderr.strip() or f"Failed to delete {path}") from e + + def delete_dashcam_files(relative_path=None, delete_all=False, mount_point=DEFAULT_MOUNT_POINT): """Delete one or all dashcam videos, restoring read-only state afterward.""" if not delete_all and relative_path is None: @@ -336,7 +354,7 @@ def delete_dashcam_files(relative_path=None, delete_all=False, mount_point=DEFAU for _abs_path, rel_path in targets: try: safe_path = _safe_video_path(source, rel_path) - os.remove(safe_path) + _remove_video_file(safe_path) deleted.append(rel_path) except (OSError, ValueError) as e: errors.append(f"{rel_path}: {e}")