pub mod block_io;
pub mod block_io2;
use alloc::boxed::Box;
use vck_common::{VckResult, VolumeCipher};
use crate::provider::HookGeometry;
pub struct BlockIoHookEngine {
geometry: HookGeometry,
cipher: Box<dyn VolumeCipher>,
block_io: Option<block_io::BlockIoHook>,
block_io2: Option<block_io2::BlockIo2Hook>,
}
impl BlockIoHookEngine {
pub fn new(geometry: HookGeometry, cipher: Box<dyn VolumeCipher>) -> VckResult<Self> {
Ok(Self {
geometry,
cipher,
block_io: None,
block_io2: None,
})
}
pub fn install(&mut self) -> VckResult<()> {
use alloc::format;
use uefi::boot::{self, open_protocol_exclusive, SearchType};
use uefi::proto::media::block::BlockIO;
use uefi::proto::media::partition::PartitionInfo;
use vck_common::types::guid_from_windows_bytes;
use vck_common::VckError;
let target = self.geometry.partition_guid;
let engine_ptr: *const BlockIoHookEngine = self;
let handles = boot::locate_handle_buffer(SearchType::from_proto::<BlockIO>())
.map_err(|e| VckError::Io(format!("locate BlockIO handles failed: {e:?}")))?;
for &handle in handles.iter() {
let matched = match open_protocol_exclusive::<PartitionInfo>(handle) {
Ok(pinfo) => pinfo
.gpt_partition_entry()
.map(|gpt| {
guid_from_windows_bytes(gpt.unique_partition_guid.to_bytes()) == target
})
.unwrap_or(false),
Err(_) => false,
};
if !matched {
continue;
}
let mut scoped = open_protocol_exclusive::<BlockIO>(handle)
.map_err(|e| VckError::Io(format!("open BlockIO for hook failed: {e:?}")))?;
let proto = scoped
.get_mut()
.ok_or(VckError::Io(alloc::string::String::from(
"BlockIO interface is null",
)))? as *mut BlockIO
as *mut uefi_raw::protocol::block::BlockIoProtocol;
self.block_io = Some(block_io::BlockIoHook::install(proto, engine_ptr)?);
core::mem::forget(scoped);
return Ok(());
}
Err(VckError::NotFound(
"no Block IO partition matched the target GUID for hooking",
))
}
pub fn uninstall(&mut self) -> VckResult<()> {
if let Some(hook) = self.block_io.take() {
hook.uninstall()?;
}
if let Some(hook) = self.block_io2.take() {
hook.uninstall()?;
}
Ok(())
}
pub(crate) fn decrypt_after_read(
&self,
lba: u64,
sector_size: usize,
buf: &mut [u8],
) -> VckResult<()> {
if sector_size == 0 {
return Ok(());
}
let offset_sector = self.geometry.offset_sector;
let total = self.geometry.encrypted_offset.total_sectors;
for (i, sector) in buf.chunks_mut(sector_size).enumerate() {
let abs = lba + i as u64;
let Some(rel) = abs.checked_sub(offset_sector) else {
continue;
};
if rel >= total {
continue;
}
if self.geometry.encrypted_offset.is_encrypted(rel) {
self.cipher.decrypt_sector(rel, sector);
}
}
Ok(())
}
}