#!/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('