import struct

CHUNK    = 128*512
LV_START = 5120000*512
BSIZE    = 4096
IPG      = 8192
INODE_SZ = 256

def phys_from_virt(virt):
    grp      = virt//(5*CHUNK)
    in_group = virt%(5*CHUNK)
    chunk_idx= in_group//CHUNK
    intra    = in_group%CHUNK
    if chunk_idx == 4: return None
    return LV_START + grp*4*CHUNK + chunk_idx*CHUNK + intra

def read_phys(phys, length):
    with open('/dev/sda','rb') as f:
        f.seek(phys)
        return f.read(length)

def read_virt(virt, length):
    phys = phys_from_virt(virt)
    if phys is None: return b'\x00'*length
    return read_phys(phys, length)

def read_inode(inode_num):
    group    = (inode_num-1)//IPG
    idx      = (inode_num-1)%IPG
    it_block = 1070 + group*512
    virt     = it_block*BSIZE + idx*INODE_SZ
    phys     = phys_from_virt(virt)
    if phys is None: return None
    return read_phys(phys, INODE_SZ)

def get_extents(inode_data):
    blocks = []
    if struct.unpack_from('<H', inode_data, 40)[0] != 0xf30a:
        return blocks
    entries = struct.unpack_from('<H', inode_data, 42)[0]
    depth   = struct.unpack_from('<H', inode_data, 46)[0]
    if depth == 0:
        for i in range(min(entries,4)):
            off = 52+i*12
            ee_len = struct.unpack_from('<H', inode_data, off+4)[0]
            ee_hi  = struct.unpack_from('<H', inode_data, off+6)[0]
            ee_lo  = struct.unpack_from('<I', inode_data, off+8)[0]
            start  = (ee_hi<<32)|ee_lo
            for b in range(min(ee_len,8)):
                blocks.append(start+b)
    return blocks

def list_dir(inode_num):
    data = read_inode(inode_num)
    if not data: return []
    entries = []
    for blk in get_extents(data):
        blk_data = read_virt(blk*BSIZE, BSIZE)
        off = 0
        while off < BSIZE-8:
            ino     = struct.unpack_from('<I', blk_data, off)[0]
            rec_len = struct.unpack_from('<H', blk_data, off+4)[0]
            name_len= blk_data[off+6]
            ftype   = blk_data[off+7]
            if rec_len < 8: break
            if ino > 0 and name_len > 0:
                name = blk_data[off+8:off+8+name_len].decode('utf-8',errors='replace')
                entries.append((ino, name, ftype))
            off += rec_len
    return entries

# List root directory
print('Root directory (inode 2):')
for ino, name, ftype in list_dir(2):
    tname = {1:'file',2:'dir',7:'link'}.get(ftype,'?')
    print(f'  {tname:4s} {name:20s} inode={ino}')

# Find var
print()
for ino, name, ftype in list_dir(2):
    if name == 'var':
        print(f'Found /var at inode {ino}')
        print('Contents of /var:')
        for i2, n2, f2 in list_dir(ino):
            tname = {1:'file',2:'dir',7:'link'}.get(f2,'?')
            print(f'  {tname:4s} {n2:20s} inode={i2}')
        
        # Find var/lib
        for i2, n2, f2 in list_dir(ino):
            if n2 == 'lib':
                print(f'\nFound /var/lib at inode {i2}')
                print('Contents of /var/lib:')
                for i3, n3, f3 in list_dir(i2):
                    tname = {1:'file',2:'dir',7:'link'}.get(f3,'?')
                    print(f'  {tname:4s} {n3:20s} inode={i3}')
