use core::ptr::null_mut;
use core::sync::atomic::{AtomicPtr, Ordering};
use alloc::boxed::Box;
use alloc::sync::Arc;
use vck_common::{
types::Guid, EncryptedOffset, EncryptedOffsetStore, SectorIo, VckResult, VolumeCipher, VolumeId,
};
use wdk_sys::{
ntddk::{PsDereferencePrimaryToken, PsReferencePrimaryToken, SeTokenIsAdmin},
PACCESS_TOKEN, PEPROCESS,
};
pub trait VolumeProvider: Send + Sync + 'static {
fn on_attach(&self, ctx: &AttachContext<'_>) -> VckResult<IoConfig>;
fn on_detach(&self, ctx: &DetachContext<'_>) -> VckResult<()> {
let _ = ctx;
Ok(())
}
}
pub enum IoConfig {
Passthrough,
Encrypted {
cipher: Option<Box<dyn VolumeCipher>>,
offset_sector: u64,
encrypted_offset: EncryptedOffset,
offset_store: Arc<dyn EncryptedOffsetStore>,
},
Custom {
io_hooks: Arc<dyn IoHooks>,
offset_sector: u64,
encrypted_offset: EncryptedOffset,
offset_store: Arc<dyn EncryptedOffsetStore>,
},
}
impl IoConfig {
pub fn offset_sector(&self) -> u64 {
match self {
IoConfig::Passthrough => 0,
IoConfig::Encrypted { offset_sector, .. } | IoConfig::Custom { offset_sector, .. } => {
*offset_sector
}
}
}
pub fn geometry(&self) -> Option<(u64, EncryptedOffset, Arc<dyn EncryptedOffsetStore>)> {
match self {
IoConfig::Passthrough => None,
IoConfig::Encrypted {
offset_sector,
encrypted_offset,
offset_store,
..
}
| IoConfig::Custom {
offset_sector,
encrypted_offset,
offset_store,
..
} => Some((
*offset_sector,
encrypted_offset.clone(),
offset_store.clone(),
)),
}
}
}
pub trait IoHooks: Send + Sync + 'static {
fn read(&self, sector: u64, buf: &mut [u8]) -> VckResult<()>;
fn write(&self, sector: u64, buf: &[u8]) -> VckResult<()>;
}
pub struct AttachContext<'a> {
pub io: Arc<dyn SectorIo>,
pub vmk: &'a [u8],
pub partition_guid: Option<Guid>,
}
pub struct DetachContext<'a> {
pub volume_id: &'a VolumeId,
}
static GLOBAL_PROVIDER: AtomicPtr<()> = AtomicPtr::new(null_mut());
pub fn set_volume_provider(provider: &'static dyn VolumeProvider) {
let boxed: Box<&'static dyn VolumeProvider> = Box::new(provider);
GLOBAL_PROVIDER.store(Box::into_raw(boxed).cast(), Ordering::Release);
}
pub fn global_volume_provider() -> Option<&'static dyn VolumeProvider> {
let ptr = GLOBAL_PROVIDER.load(Ordering::Acquire);
if ptr.is_null() {
None
} else {
Some(*unsafe { &*ptr.cast::<&'static dyn VolumeProvider>() })
}
}
pub trait IoctlAuthorization: Send + Sync + 'static {
fn authorize(&self, ctx: &IoctlAuthContext<'_>) -> VckResult<()>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequestorMode {
Kernel,
User,
}
pub struct AccessToken {
token: PACCESS_TOKEN,
owned: bool,
}
impl AccessToken {
pub unsafe fn from_process(process: PEPROCESS) -> Option<Self> {
if process.is_null() {
return None;
}
let token = PsReferencePrimaryToken(process);
if token.is_null() {
return None;
}
Some(Self { token, owned: true })
}
pub unsafe fn from_raw(token: PACCESS_TOKEN) -> Self {
Self {
token,
owned: false,
}
}
pub fn as_raw(&self) -> PACCESS_TOKEN {
self.token
}
pub fn is_admin(&self) -> bool {
unsafe { SeTokenIsAdmin(self.token) != 0 }
}
}
impl Drop for AccessToken {
fn drop(&mut self) {
if self.owned && !self.token.is_null() {
unsafe { PsDereferencePrimaryToken(self.token) };
}
}
}
pub struct IoctlAuthContext<'a> {
pub ioctl_code: u32,
pub requestor_mode: RequestorMode,
pub requestor_token: Option<&'a AccessToken>,
}