Files
verstack/plugins/spike_extract.py
Verstack Local 841a95c56a Complete native workbench, imports, signatures, and catalog management
Save the tested release workflows, RAM media processing, program discovery, FLIRT library, and shared-object maintenance. Add Git exclusions, file attributes, and instructions for a later push using a forwarded SSH agent.
2026-09-13 16:38:57 -05:00

57 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Adapter for a pinned local bdash/spike-spk executable; wrapper coverage is partial."""
import hashlib
import json
import os
import pathlib
import re
import subprocess
import sys
from parallelism import worker_count
from progress import report
def run(request):
if request["protocol"] != 1:
raise ValueError("unsupported protocol")
settings = request["settings"]
tool = pathlib.Path(settings["tool"]).resolve()
with tool.open("rb") as stream:
tool_hash = hashlib.file_digest(stream, "sha256").hexdigest()
if tool_hash != settings["expected_sha256"]:
raise ValueError("SPK tool build differs from pinned expected_sha256")
source = pathlib.Path(request["input_dir"])
output = pathlib.Path(request["output_dir"])
env = os.environ.copy()
limit=settings.get('max_threads',0)
if not isinstance(limit,int) or limit<0:raise ValueError('max_threads must be a nonnegative integer (0 selects automatic sizing)')
threads=worker_count(request,limit or 1_000_000)
env["RAYON_NUM_THREADS"] = str(threads)
candidates = [p for p in sorted(source.rglob("*")) if p.is_file() and not p.is_symlink()
and (p.name.lower().endswith(".spk") or re.search(r"\.spk\.\d{3}\.000$", p.name, re.I))]
if not candidates:
raise ValueError("no SPK or first split-package part found")
packages = []
for index, path in enumerate(candidates):
# Let the upstream parser validate actual structure; never assume generation
# or architecture from extension, package name, or release metadata.
destination = output / f"package-{index:04d}"
destination.mkdir()
report('Extracting SPK package',None,None,'files',f'{path.name} · up to {threads} threads',force=True)
# Extraction verifies all checksums itself; avoid reading every payload twice.
subprocess.run([str(tool), "extract", str(path), "--output", str(destination)], check=True, env=env)
packages.append({"source": path.relative_to(source).as_posix(), "generation": "unknown", "tool_sha256": tool_hash})
# Symlinks need portable metadata rather than links the host could traverse.
links = []
for path in sorted(output.rglob("*")):
if path.is_symlink():
links.append({"path": path.relative_to(output).as_posix(), "target": str(path.readlink())})
path.unlink()
(output / "package-evidence.json").write_text(json.dumps({"schema": 1, "packages": packages, "symlinks": links}))
files = [p.relative_to(output).as_posix() for p in sorted(output.rglob("*")) if p.is_file()]
return {"protocol": 1, "layer": "extracted", "coverage": "partial", "files": files,
"warnings": ["Generation not inferred. Nested asset containers remain opaque; SPIKE 3 LUKS wrappers need a separate decoder. Original inputs retained."]}
if __name__ == "__main__":
request = json.loads(pathlib.Path(sys.argv[1]).read_text())
pathlib.Path(request["result_file"]).write_text(json.dumps(run(request)))