use std::fs::OpenOptions;
use std::io::{Read, Seek, SeekFrom};
use std::time::{Duration, Instant};
use io_buffers::AlignedBuf;
use super::QUEUE;
use crate::dioring::{DeliveryMode, Dioring, Setup, TargetSetup};
use crate::scratch_volume::ScratchDisk;
use crate::windows_backend::{
DiskPath, OpenedDisk, Unbuffered, Win32FileTarget, disk_flush_domain,
};
use durable_ioring::backend::RefusalCause;
use durable_ioring::{
DurableRing, Entry, EntryOf, IoTarget, IoTargetKey, LineageHandle, Outcome, PushRefusal,
WarrantyClass, WriteCaching, WriteOptions,
};
const DISK: IoTargetKey = IoTargetKey(1);
const BOUND: Duration = Duration::from_secs(30);
const MEGABYTES: u32 = 64;
type DiskRing = Dioring<AlignedBuf, u64, u32>;
fn raw_instance(disk: OpenedDisk<Unbuffered>) -> (DiskRing, LineageHandle) {
DiskRing::new(Setup {
submission_queue_size: QUEUE,
completion_queue_size: QUEUE,
files: vec![TargetSetup {
key: DISK,
target: Win32FileTarget::from_disk(disk),
}],
buffers: Vec::<Vec<u8>>::new(),
provider: None,
diagnostics: None,
delivery: DeliveryMode::Pool(None),
})
.expect("build an instance over the raw disk")
}
fn next_entry(ring: &mut DiskRing) -> EntryOf<DiskRing> {
let started = Instant::now();
loop {
if let Some(entry) = ring.pop().expect("pop") {
return entry;
}
assert!(started.elapsed() < BOUND, "nothing arrived");
std::thread::sleep(Duration::from_millis(1));
}
}
fn sector_of(fill: u8, sector: usize) -> AlignedBuf {
AlignedBuf::from_bytes(&vec![fill; sector], sector).expect("an aligned buffer")
}
fn outcome(entry: EntryOf<DiskRing>) -> Outcome<crate::WindowsIds<u64>> {
match entry {
Entry::Op(completion) => completion.outcome,
other => panic!("expected a completion, got {other:?}"),
}
}
fn read_back(disk_number: u32, offset: u64, length: usize) -> Vec<u8> {
let mut disk = OpenOptions::new()
.read(true)
.open(DiskPath::number(disk_number).path())
.expect("open the disk for reading");
disk.seek(SeekFrom::Start(offset)).expect("seek");
let mut bytes = vec![0; length];
disk.read_exact(&mut bytes).expect("read the disk");
bytes
}
fn open(scratch: &ScratchDisk) -> OpenedDisk<Unbuffered> {
OpenedDisk::<Unbuffered>::open(DiskPath::number(scratch.disk_number()))
.expect("open the scratch disk")
}
#[test]
#[ignore = "needs an administrator: creates and attaches a virtual disk"]
fn a_raw_disks_sector_is_found_and_its_target_names_the_disk() {
let scratch = ScratchDisk::create(MEGABYTES).expect("a scratch disk");
let disk = open(&scratch);
assert_eq!(disk.disk_number(), scratch.disk_number());
let sector = disk.geometry().offset_alignment();
assert!(sector.is_power_of_two() && sector >= 512, "{sector}");
assert_eq!(disk.geometry().length_alignment(), sector);
assert_eq!(disk.geometry().buffer_alignment(), sector);
let target = Win32FileTarget::from_disk(disk);
assert_eq!(target.warranty(), WarrantyClass::SharedDevice);
assert_eq!(
target.flush_domains(),
[disk_flush_domain(scratch.disk_number())]
);
assert!(target.capabilities().write_through());
}
#[test]
#[ignore = "needs an administrator: creates and attaches a virtual disk"]
fn sectors_written_to_a_raw_disk_are_made_durable_and_are_on_the_disk() {
let scratch = ScratchDisk::create(MEGABYTES).expect("a scratch disk");
let disk = open(&scratch);
let sector = disk.geometry().offset_alignment() as usize;
let (mut ring, lineage) = raw_instance(disk);
let at = [0, 1024 * 1024];
for (context, offset) in at.into_iter().enumerate() {
ring.write_with(
DISK,
offset,
sector_of(0xC0 + context as u8, sector),
lineage.at(1),
context as u32,
WriteOptions::new(),
)
.expect("push an aligned write");
}
for _ in at {
assert!(
matches!(outcome(next_entry(&mut ring)), Outcome::Transferred(n) if n as usize == sector)
);
}
ring.make_durable_through(lineage.at(1))
.expect("seal the epoch");
match next_entry(&mut ring) {
Entry::Durable { through } => assert_eq!(through.id, 1),
other => panic!("expected Durable, got {other:?}"),
}
for (context, offset) in at.into_iter().enumerate() {
let on_disk = read_back(scratch.disk_number(), offset, sector);
assert!(
on_disk.iter().all(|&b| b == 0xC0 + context as u8),
"the sector at {offset}"
);
}
assert!(
read_back(scratch.disk_number(), sector as u64, sector)
.iter()
.all(|&b| b == 0)
);
}
#[test]
#[ignore = "needs an administrator: creates and attaches a virtual disk"]
fn the_last_sector_of_a_raw_disk_is_accepted_and_the_one_past_it_is_not() {
let scratch = ScratchDisk::create(MEGABYTES).expect("a scratch disk");
let disk = open(&scratch);
let sector = disk.geometry().offset_alignment() as usize;
let size = scratch.bytes();
let last = size - sector as u64;
let (mut ring, lineage) = raw_instance(disk);
ring.write_with(
DISK,
last,
sector_of(0x77, sector),
lineage.at(1),
1,
WriteOptions::new(),
)
.expect("push the last sector");
assert!(
matches!(outcome(next_entry(&mut ring)), Outcome::Transferred(n) if n as usize == sector),
"the last sector of the disk"
);
assert!(
read_back(scratch.disk_number(), last, sector)
.iter()
.all(|&b| b == 0x77)
);
ring.write_with(
DISK,
size,
sector_of(0x88, sector),
lineage.at(1),
2,
WriteOptions::new(),
)
.expect("the backend cannot know the disk's size, so it pushes it");
let past = outcome(next_entry(&mut ring));
assert!(
!matches!(past, Outcome::Transferred(n) if n > 0),
"a write past the end of the disk transferred: {past:?}"
);
}
#[test]
#[ignore = "needs an administrator: creates and attaches a virtual disk"]
fn an_operation_off_the_sector_is_refused_before_the_disk_is_asked() {
let scratch = ScratchDisk::create(MEGABYTES).expect("a scratch disk");
let disk = open(&scratch);
let sector = disk.geometry().offset_alignment() as usize;
let (mut ring, lineage) = raw_instance(disk);
for offset in [1, sector as u64 / 2, sector as u64 + 1] {
let refused = ring
.write(DISK, offset, sector_of(1, sector), lineage.at(1), 3)
.expect_err("off the sector");
assert!(
matches!(refused.reason, PushRefusal::Backend(RefusalCause::Invalid)),
"offset {offset}"
);
}
let short = AlignedBuf::from_bytes(&vec![0; sector / 2], sector).expect("aligned, but short");
let refused = ring
.write(DISK, 0, short, lineage.at(1), 4)
.expect_err("a length off the sector");
assert!(matches!(
refused.reason,
PushRefusal::Backend(RefusalCause::Invalid)
));
assert!(
read_back(scratch.disk_number(), 0, sector)
.iter()
.all(|&b| b == 0)
);
}
#[test]
#[ignore = "needs an administrator: creates and attaches a virtual disk"]
fn a_write_through_write_completes_on_a_raw_disk() {
let scratch = ScratchDisk::create(MEGABYTES).expect("a scratch disk");
let disk = open(&scratch);
let sector = disk.geometry().offset_alignment() as usize;
let (mut ring, lineage) = raw_instance(disk);
ring.write_with(
DISK,
0,
sector_of(0x5A, sector),
lineage.at(1),
5,
WriteOptions::new().caching(WriteCaching::WriteThrough),
)
.expect("push a write-through write");
assert!(
matches!(outcome(next_entry(&mut ring)), Outcome::Transferred(n) if n as usize == sector)
);
assert!(
read_back(scratch.disk_number(), 0, sector)
.iter()
.all(|&b| b == 0x5A)
);
}