89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
import struct
|
|
|
|
CHUNK = 128 * 512
|
|
LV_START = 5120000 * 512
|
|
BSIZE = 4096
|
|
IPG = 8192
|
|
INODE_SIZE = 256
|
|
|
|
def read_virt(virt_offset, length):
|
|
result = bytearray(length)
|
|
pos = virt_offset
|
|
remaining = length
|
|
with open('/dev/nbd0', 'rb') as f:
|
|
while remaining > 0:
|
|
f.seek(pos)
|
|
chunk = f.read(min(remaining, 65536))
|
|
if not chunk: break
|
|
dst = pos - virt_offset
|
|
result[dst:dst+len(chunk)] = chunk
|
|
pos += len(chunk)
|
|
remaining -= len(chunk)
|
|
return bytes(result)
|
|
|
|
def get_inode_table_block(group):
|
|
# Use backup GDT at group 1
|
|
gdt_start = (1 * 32768 + 1) * BSIZE
|
|
entry = read_virt(gdt_start + group * 64, 64)
|
|
it_lo = struct.unpack_from('<I', entry, 8)[0]
|
|
it_hi = struct.unpack_from('<I', entry, 40)[0]
|
|
return (it_hi << 32) | it_lo
|
|
|
|
def read_inode(inode_num):
|
|
group = (inode_num - 1) // IPG
|
|
index = (inode_num - 1) % IPG
|
|
it_block = get_inode_table_block(group)
|
|
inode_off = it_block * BSIZE + index * INODE_SIZE
|
|
return read_virt(inode_off, INODE_SIZE)
|
|
|
|
def read_extents(inode_data):
|
|
blocks = []
|
|
eh_magic = struct.unpack_from('<H', inode_data, 40)[0]
|
|
if eh_magic != 0xf30a:
|
|
return blocks
|
|
eh_entries = struct.unpack_from('<H', inode_data, 42)[0]
|
|
eh_depth = struct.unpack_from('<H', inode_data, 46)[0]
|
|
if eh_depth == 0:
|
|
for i in range(min(eh_entries, 4)):
|
|
off = 52 + i * 12
|
|
ee_len = struct.unpack_from('<H', inode_data, off+4)[0]
|
|
ee_start_hi = struct.unpack_from('<H', inode_data, off+6)[0]
|
|
ee_start_lo = struct.unpack_from('<I', inode_data, off+8)[0]
|
|
ee_start = (ee_start_hi << 32) | ee_start_lo
|
|
for b in range(ee_len):
|
|
blocks.append(ee_start + b)
|
|
return blocks
|
|
|
|
def list_dir(inode_num):
|
|
inode_data = read_inode(inode_num)
|
|
mode = struct.unpack_from('<H', inode_data, 0)[0]
|
|
size = struct.unpack_from('<I', inode_data, 4)[0]
|
|
links = struct.unpack_from('<H', inode_data, 26)[0]
|
|
print(f'Inode {inode_num}: mode=0x{mode:04x} size={size} links={links}')
|
|
|
|
entries = []
|
|
for block_num in read_extents(inode_data):
|
|
data = read_virt(block_num * BSIZE, BSIZE)
|
|
off = 0
|
|
while off < BSIZE - 8:
|
|
ino = struct.unpack_from('<I', data, off)[0]
|
|
rec_len = struct.unpack_from('<H', data, off+4)[0]
|
|
name_len = data[off+6]
|
|
ftype = data[off+7]
|
|
if rec_len < 8: break
|
|
if ino > 0 and name_len > 0:
|
|
name = data[off+8:off+8+name_len].decode('utf-8',errors='replace')
|
|
grp = (ino-1)//IPG
|
|
entries.append((name, ino, ftype, grp))
|
|
off += rec_len
|
|
|
|
type_names = {1:'file',2:'dir',7:'symlink'}
|
|
print(f'Directory entries ({len(entries)}):')
|
|
for name, ino, ftype, grp in sorted(entries):
|
|
status = 'INTACT' if grp >= 13 else 'LOST'
|
|
tname = type_names.get(ftype, str(ftype))
|
|
print(f' [{status}] {tname:6s} inode={ino:10d} group={grp:6d} {name!r}')
|
|
|
|
# Read the volumes directory
|
|
list_dir(1585918)
|