| use gpt; |
| |
| use gpt::disk; |
| use std::collections::BTreeMap; |
| use std::convert::TryFrom; |
| use std::io::{SeekFrom, Write}; |
| use std::path; |
| use tempfile::NamedTempFile; |
| |
| #[test] |
| fn test_gptconfig_empty() { |
| let tempdisk = NamedTempFile::new().expect("failed to create tempfile disk"); |
| let cfg = { |
| let c1 = gpt::GptConfig::new(); |
| let c2 = gpt::GptConfig::default(); |
| assert_eq!(c1, c2); |
| c1 |
| }; |
| |
| let lb_size = disk::LogicalBlockSize::Lb4096; |
| let disk = cfg |
| .initialized(false) |
| .logical_block_size(lb_size) |
| .open(tempdisk.path()) |
| .unwrap(); |
| assert_eq!(*disk.logical_block_size(), lb_size); |
| assert_eq!(disk.primary_header(), None); |
| assert_eq!(disk.backup_header(), None); |
| assert!(disk.partitions().is_empty()); |
| } |
| |
| #[test] |
| fn test_gptdisk_linux_01() { |
| let diskpath = path::Path::new("tests/fixtures/gpt-linux-disk-01.img"); |
| let lb_size = disk::LogicalBlockSize::Lb512; |
| |
| let gdisk = gpt::GptConfig::new().open(diskpath).unwrap(); |
| assert_eq!(*gdisk.logical_block_size(), lb_size); |
| assert!(gdisk.primary_header().is_some()); |
| assert!(gdisk.backup_header().is_some()); |
| assert_eq!(gdisk.partitions().len(), 1); |
| |
| let h1 = gdisk.primary_header().unwrap(); |
| assert_eq!(h1.current_lba, 1); |
| assert_eq!(h1.backup_lba, 95); |
| |
| let h2 = gdisk.backup_header().unwrap(); |
| assert_eq!(h2.current_lba, 95); |
| assert_eq!(h2.backup_lba, 1); |
| |
| let p1 = &gdisk.partitions().get(&1_u32).unwrap(); |
| assert_eq!(p1.name, "primary"); |
| let p1_start = p1.bytes_start(*gdisk.logical_block_size()).unwrap(); |
| assert_eq!(p1_start, 0x22 * 512); |
| let p1_len = p1.bytes_len(*gdisk.logical_block_size()).unwrap(); |
| assert_eq!(p1_len, (0x3E - 0x22) * 512); |
| } |
| |
| #[test] |
| fn test_gptdisk_linux_01_write_fidelity_with_device() { |
| let diskpath = path::Path::new("tests/fixtures/gpt-linux-disk-01.img"); |
| |
| // Assumes that test_gptdisk_linux_01 has passed, no need to check answers. |
| let mut gdisk = gpt::GptConfig::new().open(diskpath).unwrap(); |
| let good_header1 = gdisk.primary_header().unwrap().clone(); |
| let good_header2 = gdisk.backup_header().unwrap().clone(); |
| let good_partitions = gdisk.partitions().clone(); |
| println!("good header1={:?}", good_header1); |
| println!("good header2={:?}", good_header2); |
| println!("good partitions={:#?}", good_partitions); |
| |
| // Test that we can write this test partition table to an in-memory buffer |
| // instead, then load the results and verify they should be the same. |
| let image_size = usize::try_from(std::fs::metadata(diskpath).unwrap().len()).unwrap(); |
| let mem_device = Box::new(std::io::Cursor::new(vec![0u8; image_size])); |
| gdisk.update_disk_device(mem_device, true); |
| let mut mem_device = gdisk.write().unwrap(); |
| |
| // Write this memory buffer to a temp file, and load from the file to verify |
| // that we wrote the data to the memory buffer correctly. |
| let mut tempdisk = NamedTempFile::new().expect("failed to create tempfile disk"); |
| let mut gpt_in_mem = vec![0u8; image_size]; |
| let _ = mem_device.seek(SeekFrom::Start(0)).unwrap(); |
| mem_device.read_exact(&mut gpt_in_mem).unwrap(); |
| tempdisk.write_all(&gpt_in_mem).unwrap(); |
| tempdisk.flush().unwrap(); |
| |
| let gdisk_file = gpt::GptConfig::new().open(tempdisk.path()).unwrap(); |
| println!("file header1={:?}", gdisk_file.primary_header().unwrap()); |
| println!("file header2={:?}", gdisk_file.backup_header().unwrap()); |
| println!("file partitions={:#?}", gdisk_file.partitions()); |
| assert_eq!(gdisk_file.primary_header().unwrap(), &good_header1); |
| assert_eq!(gdisk_file.backup_header().unwrap(), &good_header2); |
| assert_eq!(gdisk_file.partitions().clone(), good_partitions); |
| |
| // Test that if we read it back from this memory buffer, it matches the known good. |
| let gdisk_mem = gpt::GptConfig::new().open_from_device(mem_device).unwrap(); |
| assert_eq!(gdisk_mem.primary_header().unwrap(), &good_header1); |
| assert_eq!(gdisk_mem.backup_header().unwrap(), &good_header2); |
| assert_eq!(gdisk_mem.partitions().clone(), good_partitions); |
| } |
| |
| #[test] |
| fn test_create_simple_on_device() { |
| const TOTAL_BYTES: usize = 1024 * 64; |
| let mut mem_device = Box::new(std::io::Cursor::new(vec![0u8; TOTAL_BYTES])); |
| |
| // Create a protective MBR at LBA0 |
| let mbr = gpt::mbr::ProtectiveMBR::with_lb_size( |
| u32::try_from((TOTAL_BYTES / 512) - 1).unwrap_or(0xFF_FF_FF_FF)); |
| mbr.overwrite_lba0(&mut mem_device).unwrap(); |
| |
| let mut gdisk = gpt::GptConfig::default() |
| .initialized(false) |
| .writable(true) |
| .logical_block_size(disk::LogicalBlockSize::Lb512) |
| .create_from_device(mem_device, None) |
| .unwrap(); |
| // Initialize the headers using a blank partition table |
| gdisk.update_partitions(BTreeMap::<u32, gpt::partition::Partition>::new()).unwrap(); |
| // At this point, gdisk.primary_header() and gdisk.backup_header() are populated... |
| // Add a few partitions to demonstrate how... |
| gdisk.add_partition("test1", 1024 * 12, gpt::partition_types::BASIC, 0).unwrap(); |
| gdisk.add_partition("test2", 1024 * 18, gpt::partition_types::LINUX_FS, 0).unwrap(); |
| let mut mem_device = gdisk.write().unwrap(); |
| mem_device.seek(std::io::SeekFrom::Start(0)).unwrap(); |
| let mut final_bytes = vec![0u8; TOTAL_BYTES]; |
| mem_device.read_exact(&mut final_bytes).unwrap(); |
| } |
| |
| fn t_read_bytes(device: &mut gpt::DiskDeviceObject, offset: u64, bytes: usize) -> Vec<u8> { |
| let mut buf = vec![0u8; bytes]; |
| device.seek(std::io::SeekFrom::Start(offset)).unwrap(); |
| device.read_exact(&mut buf).unwrap(); |
| buf |
| } |
| |
| fn test_helper_gptdisk_write_efi_unused_partition_entries(lb_size: disk::LogicalBlockSize) { |
| // Test that we write zeros to unused areas of the partition array, so that |
| // if we're creating a partition table from scratch (not loading an existing |
| // table and modifying it) it will create a partition array that is UEFI |
| // compliant (has 128 entries) and unused entries are properly initialized |
| // with zeros. |
| |
| let lb_bytes: u64 = lb_size.into(); |
| let lb_bytes_usize = lb_bytes as usize; |
| // protective MBR + GPT header + GPT partition array |
| let header_lbs = 1 + 1 + ((128 * 128) / lb_bytes); |
| assert_eq!((128 * 128) % lb_bytes, 0); |
| let data_lbs = 10; |
| // GPT partition array + GPT header |
| let footer_lbs = ((128 * 128) / lb_bytes) + 1; |
| let total_lbs = header_lbs + data_lbs + footer_lbs; |
| let total_bytes = (total_lbs * lb_bytes) as usize; |
| |
| // Initialize the buffer with all '255' values so we can tell what's been overwritten vs preserved. |
| let mem_device = Box::new(std::io::Cursor::new(vec![255u8; total_bytes])); |
| |
| // Setup a new partition table and add a couple entries to it. |
| let mut gdisk = gpt::GptConfig::default() |
| .initialized(false) |
| .writable(true) |
| .logical_block_size(lb_size) |
| .create_from_device(mem_device, None) |
| .unwrap(); |
| // Initialize the headers using a blank partition table. |
| gdisk.update_partitions(BTreeMap::<u32, gpt::partition::Partition>::new()).unwrap(); |
| |
| let part1_bytes = 3 * lb_bytes; |
| gdisk.add_partition("test1", part1_bytes, gpt::partition_types::BASIC, 0).unwrap(); |
| gdisk.add_partition("test2", (data_lbs * lb_bytes) - part1_bytes, gpt::partition_types::LINUX_FS, 0).unwrap(); |
| |
| // Write out the table and get back the memory buffer so we can validate its contents. |
| let mut mem_device = gdisk.write().unwrap(); |
| // Should NOT have overwritten the MBR (we have to generate a protective MBR explicitly using mbr module) |
| assert_eq!(t_read_bytes(&mut mem_device, 0, lb_bytes_usize), vec![255u8; lb_bytes_usize]); |
| // Should have overwritten the header |
| assert_ne!(t_read_bytes(&mut mem_device, lb_bytes, 92), vec![255u8; 92]); |
| // According to the spec, the rest of the sector containing the header should be zeros. |
| assert_eq!(t_read_bytes(&mut mem_device, lb_bytes + 92, lb_bytes_usize - 92), vec![0u8; lb_bytes_usize - 92]); |
| // The first two partition entries should have been overwritten with non-zero data. |
| let first_two = t_read_bytes(&mut mem_device, 2 * lb_bytes, 128 * 2); |
| assert_ne!(first_two, vec![255u8; 128 * 2]); |
| assert_ne!(first_two, vec![0u8; 128 * 2]); |
| // The remaining entries should have been overwritten with all zeros. |
| assert_eq!(t_read_bytes(&mut mem_device, (2 * lb_bytes) + (128 * 2), 126 * 128), vec![0u8; 126 * 128]); |
| |
| // The data area should be completely undisturbed... |
| let data_bytes = (data_lbs as usize) * lb_bytes_usize; |
| assert_eq!(t_read_bytes(&mut mem_device, header_lbs * lb_bytes, data_bytes), vec![255u8; data_bytes]); |
| |
| // The first two partition entries in the volume footer should have been overwritten with non-zero data. |
| // The remaining entries should have been overwritten with all zeros. |
| let first_two = t_read_bytes(&mut mem_device, (header_lbs + data_lbs) * lb_bytes, 128 * 2); |
| assert_ne!(first_two, vec![255u8; 128 * 2]); |
| assert_ne!(first_two, vec![0u8; 128 * 2]); |
| // The remaining entries should have been overwritten with all zeros. |
| assert_eq!(t_read_bytes(&mut mem_device, (2 * lb_bytes) + (128 * 2), 126 * 128), vec![0u8; 126 * 128]); |
| |
| // Should have overwritten the backup header |
| assert_ne!(t_read_bytes(&mut mem_device, total_bytes as u64 - lb_bytes, 92), vec![255u8; 92]); |
| // Remainder of the sector with the backup header should be all zeros |
| assert_eq!(t_read_bytes(&mut mem_device, total_bytes as u64 - lb_bytes + 92, lb_bytes_usize - 92), vec![0u8; lb_bytes_usize - 92]); |
| } |
| |
| #[test] |
| fn test_gptdisk_write_efi_unused_partition_entries_512() { |
| test_helper_gptdisk_write_efi_unused_partition_entries(disk::LogicalBlockSize::Lb512); |
| } |
| |
| #[test] |
| fn test_gptdisk_write_efi_unused_partition_entries_4096() { |
| test_helper_gptdisk_write_efi_unused_partition_entries(disk::LogicalBlockSize::Lb4096); |
| } |