mod common;
use common::{
create_partition_entry, create_primary_header, create_secondary_header,
};
use gpt_disk_io::{BlockIo, BlockIoAdapter, Disk};
use gpt_disk_types::{BlockSize, GptPartitionEntryArray};
#[cfg(feature = "std")]
use std::fs::{self, File, OpenOptions};
struct SparseChunk {
offset: usize,
data: [u8; 16],
}
impl SparseChunk {
const fn new(offset: usize, data: [u8; 16]) -> Self {
Self { offset, data }
}
}
#[rustfmt::skip]
const SPARSE_DISK: &'static [SparseChunk] = &[
SparseChunk::new(0x1c0, [2,0,238,130,2,0,1,0,0,0,255,31,0,0,0,0,]),
SparseChunk::new(0x1f0, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,170,]),
SparseChunk::new(0x200, [69,70,73,32,80,65,82,84,0,0,1,0,92,0,0,0,]),
SparseChunk::new(0x210, [67,120,135,164,0,0,0,0,1,0,0,0,0,0,0,0,]),
SparseChunk::new(0x220, [255,31,0,0,0,0,0,0,34,0,0,0,0,0,0,0,]),
SparseChunk::new(0x230, [222,31,0,0,0,0,0,0,182,254,167,87,213,140,34,73,]),
SparseChunk::new(0x240, [183,189,199,139,9,20,232,112,2,0,0,0,0,0,0,0,]),
SparseChunk::new(0x250, [128,0,0,0,128,0,0,0,255,173,6,146,0,0,0,0,]),
SparseChunk::new(0x400, [79,153,240,204,224,247,38,78,160,17,132,62,56,170,46,172,]),
SparseChunk::new(0x410, [253,95,199,55,50,137,122,70,156,86,140,241,240,69,107,18,]),
SparseChunk::new(0x420, [0,8,0,0,0,0,0,0,0,16,0,0,0,0,0,0,]),
SparseChunk::new(0x430, [0,0,0,0,0,0,0,0,104,0,101,0,108,0,108,0,]),
SparseChunk::new(0x440, [111,0,32,0,119,0,111,0,114,0,108,0,100,0,33,0,]),
SparseChunk::new(0x3fbe00, [79,153,240,204,224,247,38,78,160,17,132,62,56,170,46,172,]),
SparseChunk::new(0x3fbe10, [253,95,199,55,50,137,122,70,156,86,140,241,240,69,107,18,]),
SparseChunk::new(0x3fbe20, [0,8,0,0,0,0,0,0,0,16,0,0,0,0,0,0,]),
SparseChunk::new(0x3fbe30, [0,0,0,0,0,0,0,0,104,0,101,0,108,0,108,0,]),
SparseChunk::new(0x3fbe40, [111,0,32,0,119,0,111,0,114,0,108,0,100,0,33,0,]),
SparseChunk::new(0x3ffe00, [69,70,73,32,80,65,82,84,0,0,1,0,92,0,0,0,]),
SparseChunk::new(0x3ffe10, [19,76,235,219,0,0,0,0,255,31,0,0,0,0,0,0,]),
SparseChunk::new(0x3ffe20, [1,0,0,0,0,0,0,0,34,0,0,0,0,0,0,0,]),
SparseChunk::new(0x3ffe30, [222,31,0,0,0,0,0,0,182,254,167,87,213,140,34,73,]),
SparseChunk::new(0x3ffe40, [183,189,199,139,9,20,232,112,223,31,0,0,0,0,0,0,]),
SparseChunk::new(0x3ffe50, [128,0,0,0,128,0,0,0,255,173,6,146,0,0,0,0,]),
];
fn load_test_disk() -> Vec<u8> {
let mut disk = vec![0; 4 * 1024 * 1024];
for chunk in SPARSE_DISK {
let end = chunk.offset + chunk.data.len();
disk[chunk.offset..end].copy_from_slice(&chunk.data);
}
disk
}
fn test_disk_read<Io>(block_io: Io)
where
Io: BlockIo,
{
let bs = BlockSize::BS_512;
let mut block_buf = vec![0u8; bs.to_usize().unwrap()];
let mut disk = Disk::new(block_io).unwrap();
let primary_header = disk.read_primary_gpt_header(&mut block_buf).unwrap();
assert_eq!(primary_header, create_primary_header());
let secondary_header =
disk.read_secondary_gpt_header(&mut block_buf).unwrap();
assert_eq!(secondary_header, create_secondary_header());
let expected_partition_entry = create_partition_entry();
let check_partition_entry_array = |disk: &mut Disk<Io>, layout| {
{
let mut block_buf = vec![0u8; bs.to_usize().unwrap()];
let mut iter = disk
.gpt_partition_entry_array_iter(layout, &mut block_buf)
.unwrap();
let entry = iter.next().unwrap().unwrap();
assert_eq!(entry, expected_partition_entry);
assert!(entry.is_used());
let entry = iter.next().unwrap().unwrap();
assert!(!entry.is_used());
}
let mut array_buf = vec![0u8; bs.to_usize().unwrap() * 34];
let array = disk
.read_gpt_partition_entry_array(layout, &mut array_buf)
.unwrap();
let entry = *array.get_partition_entry(0).unwrap();
assert_eq!(entry, expected_partition_entry);
assert!(entry.is_used());
let entry = *array.get_partition_entry(1).unwrap();
assert!(!entry.is_used());
};
check_partition_entry_array(
&mut disk,
primary_header.get_partition_entry_array_layout().unwrap(),
);
check_partition_entry_array(
&mut disk,
secondary_header.get_partition_entry_array_layout().unwrap(),
);
}
fn test_disk_write<Io>(block_io: Io)
where
Io: BlockIo,
{
let bs = BlockSize::BS_512;
let mut block_buf = vec![0u8; bs.to_usize().unwrap()];
let mut disk = Disk::new(block_io).unwrap();
let primary_header = create_primary_header();
let secondary_header = create_secondary_header();
let partition_entry = create_partition_entry();
disk.write_protective_mbr(&mut block_buf).unwrap();
disk.write_primary_gpt_header(&primary_header, &mut block_buf)
.unwrap();
disk.write_secondary_gpt_header(&secondary_header, &mut block_buf)
.unwrap();
let layout = primary_header.get_partition_entry_array_layout().unwrap();
let mut bytes =
vec![0; layout.num_bytes_rounded_to_block_as_usize(bs).unwrap()];
let mut entry_array =
GptPartitionEntryArray::new(layout, bs, &mut bytes).unwrap();
*entry_array.get_partition_entry_mut(0).unwrap() = partition_entry;
disk.write_gpt_partition_entry_array(&entry_array).unwrap();
entry_array.set_start_lba(secondary_header.partition_entry_lba.into());
disk.write_gpt_partition_entry_array(&entry_array).unwrap();
disk.flush().unwrap();
}
fn test_with_slice(test_disk: &[u8]) {
test_disk_read(BlockIoAdapter::new(test_disk, BlockSize::BS_512));
}
fn test_with_mut_slice(test_disk: &[u8]) {
let mut contents = test_disk.to_vec();
test_disk_read(BlockIoAdapter::new(
contents.as_mut_slice(),
BlockSize::BS_512,
));
let mut new_contents = vec![0; contents.len()];
test_disk_write(BlockIoAdapter::new(
new_contents.as_mut_slice(),
BlockSize::BS_512,
));
assert_eq!(contents, new_contents);
}
#[cfg(feature = "std")]
fn test_with_file(test_disk: &[u8]) {
let path = "tmp_test_disk_file.bin";
fs::write(path, test_disk).unwrap();
let test_disk_file = File::open(path).unwrap();
test_disk_read(BlockIoAdapter::new(test_disk_file, BlockSize::BS_512));
fs::remove_file(path).unwrap();
fs::write(path, vec![0; 4 * 1024 * 1024]).unwrap();
let new_disk_file = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.unwrap();
test_disk_write(BlockIoAdapter::new(new_disk_file, BlockSize::BS_512));
assert_eq!(fs::read(path).unwrap(), test_disk);
fs::remove_file(path).unwrap();
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_disk() {
let test_disk = load_test_disk();
test_with_slice(&test_disk);
test_with_mut_slice(&test_disk);
#[cfg(feature = "std")]
test_with_file(&test_disk);
}