use std::ffi::c_void;
use std::io;
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
use windows::Wdk::Foundation::OBJECT_ATTRIBUTES;
use windows::Wdk::Storage::FileSystem::{
FILE_OPEN, NTCREATEFILE_CREATE_OPTIONS, NtCancelIoFileEx, NtCreateFile,
};
use windows::Wdk::System::IO::NtDeviceIoControlFile;
use windows::Win32::Foundation::{
HANDLE, NTSTATUS, RtlNtStatusToDosError, STATUS_NOT_FOUND, STATUS_SUCCESS, UNICODE_STRING,
};
use windows::Win32::Storage::FileSystem::{
FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_READ, FILE_SHARE_WRITE, SYNCHRONIZE,
SetFileCompletionNotificationModes,
};
use windows::Win32::System::IO::IO_STATUS_BLOCK;
use windows::core::PWSTR;
use super::abi::{AfdPollInfo, IOCTL_AFD_POLL};
use super::completion_port::CompletionPort;
use crate::Event;
const SKIP_SET_EVENT_ON_HANDLE: u8 = 0x2;
const DEVICE_PATH: &str = r"\Device\Afd\Moirai";
pub(super) fn status_error(status: NTSTATUS) -> io::Error {
let code = unsafe { RtlNtStatusToDosError(status) };
io::Error::from_raw_os_error(code.cast_signed())
}
pub(super) fn started(status: NTSTATUS) -> io::Result<()> {
if status.0.cast_unsigned() >> 30 == SEVERITY_ERROR {
Err(status_error(status))
} else {
Ok(())
}
}
const SEVERITY_ERROR: u32 = 3;
pub(super) fn nt_result(status: NTSTATUS) -> io::Result<()> {
if status.0 >= 0 {
Ok(())
} else {
Err(status_error(status))
}
}
pub(super) fn finished(status: NTSTATUS, readiness: Event) -> io::Result<Event> {
nt_result(status).map(|()| readiness)
}
pub(super) struct AfdDevice(OwnedHandle);
impl AfdDevice {
pub(super) fn open(port: &CompletionPort, key: usize) -> io::Result<Self> {
let mut path: Vec<u16> = DEVICE_PATH.encode_utf16().collect();
let bytes = u16::try_from(path.len() * 2).map_err(|_| io::ErrorKind::InvalidInput)?;
let name = UNICODE_STRING {
Length: bytes,
MaximumLength: bytes,
Buffer: PWSTR(path.as_mut_ptr()),
};
let attributes = OBJECT_ATTRIBUTES {
Length: size_of::<OBJECT_ATTRIBUTES>() as u32,
ObjectName: &raw const name,
..OBJECT_ATTRIBUTES::default()
};
let mut handle = HANDLE::default();
let mut status_block = IO_STATUS_BLOCK::default();
let status = unsafe {
NtCreateFile(
&raw mut handle,
SYNCHRONIZE,
&raw const attributes,
&raw mut status_block,
None,
FILE_FLAGS_AND_ATTRIBUTES(0),
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
NTCREATEFILE_CREATE_OPTIONS(0),
None,
0,
)
};
if status != STATUS_SUCCESS {
return Err(status_error(status));
}
let device = Self(unsafe { OwnedHandle::from_raw_handle(handle.0 as _) });
unsafe { port.bind(handle, key)? };
unsafe { SetFileCompletionNotificationModes(handle, SKIP_SET_EVENT_ON_HANDLE) }
.map_err(io::Error::from)?;
Ok(device)
}
fn raw(&self) -> HANDLE {
HANDLE(self.0.as_raw_handle() as _)
}
pub(super) unsafe fn poll(
&self,
info: *mut AfdPollInfo,
status_block: *mut IO_STATUS_BLOCK,
context: *const c_void,
) -> io::Result<()> {
let status = unsafe {
NtDeviceIoControlFile(
self.raw(),
None,
None,
Some(context),
status_block,
IOCTL_AFD_POLL,
Some(info.cast_const().cast()),
size_of::<AfdPollInfo>() as u32,
Some(info.cast()),
size_of::<AfdPollInfo>() as u32,
)
};
started(status)
}
pub(super) unsafe fn cancel(&self, status_block: *const IO_STATUS_BLOCK) -> io::Result<()> {
let mut cancel_block = IO_STATUS_BLOCK::default();
let status =
unsafe { NtCancelIoFileEx(self.raw(), Some(status_block), &raw mut cancel_block) };
if status == STATUS_SUCCESS || status == STATUS_NOT_FOUND {
Ok(())
} else {
Err(status_error(status))
}
}
}