use disk_backend::Disk;
use disk_backend::DiskError;
use guestmem::GuestMemory;
use scsi_buffers::OwnedRequestBuffers;
use thiserror::Error;
#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
pub(crate) struct VmgsStorage {
disk: Disk,
mem: GuestMemory,
mem_size: usize,
sector_size: u32,
sector_shift: u32,
}
#[derive(Debug, Error)]
pub enum StorageError {
#[error(transparent)]
Disk(DiskError),
#[error("access is not aligned to a sector boundary")]
Unaligned,
}
impl VmgsStorage {
pub fn new(disk: Disk) -> Self {
let sector_size = disk.sector_size();
let mem_size = 64 * 1024;
Self {
mem: GuestMemory::allocate(mem_size),
mem_size,
sector_size,
sector_shift: sector_size.trailing_zeros(),
disk,
}
}
pub async fn read_block(
&mut self,
mut byte_offset: u64,
buf: &mut [u8],
) -> Result<(), StorageError> {
if byte_offset & (self.sector_size as u64 - 1) != 0 {
return Err(StorageError::Unaligned);
}
for buf in buf.chunks_mut(self.mem_size) {
let sector = byte_offset >> self.sector_shift;
let sector_end = (byte_offset + buf.len() as u64 + (self.sector_size as u64 - 1))
>> self.sector_shift;
let len = (sector_end - sector) << self.sector_shift;
let buffers = OwnedRequestBuffers::linear(0, len as usize, true);
self.disk
.read_vectored(&buffers.buffer(&self.mem), sector)
.await
.map_err(StorageError::Disk)?;
self.mem.read_at(0, buf).unwrap();
byte_offset += len;
}
Ok(())
}
pub async fn write_block(
&mut self,
mut byte_offset: u64,
buf: &[u8],
) -> Result<(), StorageError> {
if byte_offset & (self.sector_size as u64 - 1) != 0 {
return Err(StorageError::Unaligned);
}
for buf in buf.chunks(self.mem_size) {
let sector = byte_offset >> self.sector_shift;
let sector_end = (byte_offset + buf.len() as u64 + (self.sector_size as u64 - 1))
>> self.sector_shift;
let len = (sector_end - sector) << self.sector_shift;
self.mem.write_at(0, buf).unwrap();
self.mem
.fill_at(buf.len() as u64, 0, len as usize - buf.len())
.unwrap();
let buffers = OwnedRequestBuffers::linear(0, len as usize, false);
self.disk
.write_vectored(&buffers.buffer(&self.mem), sector, false)
.await
.map_err(StorageError::Disk)?;
byte_offset += len;
}
Ok(())
}
pub async fn flush(&mut self) -> Result<(), StorageError> {
self.disk.sync_cache().await.map_err(StorageError::Disk)
}
pub fn sector_size(&self) -> u32 {
self.sector_size
}
pub fn sector_count(&self) -> u64 {
self.disk.sector_count()
}
pub fn block_capacity(&self) -> u32 {
((self.sector_count() * self.sector_size() as u64)
.min(vmgs_format::VMGS_MAX_CAPACITY_BYTES)
/ vmgs_format::VMGS_BYTES_PER_BLOCK as u64) as u32
}
pub fn aligned_header_size(&self) -> u64 {
assert!(self.sector_size() >= size_of::<vmgs_format::VmgsHeader>() as u32);
self.sector_size().into()
}
}