blob: 477d1a34390066cabf2fda7de7d507201770f8e8 [file] [log] [blame]
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);
}