CHUNK    = 128*512
LV_START = 5120000*512
BSIZE    = 4096

# Block 5251104 falls in:
# virt_byte = 5251104 * 4096 = 21508521984
# group=65638, chunk_idx=4, intra=0

# The physical location of this metadata chunk:
group = 65638
phys_meta = LV_START + group*5*CHUNK + 4*CHUNK
print(f'Metadata chunk physical byte: {phys_meta}')

# Read the full 64KB metadata chunk
with open('/dev/md0','rb') as f:
    f.seek(phys_meta)
    meta = f.read(CHUNK)

nonzero = sum(1 for b in meta if b != 0)
print(f'Nonzero bytes: {nonzero}/{CHUNK}')
print()

# Earlier we found that metadata chunks store 512 bytes per sector
# of the 4 adjacent data chunks = 128 sectors * 4 chunks = 512 entries
# But what if SOME of those 512-byte slots contain filesystem data
# instead of PERC metadata?

# The /var dir block should be 4096 bytes
# Could it be stored in 8 consecutive 512-byte slots?
# Let's look for directory signatures in the metadata chunk

import struct

# Search for directory entry signature: valid inode + rec_len + name_len
print('Searching for directory entries in metadata chunk...')
for off in range(0, CHUNK-8, 4):
    ino     = struct.unpack_from('<I', meta, off)[0]
    rec_len = struct.unpack_from('<H', meta, off+4)[0]
    name_len= meta[off+6]
    ftype   = meta[off+7]
    if (10 < ino < 500_000_000 and
        8 <= rec_len <= 256 and
        0 < name_len <= rec_len-8 and
        ftype in (1,2,7) and
        rec_len % 4 == 0):
        name = meta[off+8:off+8+name_len].decode('utf-8',errors='replace')
        if name.isprintable() and len(name) == name_len:
            print(f'  offset {off}: inode={ino} rec_len={rec_len} '
                  f'name={name!r} ftype={ftype}')

# Also check: does the metadata chunk contain the filesystem data
# at a fixed offset? e.g. first 4096 bytes = filesystem data block?
print()
print('First 4096 bytes as directory:')
off = 0
while off < 4096-8:
    ino     = struct.unpack_from('<I', meta, off)[0]
    rec_len = struct.unpack_from('<H', meta, off+4)[0]
    name_len= meta[off+6]
    ftype   = meta[off+7]
    if rec_len < 8: break
    if ino > 0 and name_len > 0:
        name = meta[off+8:off+8+name_len].decode('utf-8',errors='replace')
        tname = {1:'file',2:'dir',7:'link'}.get(ftype,'?')
        print(f'  {tname} inode={ino} {name!r}')
    off += rec_len
