Update with test
This commit is contained in:
92
build_merged_v2.py
Normal file
92
build_merged_v2.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Build merged GDT using corrected 5-chunk translation formula.
|
||||
|
||||
With the old formula (group*4*CHUNK), chunk_idx=4 blocks were skipped and
|
||||
every address past group 0 was wrong. The correct formula is group*5*CHUNK
|
||||
for all chunk indices 0-4.
|
||||
|
||||
Since all chunks are now readable, we use the primary GDT for everything and
|
||||
fall back to the backup (group 1) only when a primary entry reads as all-zeros
|
||||
(indicating it genuinely landed in a damaged sector).
|
||||
"""
|
||||
import struct
|
||||
|
||||
CHUNK = 128 * 512 # 64 KB
|
||||
LV_START = 5120000 * 512
|
||||
BSIZE = 4096
|
||||
BPG = 32768
|
||||
GDT_ENTRY = 64
|
||||
NUM_GROUPS = 35728
|
||||
|
||||
|
||||
def raw_read(virt_offset, length):
|
||||
"""Correct PERC H710 translation — 5-chunk stride, all chunks readable."""
|
||||
result = bytearray(length)
|
||||
pos = virt_offset
|
||||
remaining = length
|
||||
with open('/dev/md0', 'rb') as f:
|
||||
while remaining > 0:
|
||||
group = pos // (5 * CHUNK)
|
||||
in_group = pos % (5 * CHUNK)
|
||||
chunk_idx = in_group // CHUNK
|
||||
intra = in_group % CHUNK
|
||||
seg_len = min(CHUNK - intra, remaining)
|
||||
dst_off = pos - virt_offset
|
||||
phys = LV_START + group * 5 * CHUNK + chunk_idx * CHUNK + intra
|
||||
f.seek(phys)
|
||||
data = f.read(seg_len)
|
||||
result[dst_off:dst_off + len(data)] = data
|
||||
pos += seg_len
|
||||
remaining -= seg_len
|
||||
return bytes(result)
|
||||
|
||||
|
||||
print('Building merged GDT (v2 — corrected 5-chunk formula)...')
|
||||
primary_start = BSIZE # block 1
|
||||
backup_start = (1 * BPG + 1) * BSIZE # group 1 backup SB location
|
||||
|
||||
primary_gdt = raw_read(primary_start, NUM_GROUPS * GDT_ENTRY)
|
||||
backup_gdt = raw_read(backup_start, NUM_GROUPS * GDT_ENTRY)
|
||||
|
||||
merged = bytearray(NUM_GROUPS * GDT_ENTRY)
|
||||
primary_used = 0
|
||||
backup_used = 0
|
||||
zero_both = 0
|
||||
|
||||
for g in range(NUM_GROUPS):
|
||||
src_off = g * GDT_ENTRY
|
||||
prim_e = primary_gdt[src_off:src_off + GDT_ENTRY]
|
||||
back_e = backup_gdt[src_off:src_off + GDT_ENTRY]
|
||||
|
||||
if any(prim_e):
|
||||
merged[src_off:src_off + GDT_ENTRY] = prim_e
|
||||
primary_used += 1
|
||||
elif any(back_e):
|
||||
merged[src_off:src_off + GDT_ENTRY] = back_e
|
||||
backup_used += 1
|
||||
else:
|
||||
zero_both += 1
|
||||
|
||||
print(f' From primary GDT : {primary_used}')
|
||||
print(f' From backup GDT : {backup_used}')
|
||||
print(f' Both zero (lost) : {zero_both}')
|
||||
|
||||
if zero_both > 0:
|
||||
print(f' WARNING: {zero_both} group descriptors could not be recovered')
|
||||
|
||||
print()
|
||||
print('Sample entries:')
|
||||
for g in [0, 1, 13, 100, 1000, 35000, 35727]:
|
||||
e = merged[g * GDT_ENTRY:(g + 1) * GDT_ENTRY]
|
||||
bb = struct.unpack_from('<I', e, 0)[0]
|
||||
ib = struct.unpack_from('<I', e, 4)[0]
|
||||
it = struct.unpack_from('<I', e, 8)[0]
|
||||
cs = struct.unpack_from('<H', e, 30)[0]
|
||||
src = 'primary' if any(primary_gdt[g*GDT_ENTRY:(g+1)*GDT_ENTRY]) else 'backup'
|
||||
print(f' Group {g:6d}: bb={bb:10d} ib={ib:10d} it={it:10d} '
|
||||
f'csum=0x{cs:04x} [{src}]')
|
||||
|
||||
with open('/tmp/merged_gdt.bin', 'wb') as f:
|
||||
f.write(merged)
|
||||
print(f'\nSaved /tmp/merged_gdt.bin ({len(merged) // 1024} KB)')
|
||||
Reference in New Issue
Block a user