DF-2747 / parse_stream.py
#!/usr/bin/env python3 """Parse a DragonFly journal raw-record stream (vfs_journal.c format). A race-free stream is a contiguous chain of records with begmagic 0x1234 and strictly increasing transids (each stream record +2, pads interleave at +1: see journal_reserve()/journal_build_pad()). Reports: record count, monotonicity violations (transid went backwards/equal => concurrent unsynchronized reserve), forged/garbage headers, and chain breaks (gap before EOF).""" import struct, sys BEG, PAD_SID = 0x1234, (0x8000 | 0x4000 | 0x0001) data = open(sys.argv[1], 'rb').read() off = recs = pads = mono_viol = 0 last_tid = None first_break = None tids = {} while off + 16 <= len(data): beg, sid = struct.unpack_from('<HH', data, off) rs, = struct.unpack_from('<i', data, off + 4) tid, = struct.unpack_from('<q', data, off + 8) if beg != BEG: first_break = (off, beg, rs) break step = (rs + 15) & ~15 if rs >= 0 else -1 if step < 16 or step > (128 << 20): print(f"FATAL: bogus recsize {rs:#x} at off {off:#x} sid {sid:#x} tid {tid:#x}") first_break = (off, beg, rs) break if sid == PAD_SID: pads += 1 # 16-byte pads overlay their trailer on the transid field: # journal.h says the pad transid MUST be ignored. if rs != 16 and last_tid is not None and tid <= last_tid: mono_viol += 1 print(f"MONO VIOLATION (pad>=32): off {off:#x} tid {tid:#x} <= {last_tid:#x}") else: tids[tid] = tids.get(tid, 0) + 1 if sid != PAD_SID and last_tid is not None and tid <= last_tid: mono_viol += 1 if mono_viol <= 8: print(f"MONO VIOLATION #{mono_viol}: off {off:#x} tid {tid:#x} " f"<= previous {last_tid:#x} (sid {sid:#x} recsize {rs})") last_tid = tid off += step recs += 1 dupes = {t: c for t, c in tids.items() if c > 1} print(f"stream={sys.argv[1]} bytes={len(data)} records={recs} pads={pads}") print(f"chain_offset_end={off:#x} eof={len(data):#x} " f"chain_break={'YES at ' + hex(first_break[0]) + f' beg={first_break[1]:#x} rs={first_break[2]}' if first_break else 'no'}") print(f"transid_monotonic_violations={mono_viol}") print(f"duplicate_transids={len(dupes)}" + (f" sample={list(dupes.items())[:5]}" if dupes else "")) |