#[path = "../../../os-partitions-gpt/src/lib.rs"] mod gpt; const DISK_SECTORS: u64 = 2048; struct Fixture { primary_header: [u8; gpt::SECTOR_BYTES], primary_entries: [u8; gpt::ENTRY_ARRAY_BYTES], backup_header: [u8; gpt::SECTOR_BYTES], backup_entries: [u8; gpt::ENTRY_ARRAY_BYTES], } 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 fill_header(header: &mut [u8; gpt::SECTOR_BYTES], here: u64, there: u64, array_lba: u64, array_crc: u32) { header[0..8].copy_from_slice(b"EFI PART"); put32(header, 8, 0x0001_0000); put32(header, 12, 92); put64(header, 24, here); put64(header, 32, there); put64(header, 40, 34); put64(header, 48, 2014); header[56] = 0x99; put64(header, 72, array_lba); put32(header, 80, gpt::ENTRY_COUNT as u32); put32(header, 84, gpt::ENTRY_BYTES as u32); put32(header, 88, array_crc); refresh_header_crc(header); } fn refresh_header_crc(header: &mut [u8; gpt::SECTOR_BYTES]) { put32(header, 16, 0); let crc = gpt::crc32(&header[..92]); put32(header, 16, crc); } fn make_fixture() -> Fixture { let mut fixture = Fixture { primary_header: [0; gpt::SECTOR_BYTES], primary_entries: [0; gpt::ENTRY_ARRAY_BYTES], backup_header: [0; gpt::SECTOR_BYTES], backup_entries: [0; gpt::ENTRY_ARRAY_BYTES] }; let first = &mut fixture.primary_entries[..gpt::ENTRY_BYTES]; first[0] = 0xaa; first[16] = 1; put64(first, 32, 34); put64(first, 40, 100); let third = &mut fixture.primary_entries[2 * gpt::ENTRY_BYTES..3 * gpt::ENTRY_BYTES]; third[0] = 0xbb; third[16] = 2; put64(third, 32, 101); put64(third, 40, 2014); fixture.backup_entries.copy_from_slice(&fixture.primary_entries); let crc = gpt::crc32(&fixture.primary_entries); fill_header(&mut fixture.primary_header, 1, DISK_SECTORS - 1, 2, crc); fill_header(&mut fixture.backup_header, DISK_SECTORS - 1, 1, 2015, crc); fixture } fn mount(state: &mut gpt::MountState, fixture: &Fixture) -> Result { state.mount(&fixture.primary_header, &fixture.primary_entries, &fixture.backup_header, &fixture.backup_entries, DISK_SECTORS) } fn mount_stream(state: &mut gpt::MountState, fixture: &Fixture, reads: &mut usize) -> Result> { state.mount_stream(&fixture.primary_header, &fixture.backup_header, DISK_SECTORS, |lba, out| { let (array, offset) = if (2..34).contains(&lba) { (&fixture.primary_entries[..], (lba - 2) as usize * gpt::SECTOR_BYTES) } else if (2015..2047).contains(&lba) { (&fixture.backup_entries[..], (lba - 2015) as usize * gpt::SECTOR_BYTES) } else { return Err("outside fixture"); }; out.copy_from_slice(&array[offset..offset + gpt::SECTOR_BYTES]); *reads += 1; Ok(()) }) } #[test] fn os_partitions_gpt_positive() { assert_eq!(gpt::crc32(b"123456789"), 0xcbf4_3926); let fixture = make_fixture(); let layout = gpt::inspect_headers(&fixture.primary_header, &fixture.backup_header, DISK_SECTORS).unwrap(); assert_eq!((layout.primary_array_lba, layout.backup_array_lba), (2, 2015)); assert_eq!((layout.first_usable, layout.last_usable), (34, 2014)); let mut state = gpt::MountState::new(); let table = mount(&mut state, &fixture).unwrap(); assert_eq!(table.count, 2); assert_eq!(state.generation, 1); assert_eq!(state.table, Some(table)); assert_eq!(table.entries[0].unwrap().slot, 0); assert_eq!((table.entries[1].unwrap().first_lba, table.entries[1].unwrap().last_lba), (101, 2014)); assert!(table.entries[2..].iter().all(Option::is_none)); let mut stream_state = gpt::MountState::new(); let mut reads = 0; assert_eq!(mount_stream(&mut stream_state, &fixture, &mut reads), Ok(table)); assert_eq!(reads, 64); assert_eq!(stream_state, state); } #[test] fn os_partitions_gpt_negative() { let fixture = make_fixture(); let mut state = gpt::MountState::new(); let healthy = mount(&mut state, &fixture).unwrap(); let baseline = state; let mut bad_crc = make_fixture(); bad_crc.primary_entries[32] ^= 1; assert_eq!(mount(&mut state, &bad_crc), Err(gpt::Error::BadArrayCrc)); assert_eq!(state, baseline); let mut corrupt_copies = make_fixture(); corrupt_copies.primary_entries[32] ^= 1; corrupt_copies.backup_entries[32] ^= 1; let mut reads = 0; assert_eq!(mount_stream(&mut state, &corrupt_copies, &mut reads), Err(gpt::StreamError::Parse(gpt::Error::BadArrayCrc))); assert_eq!(reads, 64); assert_eq!(state, baseline); let mut oversized = make_fixture(); put32(&mut oversized.primary_header, 80, 4096); refresh_header_crc(&mut oversized.primary_header); assert_eq!(gpt::inspect_headers(&oversized.primary_header, &oversized.backup_header, DISK_SECTORS), Err(gpt::Error::UnsupportedLayout)); assert_eq!(mount(&mut state, &oversized), Err(gpt::Error::UnsupportedLayout)); assert_eq!(state, baseline); let mut reads = 0; assert_eq!(mount_stream(&mut state, &oversized, &mut reads), Err(gpt::StreamError::Parse(gpt::Error::UnsupportedLayout))); assert_eq!(reads, 0); assert_eq!(state, baseline); let mut overlapping = make_fixture(); let third = &mut overlapping.primary_entries[2 * gpt::ENTRY_BYTES..3 * gpt::ENTRY_BYTES]; put64(third, 32, 100); overlapping.backup_entries.copy_from_slice(&overlapping.primary_entries); let crc = gpt::crc32(&overlapping.primary_entries); put32(&mut overlapping.primary_header, 88, crc); put32(&mut overlapping.backup_header, 88, crc); refresh_header_crc(&mut overlapping.primary_header); refresh_header_crc(&mut overlapping.backup_header); assert_eq!(mount(&mut state, &overlapping), Err(gpt::Error::Overlap)); assert_eq!(state, baseline); assert_eq!(state.table, Some(healthy)); }