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_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
    print(f'  inode {inode_num}: group={group} idx={idx}')
    print(f'  inode table block: {it_block}')
    print(f'  virtual byte: {virt}')
    phys = phys_from_virt(virt)
    print(f'  physical byte: {phys}')
    if phys is None:
        print(f'  IN METADATA CHUNK')
        return None
    with open('/dev/sda','rb') as f:
        f.seek(phys)
        return f.read(INODE_SZ)

# Check known good inode - volumes directory
print('=== Inode 1585918 (volumes dir) ===')
data = read_inode(1585918)
if data:
    mode  = struct.unpack_from('<H', data,  0)[0]
    size  = struct.unpack_from('<I', data,  4)[0]
    links = struct.unpack_from('<H', data, 26)[0]
    mtime = struct.unpack_from('<I', data, 16)[0]
    ext_magic = struct.unpack_from('<H', data, 40)[0]
    print(f'  mode=0x{mode:04x} size={size} links={links}')
    print(f'  mtime={mtime} ext_magic=0x{ext_magic:04x}')
    print(f'  valid dir: {(mode&0xf000)==0x4000 and ext_magic==0xf30a}')

# Now check inode 2 via NBD (which applies translation correctly)
print()
print('=== Inode 2 via NBD device ===')
group    = (2-1)//IPG
idx      = (2-1)%IPG
it_block = 1070 + group*512
virt     = it_block*BSIZE + idx*INODE_SZ
print(f'  virtual byte: {virt}')
with open('/dev/nbd0','rb') as f:
    f.seek(virt)
    data = f.read(INODE_SZ)
mode  = struct.unpack_from('<H', data,  0)[0]
links = struct.unpack_from('<H', data, 26)[0]
mtime = struct.unpack_from('<I', data, 16)[0]
ext_magic = struct.unpack_from('<H', data, 40)[0]
print(f'  mode=0x{mode:04x} links={links} mtime={mtime}')
print(f'  ext_magic=0x{ext_magic:04x}')
print(f'  first 32 bytes: {data[:32].hex()}')

# Compare: what does debugfs think inode 2 contains?
print()
print('=== What istat says about inode 2 ===')
import subprocess
r = subprocess.run(['istat', '/dev/nbd0', '2'],
                  capture_output=True, text=True)
print(r.stdout[:500])
print(r.stderr[:200])
