pub mod block_io;
pub mod block_io2;
use alloc::boxed::Box;
use alloc::vec::Vec;
use vck_common::{VckResult, VolumeCipherSupplier};
use crate::provider::HookGeometry;
pub struct BlockIoHookEngine {
geometry: HookGeometry,
cipher_supplier: Box<dyn VolumeCipherSupplier>,
block_io: Option<block_io::BlockIoHook>,
block_io2: Option<block_io2::BlockIo2Hook>,
}
impl BlockIoHookEngine {
pub fn new(
geometry: HookGeometry,
cipher_supplier: Box<dyn VolumeCipherSupplier>,
) -> VckResult<Self> {
Ok(Self {
geometry,
cipher_supplier,
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(vck_common::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 mut cipher = match self.cipher_supplier.get_cipher() {
Some(c) => c,
None => 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) {
cipher.decrypt_sector(rel, sector);
}
}
cipher.destroy();
Ok(())
}
pub(crate) fn encrypt_before_write(
&self,
lba: u64,
sector_size: usize,
buf: &[u8],
) -> VckResult<Vec<u8>> {
let mut out = Vec::from(buf);
if sector_size == 0 {
return Ok(out);
}
let mut cipher = match self.cipher_supplier.get_cipher() {
Some(c) => c,
None => return Ok(out),
};
let offset_sector = self.geometry.offset_sector;
let total = self.geometry.encrypted_offset.total_sectors;
for (i, sector) in out.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) {
cipher.encrypt_sector(rel, sector);
}
}
cipher.destroy();
Ok(out)
}
}