Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 95 additions & 20 deletions src/us_equity_snapshot_pipelines/tqqq_r1_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

import ctypes
import errno
import fcntl

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard the Unix-only fcntl import

On Windows, where the standard-library fcntl module is unavailable, importing tqqq_r1_snapshot now fails immediately with ModuleNotFoundError. This bypasses the intended fail-closed platform checks and also makes the otherwise portable path-based verifier unavailable; guard the import as the new trusted-package module does.

Useful? React with 👍 / 👎.

import hashlib
import json
import math
import os
import shutil
import stat
import sys
import tempfile
from dataclasses import dataclass
from dataclasses import dataclass, field
from io import BytesIO
from pathlib import Path
from typing import Any
Expand All @@ -36,6 +38,7 @@ class SnapshotValidationError(ValueError):
class SnapshotResult:
output_dir: Path
manifest_sha256: str
verified_members: tuple[tuple[str, bytes], ...] = field(default=(), repr=False, compare=False)


def _sha256(path: Path) -> str:
Expand Down Expand Up @@ -268,27 +271,12 @@ def _has_exact_type(value: object, expected: object) -> bool:
return value == expected


def verify_tqqq_r1_snapshot(
output_dir: str | Path,
def _verify_snapshot_members(
members: dict[str, bytes],
*,
output_dir: Path,
expected_manifest_sha256: str,
) -> SnapshotResult:
output = Path(output_dir)
if output.is_symlink():
_invalid("snapshot root symlink is not allowed")
try:
names = tuple(sorted(path.name for path in output.iterdir())) if output.is_dir() else ()
except OSError as exc:
raise SnapshotValidationError("unable to read snapshot members") from exc
if names != tuple(sorted(OUTPUT_FILENAMES)):
_invalid(f"unexpected output files: {names}")
if any(not (output / name).is_file() or (output / name).is_symlink() for name in OUTPUT_FILENAMES):
_invalid("snapshot members must be regular non-symlink files")
try:
members = {name: (output / name).read_bytes() for name in OUTPUT_FILENAMES}
except OSError as exc:
raise SnapshotValidationError("unable to read snapshot members") from exc

member_hashes = {name: hashlib.sha256(content).hexdigest() for name, content in members.items()}
if type(expected_manifest_sha256) is not str or member_hashes["manifest.json"] != expected_manifest_sha256:
_invalid("trusted manifest hash mismatch")
Expand Down Expand Up @@ -330,7 +318,94 @@ def verify_tqqq_r1_snapshot(
expected_validation = {"valid": True, "row_count": len(prices), "symbols": list(SYMBOLS)}
if not _has_exact_type(validation, expected_validation):
_invalid("invalid validation")
return SnapshotResult(output_dir=output, manifest_sha256=member_hashes["manifest.json"])
return SnapshotResult(
output_dir=output_dir,
manifest_sha256=member_hashes["manifest.json"],
verified_members=tuple(sorted(members.items())),
)


def read_tqqq_r1_snapshot_member_fd(directory_fd: int, name: str) -> bytes:
if name not in OUTPUT_FILENAMES:
_invalid("invalid snapshot member")
try:
member_fd = os.open(name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory_fd)
except OSError as exc:
raise SnapshotValidationError("unable to read snapshot members") from exc
try:
member_stat = os.fstat(member_fd)
if not stat.S_ISREG(member_stat.st_mode):
_invalid("snapshot members must be regular non-symlink files")
chunks: list[bytes] = []
while True:
chunk = os.read(member_fd, 1024 * 1024)
if not chunk:
return b"".join(chunks)
chunks.append(chunk)
except OSError as exc:
raise SnapshotValidationError("unable to read snapshot members") from exc
finally:
os.close(member_fd)


def verify_tqqq_r1_snapshot_fd(
directory_fd: int,
*,
expected_manifest_sha256: str,
) -> SnapshotResult:
"""Verify snapshot members relative to an already-open stable directory."""
if type(directory_fd) is not int or directory_fd < 0:
_invalid("invalid snapshot directory descriptor")
try:
names = tuple(sorted(os.listdir(directory_fd)))
except OSError as exc:
raise SnapshotValidationError("unable to read snapshot members") from exc
if names != tuple(sorted(OUTPUT_FILENAMES)):
_invalid(f"unexpected output files: {names}")
members = {name: read_tqqq_r1_snapshot_member_fd(directory_fd, name) for name in OUTPUT_FILENAMES}
try:
if Path("/proc/self/fd").is_dir():
output_dir = Path(f"/proc/self/fd/{directory_fd}").resolve(strict=True)
elif sys.platform == "darwin":
output_dir = Path(
fcntl.fcntl(directory_fd, fcntl.F_GETPATH, b"\0" * 1024).split(b"\0", 1)[0].decode()
)
else:
_invalid("stable snapshot directory capability is unavailable")
except (OSError, UnicodeDecodeError) as exc:
raise SnapshotValidationError("unable to resolve snapshot directory descriptor") from exc
return _verify_snapshot_members(
members,
output_dir=output_dir,
expected_manifest_sha256=expected_manifest_sha256,
)


def verify_tqqq_r1_snapshot(
output_dir: str | Path,
*,
expected_manifest_sha256: str,
) -> SnapshotResult:
output = Path(output_dir)
if output.is_symlink():
_invalid("snapshot root symlink is not allowed")
try:
names = tuple(sorted(path.name for path in output.iterdir())) if output.is_dir() else ()
except OSError as exc:
raise SnapshotValidationError("unable to read snapshot members") from exc
if names != tuple(sorted(OUTPUT_FILENAMES)):
_invalid(f"unexpected output files: {names}")
if any(not (output / name).is_file() or (output / name).is_symlink() for name in OUTPUT_FILENAMES):
_invalid("snapshot members must be regular non-symlink files")
try:
members = {name: (output / name).read_bytes() for name in OUTPUT_FILENAMES}
except OSError as exc:
raise SnapshotValidationError("unable to read snapshot members") from exc
return _verify_snapshot_members(
members,
output_dir=output,
expected_manifest_sha256=expected_manifest_sha256,
)


def materialize_tqqq_r1_snapshot(
Expand Down
Loading