Files
ext4recovery/misc_tools/offset_check.py
2026-04-30 11:04:05 +00:00

92 lines
3.2 KiB
Python

CHUNK = 128*512 # 64KB
LV_START = 5120000*512
BSIZE = 4096
# Block 5251104, virtual byte = 5251104 * 4096 = 21508521984
# group=65638, chunk_idx=4, intra=0
# Physical layout: each group of 5 virtual chunks occupies
# 5*CHUNK bytes on the physical disk (md0)
# group 65638 starts at:
group = 65638
group_phys_start = LV_START + group * 5 * CHUNK
print(f'Group {group} physical start: {group_phys_start}')
print()
import struct
with open('/dev/md0','rb') as f:
# Read all 5 chunks of this group
f.seek(group_phys_start)
all_chunks = f.read(5*CHUNK)
for ci in range(5):
chunk = all_chunks[ci*CHUNK:(ci+1)*CHUNK]
nonzero = sum(1 for b in chunk if b != 0)
# Try first 32 bytes as directory
ino = struct.unpack_from('<I', chunk, 0)[0]
rec = struct.unpack_from('<H', chunk, 4)[0]
nl = chunk[6]
ft = chunk[7]
print(f'Chunk {ci}: nonzero={nonzero}/{CHUNK}', end='')
if 10 < ino < 500_000_000 and 8 <= rec <= 256 and 0 < nl < 32 and ft in (1,2,7):
name = chunk[8:8+nl].decode('utf-8',errors='replace')
print(f' POSSIBLE DIR: inode={ino} name={name!r}', end='')
print()
# The data chunks (0-3) map to virtual chunks as follows:
# Our translation: virtual chunk N -> physical chunk N (for N<4)
# So physical chunks 0,1,2,3 = virtual chunks 0,1,2,3
# And physical chunk 4 = the metadata chunk we skip
# But what if our anchor points were right (FAT32 and LV)
# yet the internal mapping within the LV is different?
# What if the PERC uses a different chunk as metadata inside the LV?
# Our two anchors:
# FAT32 VBR at PERC virtual 2048 -> md0 sector 1664
# LV start at PERC virtual 6400000 -> md0 sector 5120000
# These are BEFORE the LV. Inside the LV, could the chunk order differ?
# What if inside the LV the metadata chunk is chunk 0, not chunk 4?
# Test: read chunk 0 of group 65638 as directory
chunk0 = all_chunks[0:CHUNK]
print()
print('Chunk 0 as directory:')
off = 0
while off < BSIZE-8:
ino = struct.unpack_from('<I', chunk0, off)[0]
rec_len = struct.unpack_from('<H', chunk0, off+4)[0]
name_len= chunk0[off+6]
ftype = chunk0[off+7]
if rec_len < 8: break
if ino > 0 and name_len > 0:
name = chunk0[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
# Test each chunk as potential /var directory
print()
for ci in range(5):
chunk = all_chunks[ci*CHUNK:(ci+1)*CHUNK]
off = 0
entries = []
while off < BSIZE-8:
ino = struct.unpack_from('<I', chunk, off)[0]
rec_len = struct.unpack_from('<H', chunk, off+4)[0]
name_len= chunk[off+6]
ftype = chunk[off+7]
if rec_len < 8: break
if 10 < ino < 500_000_000 and name_len > 0 and ftype in (1,2,7):
name = chunk[off+8:off+8+name_len].decode('utf-8',errors='replace')
if name.isprintable():
entries.append((ino, name, ftype))
off += rec_len
if entries:
print(f'Chunk {ci} has {len(entries)} directory entries:')
for ino, name, ftype in entries[:5]:
tname = {1:'file',2:'dir',7:'link'}.get(ftype,'?')
print(f' {tname} inode={ino} {name!r}')