Initial remote commit

This commit is contained in:
2026-04-30 11:04:05 +00:00
commit b86e4f9a98
103 changed files with 262770 additions and 0 deletions

88
test/inspect.py Normal file
View File

@@ -0,0 +1,88 @@
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)