mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
5deb059c54
Process drive evidence and fit bounded steering profiles off the controls loop.
113 lines
4.3 KiB
Python
113 lines
4.3 KiB
Python
"""Admit one immutable, closed local rlog for offline analysis; never download."""
|
|
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass, field
|
|
import hashlib
|
|
import os
|
|
from pathlib import Path
|
|
import stat
|
|
|
|
from openpilot.starpilot.galaxy.drive_history import MAX_SEGMENT_ENTRIES, SEGMENT_NAME
|
|
|
|
|
|
MAX_COMPRESSED_BYTES = 32 * 1024 * 1024
|
|
DIR_FLAGS = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_NONBLOCK
|
|
FILE_FLAGS = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK
|
|
RLOGS = {"rlog.zst": "zst", "rlog.bz2": "bz2"}
|
|
|
|
|
|
class LocalLogUnavailable(Exception):
|
|
pass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ClosedLog:
|
|
segment_name: str
|
|
codec: str
|
|
compressed: bytes = field(repr=False)
|
|
sha256: str
|
|
size: int
|
|
|
|
|
|
def _identity(info: os.stat_result) -> tuple[int, int]:
|
|
return info.st_dev, info.st_ino
|
|
|
|
|
|
def _revision(info: os.stat_result) -> tuple[int, ...]:
|
|
return (*_identity(info), info.st_size, info.st_mtime_ns, info.st_ctime_ns)
|
|
|
|
|
|
def _allowed(permitted: Callable[[], bool]) -> None:
|
|
if not permitted():
|
|
raise LocalLogUnavailable("Analysis is no longer allowed")
|
|
|
|
|
|
def _closed_rlog(fd: int) -> str:
|
|
found = []
|
|
with os.scandir(fd) as entries:
|
|
for count, entry in enumerate(entries, 1):
|
|
if count > MAX_SEGMENT_ENTRIES or entry.name.endswith(".lock"):
|
|
raise LocalLogUnavailable("Segment is open or unavailable")
|
|
if entry.name in RLOGS:
|
|
found.append(entry.name)
|
|
if len(found) != 1:
|
|
raise LocalLogUnavailable("Select a segment with exactly one closed full rlog")
|
|
return found[0]
|
|
|
|
|
|
def read_closed_rlog(root: Path, segment_name: str, *, permitted: Callable[[], bool]) -> ClosedLog:
|
|
"""Revalidate an inventory choice, then bind its receipt to the bytes read.
|
|
|
|
The inventory is not authority: locks, file type, bounds and inode revisions
|
|
are checked again at admission and after the complete read. The supplied
|
|
root is the locally configured recording directory, never an HTTP input.
|
|
No decoded events or successful receipt escape a partial/cancelled read.
|
|
"""
|
|
matched = SEGMENT_NAME.fullmatch(segment_name) if type(segment_name) is str and len(segment_name) <= 180 else None
|
|
if matched is None or len(matched.group("number")) > 6 or not root.is_absolute() or ".." in root.parts:
|
|
raise LocalLogUnavailable("Invalid local segment selection")
|
|
fds = []
|
|
try:
|
|
_allowed(permitted)
|
|
root_fd = os.open(root, DIR_FLAGS)
|
|
fds.append(root_fd)
|
|
root_info = os.fstat(root_fd)
|
|
segment_fd = os.open(segment_name, DIR_FLAGS, dir_fd=root_fd)
|
|
fds.append(segment_fd)
|
|
segment_info = os.fstat(segment_fd)
|
|
filename = _closed_rlog(segment_fd)
|
|
source_fd = os.open(filename, FILE_FLAGS, dir_fd=segment_fd)
|
|
fds.append(source_fd)
|
|
before = os.fstat(source_fd)
|
|
if not stat.S_ISREG(before.st_mode) or not 0 < before.st_size <= MAX_COMPRESSED_BYTES:
|
|
raise LocalLogUnavailable("Full rlog is empty, oversized or not a regular file")
|
|
chunks = []
|
|
remaining = before.st_size
|
|
while remaining:
|
|
_allowed(permitted)
|
|
chunk = os.read(source_fd, min(1024 * 1024, remaining))
|
|
if not chunk:
|
|
raise LocalLogUnavailable("Full rlog changed while reading")
|
|
chunks.append(chunk)
|
|
remaining -= len(chunk)
|
|
if os.read(source_fd, 1):
|
|
raise LocalLogUnavailable("Full rlog grew while reading")
|
|
_allowed(permitted)
|
|
after = os.fstat(source_fd)
|
|
current = os.stat(filename, dir_fd=segment_fd, follow_symlinks=False)
|
|
segment_now = os.stat(segment_name, dir_fd=root_fd, follow_symlinks=False)
|
|
root_now = os.stat(root, follow_symlinks=False)
|
|
if (_revision(before) != _revision(after) or _revision(after) != _revision(current) or
|
|
not stat.S_ISREG(current.st_mode) or not stat.S_ISDIR(segment_now.st_mode) or
|
|
not stat.S_ISDIR(root_now.st_mode) or _identity(segment_now) != _identity(segment_info) or
|
|
_identity(root_now) != _identity(root_info) or _closed_rlog(segment_fd) != filename):
|
|
raise LocalLogUnavailable("Local recording identity changed")
|
|
raw = b"".join(chunks)
|
|
_allowed(permitted)
|
|
return ClosedLog(segment_name, RLOGS[filename], raw, hashlib.sha256(raw).hexdigest(), len(raw))
|
|
except (OSError, ValueError) as error:
|
|
raise LocalLogUnavailable("Local recording is unavailable") from error
|
|
finally:
|
|
for fd in reversed(fds):
|
|
os.close(fd)
|