use gpt::{disk, GptConfig, GptError};
use std::io::{Cursor, Read, Seek, Write};
use tempfile::NamedTempFile;
#[test]
fn test_gptconfig_empty() {
let mut tempdisk = NamedTempFile::new().expect("failed to create tempfile disk");
tempdisk.write(&[0; 1024 * 64]).unwrap();
let cfg = {
let c1 = GptConfig::new();
let c2 = GptConfig::default();
assert_eq!(c1, c2);
c1
};
let lb_size = disk::LogicalBlockSize::Lb4096;
let disk = cfg
.logical_block_size(lb_size)
.create(tempdisk.path())
.unwrap();
assert_eq!(*disk.logical_block_size(), lb_size);
assert!(disk.primary_header().is_ok());
assert!(disk.backup_header().is_ok());
assert!(disk.partitions().is_empty());
}
#[test]
fn test_create_simple_on_device() {
const TOTAL_BYTES: usize = 1024 * 66;
let mut mem_device = Box::new(std::io::Cursor::new(vec![0_u8; TOTAL_BYTES]));
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 = GptConfig::default()
.writable(true)
.logical_block_size(disk::LogicalBlockSize::Lb512)
.create_from_device(mem_device, None)
.unwrap();
gdisk
.add_partition("test1", 1024 * 12, gpt::partition_types::BASIC, 0, None)
.unwrap();
gdisk
.add_partition("test2", 1024 * 18, gpt::partition_types::LINUX_FS, 0, None)
.unwrap();
let id = gdisk.find_next_partition_id().unwrap();
gdisk
.add_partition_at("test3", id, 94, 2, gpt::partition_types::BASIC, 0)
.unwrap();
let mut mem_device = gdisk.write().unwrap();
mem_device.seek(std::io::SeekFrom::Start(0)).unwrap();
let mut final_bytes = vec![0_u8; TOTAL_BYTES];
mem_device.read_exact(&mut final_bytes).unwrap();
}
fn t_read_bytes<D: gpt::DiskDevice>(device: &mut D, offset: u64, bytes: usize) -> Vec<u8> {
let mut buf = vec![0_u8; bytes];
device.seek(std::io::SeekFrom::Start(offset)).unwrap();
device.read_exact(&mut buf).unwrap();
buf
}
#[test]
fn test_only_valid_headers() {
let mut valid_disk = GptConfig::new()
.writable(true)
.create_from_device(Cursor::new(vec![0; 1024 * 70]), None)
.unwrap();
valid_disk
.add_partition("test1", 1024 * 12, gpt::partition_types::BASIC, 0, None)
.unwrap();
valid_disk
.add_partition("test2", 1024 * 18, gpt::partition_types::LINUX_FS, 0, None)
.unwrap();
let id = valid_disk.find_next_partition_id().unwrap();
valid_disk
.add_partition_at("test3", id, 94, 2, gpt::partition_types::BASIC, 0)
.unwrap();
let valid_disk = valid_disk.write().unwrap();
let mut corrupt_disk = valid_disk.clone();
corrupt_disk.get_mut()[..1024 * 32]
.iter_mut()
.for_each(|v| *v = 0);
let first_try = GptConfig::new()
.only_valid_headers(true)
.open_from_device(&mut corrupt_disk);
assert!(first_try.is_err());
let mut second_try = GptConfig::new()
.writable(true)
.open_from_device(corrupt_disk.clone())
.unwrap();
second_try.write_inplace().unwrap();
assert_eq!(&valid_disk, second_try.device_ref());
assert_eq!(second_try.partitions()[&1].name, "test1");
}
#[test]
fn test_readonly_backup() {
let mut valid_disk = GptConfig::new()
.writable(true)
.create_from_device(Cursor::new(vec![0; 1024 * 70]), None)
.unwrap();
valid_disk
.add_partition("test1", 1024 * 12, gpt::partition_types::BASIC, 0, None)
.unwrap();
valid_disk
.add_partition("test2", 1024 * 18, gpt::partition_types::LINUX_FS, 0, None)
.unwrap();
let id = valid_disk.find_next_partition_id().unwrap();
valid_disk
.add_partition_at("test4", id, 94, 2, gpt::partition_types::BASIC, 0)
.unwrap();
valid_disk.write_inplace().unwrap();
let valid_disk_bytes = valid_disk.device_ref();
let test_disk_bytes = valid_disk_bytes.clone();
let mut test_disk = GptConfig::new()
.writable(true)
.readonly_backup(true)
.open_from_device(test_disk_bytes)
.unwrap();
test_disk
.add_partition("test3", 1024 * 4, gpt::partition_types::LINUX_FS, 0, None)
.unwrap();
test_disk.write_inplace().unwrap();
let test_disk_bytes = test_disk.device_ref();
let primary_end = 512 * (1 + 1 + 32);
assert_ne!(
test_disk_bytes.get_ref()[..primary_end],
valid_disk_bytes.get_ref()[..primary_end]
);
let backup_start = test_disk_bytes.get_ref().len() - 512 * (1 + 32);
assert_eq!(
test_disk_bytes.get_ref()[backup_start..],
valid_disk_bytes.get_ref()[backup_start..]
);
}
#[test]
fn test_change_partition_count() {
let size = 67 + 128 + 1;
let mut valid_disk = GptConfig::new()
.writable(true)
.create_from_device(Cursor::new(vec![0; 512 * size]), None)
.unwrap();
for i in 0..128 {
valid_disk
.add_partition(
&format!("test{i}"),
512,
gpt::partition_types::BASIC,
0,
None,
)
.unwrap();
}
let failed = valid_disk.add_partition(
&format!("test129"),
512,
gpt::partition_types::BASIC,
0,
None,
);
assert!(matches!(failed, Err(GptError::PartitionCountWouldChange)));
valid_disk.write_inplace().unwrap();
let mut big_disk = GptConfig::new()
.writable(true)
.change_partition_count(true)
.create_from_device(Cursor::new(vec![0; 512 * size]), None)
.unwrap();
for i in 0..129 {
big_disk
.add_partition(
&format!("test{i}"),
512,
gpt::partition_types::BASIC,
0,
None,
)
.unwrap();
}
let data = big_disk.write().unwrap();
let mut n_disk = GptConfig::new()
.writable(true)
.open_from_device(data.clone())
.unwrap();
n_disk.write_inplace().unwrap();
assert_eq!(&data, n_disk.device_ref());
n_disk.remove_partition(129);
n_disk.remove_partition(128);
n_disk
.add_partition("test128", 512, gpt::partition_types::BASIC, 0, None)
.unwrap();
n_disk.write_inplace().unwrap();
assert_eq!(n_disk.header().num_parts, 129);
assert_eq!(n_disk.partitions().len(), 128);
assert_ne!(valid_disk.header().num_parts, n_disk.header().num_parts);
}
fn test_helper_gptdisk_write_efi_unused_partition_entries(lb_size: disk::LogicalBlockSize) {
let lb_bytes: u64 = lb_size.into();
let lb_bytes_usize = lb_bytes as usize;
let header_lbs = 1 + 1 + ((128 * 128) / lb_bytes);
assert_eq!((128 * 128) % lb_bytes, 0);
let data_lbs = 10;
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;
let mem_device = Box::new(std::io::Cursor::new(vec![255u8; total_bytes]));
let mut gdisk = GptConfig::default()
.writable(true)
.logical_block_size(lb_size)
.create_from_device(mem_device, None)
.unwrap();
let part1_bytes = 3 * lb_bytes;
gdisk
.add_partition("test1", part1_bytes, gpt::partition_types::BASIC, 0, None)
.unwrap();
gdisk
.add_partition(
"test2",
(data_lbs * lb_bytes) - part1_bytes,
gpt::partition_types::LINUX_FS,
0,
None,
)
.unwrap();
let mut mem_device = gdisk.write().unwrap();
assert_eq!(
t_read_bytes(&mut mem_device, 0, lb_bytes_usize),
vec![255u8; lb_bytes_usize]
);
assert_ne!(t_read_bytes(&mut mem_device, lb_bytes, 92), vec![255u8; 92]);
assert_eq!(
t_read_bytes(&mut mem_device, lb_bytes + 92, lb_bytes_usize - 92),
vec![0_u8; lb_bytes_usize - 92]
);
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![0_u8; 128 * 2]);
assert_eq!(
t_read_bytes(&mut mem_device, (2 * lb_bytes) + (128 * 2), 126 * 128),
vec![0_u8; 126 * 128]
);
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]
);
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![0_u8; 128 * 2]);
assert_eq!(
t_read_bytes(&mut mem_device, (2 * lb_bytes) + (128 * 2), 126 * 128),
vec![0_u8; 126 * 128]
);
assert_ne!(
t_read_bytes(&mut mem_device, total_bytes as u64 - lb_bytes, 92),
vec![255u8; 92]
);
assert_eq!(
t_read_bytes(
&mut mem_device,
total_bytes as u64 - lb_bytes + 92,
lb_bytes_usize - 92
),
vec![0_u8; 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);
}
#[test]
fn test_non_overlapping_partition_edge_case() {
use gpt::{disk, partition_types, GptConfig};
use std::io::Cursor;
let total_bytes = 1024 * 64;
let buf = vec![0u8; total_bytes];
let device = Cursor::new(buf);
let mut gpt_disk = GptConfig::default()
.writable(true)
.logical_block_size(disk::LogicalBlockSize::Lb512)
.create_from_device(device, None)
.expect("failed to create GPT disk in memory");
gpt_disk
.add_partition("edge1", 512, partition_types::BASIC, 0, None)
.expect("failed to add partition edge1");
gpt_disk
.add_partition("edge2", 512, partition_types::BASIC, 0, None)
.expect("failed to add partition edge2");
let parts = gpt_disk.partitions();
let part1 = parts.get(&1).expect("partition 1 missing");
let part2 = parts.get(&2).expect("partition 2 missing");
assert_eq!(
part1.last_lba + 1,
part2.first_lba,
"Partitions overlap: part1 = {:?}, part2 = {:?}",
part1.first_lba..part1.last_lba,
part2.first_lba..part2.last_lba
);
}