import struct CHUNK = 128*512 LV_START = 5120000*512 BSIZE = 4096 BPG = 32768 GDT_ENTRY = 64 NUM_GROUPS = 35728 def is_meta(virt_byte): in_group = virt_byte % (5*CHUNK) return (in_group // CHUNK) == 4 def raw_read(virt_offset, length): 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 if chunk_idx != 4: phys = LV_START + group*4*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) # Build merged GDT: for each group, use whichever of primary/backup # is NOT in a metadata chunk print('Building merged GDT...') primary_start = BSIZE # block 1 backup_start = (1*BPG + 1) * BSIZE # group 1 backup # Read both GDTs in full 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 neither = 0 for g in range(NUM_GROUPS): prim_byte = primary_start + g * GDT_ENTRY backup_byte = backup_start + g * GDT_ENTRY src_off = g * GDT_ENTRY if not is_meta(prim_byte): # Primary is valid - use it merged[src_off:src_off+GDT_ENTRY] = primary_gdt[src_off:src_off+GDT_ENTRY] primary_used += 1 elif not is_meta(backup_byte): # Primary is in metadata chunk - use backup merged[src_off:src_off+GDT_ENTRY] = backup_gdt[src_off:src_off+GDT_ENTRY] backup_used += 1 else: # Both bad - shouldn't happen given our analysis neither += 1 print(f'From primary GDT: {primary_used}') print(f'From backup GDT: {backup_used}') print(f'Neither (error): {neither}') assert neither == 0, 'Unexpected gap in coverage!' # Verify merged GDT looks sane print() print('Sample entries:') for g in [0, 1, 100, 1000, 32699, 35000, 35727]: e = merged[g*GDT_ENTRY:(g+1)*GDT_ENTRY] bb = struct.unpack_from('