// Copyright 2023, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! This module provides an implementation of [BlockIo] backed by RAM. use crate::{is_aligned, is_buffer_aligned, BlockInfo, BlockIo, SliceMaybeUninit}; use core::ops::DerefMut; use gbl_async::yield_now; use liberror::Error; use safemath::SafeNum; /// `RamBlockIo` implements [BlockIo] backed by user provided buffer. pub struct RamBlockIo { /// The storage block size in bytes. pub block_size: u64, /// The storage access alignment in bytes. pub alignment: u64, /// The backing storage data. pub storage: T, /// The number of successful write calls. pub num_writes: usize, /// The number of successful read calls. pub num_reads: usize, /// Injected error to be returned by the next read/write IO. pub error: Option, } impl> RamBlockIo { /// Creates an new instance. pub fn new(block_size: u64, alignment: u64, storage: T) -> Self { assert_eq!( storage.len() % usize::try_from(block_size).unwrap(), 0, "storage size is not multiple of block size, {}, {}", storage.len(), block_size ); Self { block_size, alignment, storage, num_writes: 0, num_reads: 0, error: None } } /// Gets the underlying ramdisk storage. pub fn storage(&mut self) -> &mut [u8] { &mut self.storage[..] } /// Checks injected error, simulates async waiting, checks read/write parameters and returns the /// offset in number of bytes. async fn checks( &mut self, blk_offset: u64, buf: &(impl SliceMaybeUninit + ?Sized), ) -> Result { assert!(is_buffer_aligned(buf.as_ref(), self.alignment).unwrap_or(false)); assert!(is_aligned(buf.len(), self.block_size).unwrap_or(false)); yield_now().await; self.error.take().map(|e| Err(e)).unwrap_or(Ok(()))?; Ok((SafeNum::from(blk_offset) * self.block_size).try_into().unwrap()) } } // SAFETY: // `read_blocks` clones `out.len()` bytes to output which initializes all elements in `out` unsafe impl> BlockIo for RamBlockIo { fn info(&mut self) -> BlockInfo { BlockInfo { block_size: self.block_size, num_blocks: u64::try_from(self.storage.len()).unwrap() / self.block_size, alignment: self.alignment, } } async fn read_blocks( &mut self, blk_offset: u64, out: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<(), Error> { let offset = self.checks(blk_offset, out).await?; let out_len = out.len(); Ok(out.clone_from_slice(&self.storage[offset..][..out_len])) } async fn write_blocks(&mut self, blk_offset: u64, data: &mut [u8]) -> Result<(), Error> { let offset = self.checks(blk_offset, &mut *data).await?; Ok(self.storage[offset..][..data.len()].clone_from_slice(data)) } }