fix(files): preserve SQLite locks during previews

Co-Authored-By: Hermes Agent / OpenAI Codex / gpt-6-sol <noreply@agents.invalid>
(cherry picked from commit 60d879c125177cd4cacdbc840f16c2e048b4199c)
This commit is contained in:
Benjamin PERRY
2026-09-24 13:16:13 +00:00
committed by kshitij
parent fb7eda7416
commit af595ff8a6
4 changed files with 136 additions and 13 deletions

View File

@@ -230,7 +230,15 @@ def offline_file_access(path: Path | str, *, what: str = "read"):
:func:`has_live_connection` and *then* doing raw I/O is a check/use race (a connection opened
in between loses its POSIX locks to the raw ``close()``). Held only for the raw I/O."""
with _live_lock:
if _key(path) in _live_connections:
key = _key(path)
# SQLite locks the main file and its WAL shared-memory sidecar. A raw
# close of either inode cancels this process's POSIX locks, while the
# connection registry is keyed by the main database path.
live = key in _live_connections or any(
key.endswith(suffix) and key[:-len(suffix)] in _live_connections
for suffix in ("-wal", "-shm")
)
if live:
raise LiveConnectionError(
f"Refusing to {what} {path}: a connection to it is still open "
"in this process, and raw file access would cancel that "

View File

@@ -176,11 +176,16 @@ def _fs_regular_file(path: Path) -> tuple[Path, os.stat_result]:
def _fs_read_bytes(target: Path, limit: Optional[int] = None) -> bytes:
"""Read (a prefix of) ``target``; 403/400 on failure."""
from hermes_cli.sqlite_safe_read import LiveConnectionError, offline_file_access
try:
if limit is None:
return target.read_bytes()
with target.open("rb") as handle:
return handle.read(limit)
# Keep admission through close; a raw close cancels this process's SQLite locks.
with offline_file_access(target, what="preview file"):
if limit is None:
return target.read_bytes()
with target.open("rb") as handle:
return handle.read(limit)
except LiveConnectionError as exc:
raise HTTPException(status_code=409, detail=str(exc)) from exc
except PermissionError:
raise HTTPException(status_code=403, detail="File is not readable")
except OSError as exc: