import struct

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

# Search for /var directory block by looking for its known contents
# /var should contain: lib, log, cache, spool, tmp, www, etc.
# inode 1310721 with links=15 means 15 entries

# The directory block must contain entry for 'lib' pointing to a valid inode
# Let's search all of md0 for a 4KB block containing 'lib' as a dir entry
# where the parent context suggests it's /var

targets = [b'log', b'cache', b'spool', b'backups', b'mail']

print('Searching for /var directory block...')
print('Looking for block containing multiple /var subdirectory names')

chunk_size = 32*1024*1024
offset = LV_START  # start from filesystem area

found_blocks = {}

with open('/dev/md0','rb') as f:
    f.seek(0,2)
    disk_size = f.tell()
    
    f.seek(offset)
    pos = offset
    while pos < disk_size:
        data = f.read(min(chunk_size, disk_size-pos))
        if not data: break
        
        # Look for blocks containing multiple target strings
        for blk_off in range(0, len(data)-BSIZE, BSIZE):
            block = data[blk_off:blk_off+BSIZE]
            matches = sum(1 for t in targets if t in block)
            if matches >= 2:
                abs_byte = pos + blk_off
                # Verify as directory block
                entries = []
                off = 0
                while off < BSIZE-8:
                    ino     = struct.unpack_from('<I', block, off)[0]
                    rec_len = struct.unpack_from('<H', block, off+4)[0]
                    name_len= block[off+6]
                    ftype   = block[off+7]
                    if rec_len < 8: break
                    if (10 < ino < 500_000_000 and
                        0 < name_len <= rec_len-8 and
                        ftype in (1,2,7) and
                        rec_len % 4 == 0):
                        name = block[off+8:off+8+name_len].decode('utf-8',errors='replace')
                        if name.isprintable():
                            entries.append((ino,name,ftype))
                    off += rec_len
                
                if len(entries) >= 3:
                    abs_block = (abs_byte - LV_START) // BSIZE
                    print(f'Candidate at md0 byte {abs_byte} '
                          f'(block {abs_block}):')
                    for ino,name,ftype in entries[:10]:
                        tname={1:\"file\",2:\"dir\",7:\"link\"}.get(ftype,'?')
                        print(f'  {tname} inode={ino} {name!r}')
                    print()

        pos += chunk_size
        if pos % (10*1024**3) == 0:
            print(f'  Scanned {pos//1024**3}GB...', flush=True)
