#[path = "../../../os-partitions-gpt/src/lib.rs"] mod gpt; const DISK_SECTORS: u64 = 2048; fn put32(bytes: &mut [u8], offset: usize, value: u32) { bytes[offset..offset + 4].copy_from_slice(&value.to_le_bytes()); } fn put64(bytes: &mut [u8], offset: usize, value: u64) { bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes()); } fn refresh_crc(header: &mut [u8; 512]) { put32(header, 16, 0); let crc = gpt::crc32(&header[..92]); put32(header, 16, crc); } struct Fixture { primary: [u8; 512], backup: [u8; 512], primary_entries: [u8; gpt::ENTRY_ARRAY_BYTES], backup_entries: [u8; gpt::ENTRY_ARRAY_BYTES], } fn header(here: u64, alternate: u64, array_lba: u64, array_crc: u32) -> [u8; 512] { let mut bytes = [0u8; 512]; bytes[..8].copy_from_slice(b"EFI PART"); put32(&mut bytes, 8, 0x10000); put32(&mut bytes, 12, 92); put64(&mut bytes, 24, here); put64(&mut bytes, 32, alternate); put64(&mut bytes, 40, 34); put64(&mut bytes, 48, 2014); bytes[56] = 0x99; put64(&mut bytes, 72, array_lba); put32(&mut bytes, 80, gpt::ENTRY_COUNT as u32); put32(&mut bytes, 84, gpt::ENTRY_BYTES as u32); put32(&mut bytes, 88, array_crc); refresh_crc(&mut bytes); bytes } fn fixture() -> Fixture { let mut primary_entries = [0u8; gpt::ENTRY_ARRAY_BYTES]; primary_entries[0] = 0xaa; primary_entries[16] = 1; put64(&mut primary_entries, 32, 34); put64(&mut primary_entries, 40, 100); primary_entries[2 * 128] = 0xbb; primary_entries[2 * 128 + 16] = 2; put64(&mut primary_entries, 2 * 128 + 32, 101); put64(&mut primary_entries, 2 * 128 + 40, 2014); let backup_entries = primary_entries; let crc = gpt::crc32(&primary_entries); Fixture {primary: header(1, 2047, 2, crc), backup: header(2047, 1, 2015, crc), primary_entries, backup_entries} } fn mount(state: &mut gpt::RecoveryState, fixture: &Fixture, reads: &mut usize) -> Result> { state.mount_stream(&fixture.primary, &fixture.backup, DISK_SECTORS, |lba, destination| { let (array, offset) = if (2..34).contains(&lba) { (&fixture.primary_entries[..], (lba - 2) as usize * 512) } else if (2015..2047).contains(&lba) { (&fixture.backup_entries[..], (lba - 2015) as usize * 512) } else { return Err("out of range"); }; destination.copy_from_slice(&array[offset..offset + 512]); *reads += 1; Ok(()) }) } #[test] fn os_partitions_recovery_positive() { let base = fixture(); let mut state = gpt::RecoveryState::new(); let mut reads = 0; let healthy = mount(&mut state, &base, &mut reads).unwrap(); assert_eq!(reads, 64); assert_eq!(healthy.status, gpt::RecoveryStatus::Healthy); assert_eq!(healthy.table.count, 2); assert_eq!(state.generation, 1); let mut degraded_fixture = fixture(); degraded_fixture.primary[56] ^= 1; reads = 0; let degraded = mount(&mut state, °raded_fixture, &mut reads).unwrap(); assert_eq!(reads, 32); assert_eq!(degraded.table, healthy.table); assert_eq!(degraded.status, gpt::RecoveryStatus::DegradedBackup {primary_error: gpt::Error::BadHeaderCrc}); assert_eq!(state.result, Some(degraded)); assert_eq!(state.generation, 2); } #[test] fn os_partitions_recovery_negative() { let base = fixture(); let mut state = gpt::RecoveryState::new(); let mut reads = 0; let healthy = mount(&mut state, &base, &mut reads).unwrap(); let baseline = state; let mut mismatch = fixture(); mismatch.primary[56] = 0x98; refresh_crc(&mut mismatch.primary); reads = 0; assert_eq!(mount(&mut state, &mismatch, &mut reads), Err(gpt::StreamError::Parse(gpt::Error::InconsistentCopies))); assert_eq!(reads, 0); assert_eq!(state, baseline); let mut bad_backup = fixture(); bad_backup.primary[56] ^= 1; bad_backup.backup_entries[32] ^= 1; reads = 0; assert_eq!(mount(&mut state, &bad_backup, &mut reads), Err(gpt::StreamError::Parse(gpt::Error::BadArrayCrc))); assert_eq!(reads, 32); assert_eq!(state, baseline); assert_eq!(state.result, Some(healthy)); let mut fresh = gpt::RecoveryState::new(); let mut both_bad = fixture(); both_bad.primary[56] ^= 1; both_bad.backup[56] ^= 1; reads = 0; assert_eq!(mount(&mut fresh, &both_bad, &mut reads), Err(gpt::StreamError::Parse(gpt::Error::NoValidCopy))); assert_eq!(reads, 0); assert_eq!(fresh, gpt::RecoveryState::new()); }