Files
chimera-gfx-Public/tools/phase10dq_inventory_protocol.py
T
Chimera GFX release export a6037502d7
phase0-ci / build-and-audit (push) Successful in 2m14s
Publish Chimera GFX source
2026-09-03 03:27:14 +02:00

34 lines
2.0 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
"""Strict parser for the bounded Phase-1.0DQ inventory stream."""
from __future__ import annotations
from dataclasses import dataclass
import struct
MAGIC=b"CHI10DQ1";FRAME=struct.Struct("<8sIIiiIIQ24s");ENTRY=struct.Struct("<256sIIIIQQq24s")
HEADER=1;TERMINAL=2;ERROR=3;MAX_ENTRIES=256;MAX_WIRE=64+MAX_ENTRIES*320+64
class ProtocolError(ValueError):pass
@dataclass(frozen=True)
class InventoryEntry:name:str;file_type:int;mode:int;size:int;inode:int;mtime:int
def _frame(raw:bytes):
if len(raw)!=FRAME.size:raise ProtocolError("frame size")
value=FRAME.unpack(raw)
if value[0]!=MAGIC or value[1]!=1 or value[8]!=bytes(24):raise ProtocolError("frame identity")
return value
def parse_stream(raw:bytes)->tuple[InventoryEntry,...]:
if not isinstance(raw,bytes) or not 128<=len(raw)<=MAX_WIRE or (len(raw)-128)%ENTRY.size:raise ProtocolError("stream length")
first=_frame(raw[:64]);kind,status,saved,count,reserved,transferred=first[2:8]
if kind==ERROR:raise ProtocolError(f"target error {status}:{saved}")
if kind!=HEADER or status or saved or count or reserved or transferred:raise ProtocolError("header invalid")
entries=[]
for offset in range(64,len(raw)-64,ENTRY.size):
name_raw,name_length,file_type,mode,reserved,size,inode,mtime,padding=ENTRY.unpack(raw[offset:offset+ENTRY.size])
if not 0<name_length<256 or reserved or padding!=bytes(24) or any(name_raw[name_length:]):raise ProtocolError("entry encoding")
try:name=name_raw[:name_length].decode("utf-8")
except UnicodeDecodeError as exc:raise ProtocolError("entry utf8") from exc
if name in {".",".."} or "/" in name or "\\" in name or "\x00" in name:raise ProtocolError("entry name")
entries.append(InventoryEntry(name,file_type,mode,size,inode,mtime))
terminal=_frame(raw[-64:]);kind,status,saved,count,reserved,transferred=terminal[2:8]
if kind!=TERMINAL or status or saved or reserved or count!=len(entries) or transferred!=len(entries)*ENTRY.size:raise ProtocolError("terminal invalid")
return tuple(entries)