DF-2620 / df2620_dump.py
#!/usr/bin/env python3 """DF-2620: dump PFS inodes under the hammer2 super-root (sanity walker).""" import struct, sys RADIX_MASK = 0x3F BREF = 128 INODE_DATA_BLOCKSET = 0x200 T_EMPTY, T_INODE, T_DATA, T_DIRENT, T_INDIRECT = 0, 1, 3, 4, 2 def bref_parse(buf, off): t, methods = struct.unpack_from('<2B', buf, off) key, mtid, modtid, doff, utid = struct.unpack_from('<QQQQQ', buf, off + 8) return dict(type=t, methods=methods, key=key, data_off=doff, off=off, modtid=modtid) def main(): img = bytearray(open(sys.argv[1], 'rb').read()) volhdr = img[0:0x10000] assert struct.unpack_from('<Q', volhdr, 0)[0] == 0x48414D3205172011, "bad magic" sroot0 = None for i in range(4): br = bref_parse(volhdr, 0x200 + i * BREF) if br['data_off']: sroot0 = br break sroot_blk = sroot0['data_off'] & ~RADIX_MASK print("[walk] volhdr sroot bref @volhdr+%#x type=%d methods=%#x doff=%#x -> blk @%#x" % (sroot0['off'], sroot0['type'], sroot0['methods'], sroot0['data_off'], sroot_blk)) # walk sroot inode's direct blockset + indirects for INODE children def walk_indirect(blk, radix, depth): nslots = min((1 << radix) // BREF, 1024) for i in range(nslots): boff = blk + i * BREF br = bref_parse(img, boff) if br['type'] == T_INODE and (br['data_off'] & RADIX_MASK): dump_child(br, "ind@%#x[%d]" % (blk, i)) elif br['type'] == T_INDIRECT and (br['data_off'] & RADIX_MASK) and depth < 4: walk_indirect(br['data_off'] & ~RADIX_MASK, br['data_off'] & RADIX_MASK, depth + 1) def dump_child(br, where): iblk = br['data_off'] & ~RADIX_MASK inum = struct.unpack_from('<Q', img, iblk + 0x58)[0] name_len = struct.unpack_from('<H', img, iblk + 0x80)[0] nmast = img[iblk + 0x86] ptype = img[iblk + 0x87] fname = bytes(img[iblk + 0x100:iblk + 0x100 + max(name_len, 1)]).split(b'\0')[0] print("[pfs ] %s: bref@%#x type=%d methods=%#x doff=%#x | inum=%#x name=%r pfs_type=%d pfs_nmasters=%d" % (where, br['off'], br['type'], br['methods'], br['data_off'], inum, fname, ptype, nmast)) for i in range(4): boff = sroot_blk + INODE_DATA_BLOCKSET + i * BREF br = bref_parse(img, boff) if br['type'] == T_INODE and (br['data_off'] & RADIX_MASK): dump_child(br, "sroot.direct[%d]" % i) elif br['type'] == T_INDIRECT and (br['data_off'] & RADIX_MASK): walk_indirect(br['data_off'] & ~RADIX_MASK, br['data_off'] & RADIX_MASK, 1) if __name__ == '__main__': main() |