Files
RedBear-OS/storage/driver-block/src/lib.rs
T

160 lines
5.4 KiB
Rust

use std::cmp;
use std::io::Error;
use std::io::{self, Read, Seek, SeekFrom};
use partitionlib::{LogicalBlockSize, PartitionTable};
/// Split the read operation into a series of block reads.
/// `read_fn` will be called with a block number to be read, and a buffer to be filled.
/// `read_fn` must return a full block of data.
/// Result will be the number of bytes read.
// FIXME make private once nvmed uses the DiskWrapper defined in this crate
pub fn block_read(
offset: u64,
blksize: u32,
buf: &mut [u8],
mut read_fn: impl FnMut(u64, &mut [u8]) -> Result<(), Error>,
) -> Result<usize, Error> {
// TODO: Yield sometimes, perhaps after a few blocks or something.
if buf.len() == 0 {
return Ok(0);
}
let to_copy = usize::try_from(
offset.saturating_add(u64::try_from(buf.len()).expect("buf.len() larger than u64"))
- offset,
)
.expect("bytes to copy larger than usize");
let mut curr_buf = &mut buf[..to_copy];
let mut curr_offset = offset;
let blk_size = usize::try_from(blksize).expect("blksize larger than usize");
let mut total_read = 0;
let mut block_bytes = [0u8; 4096];
let block_bytes = &mut block_bytes[..blk_size];
while curr_buf.len() > 0 {
// TODO: Async/await? I mean, shouldn't AHCI be async?
let blk_offset =
usize::try_from(curr_offset % u64::from(blksize)).expect("usize smaller than blksize");
let to_copy = cmp::min(curr_buf.len(), blk_size - blk_offset);
assert!(blk_offset + to_copy <= blk_size);
read_fn(curr_offset / u64::from(blksize), block_bytes)?;
let src_buf = &block_bytes[blk_offset..];
curr_buf[..to_copy].copy_from_slice(&src_buf[..to_copy]);
curr_buf = &mut curr_buf[to_copy..];
curr_offset += u64::try_from(to_copy).expect("bytes to copy larger than u64");
total_read += to_copy;
}
Ok(total_read)
}
pub trait Disk {
fn id(&self) -> usize;
fn block_length(&mut self) -> syscall::error::Result<u32>;
fn size(&mut self) -> u64;
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>>;
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>>;
}
pub struct DiskWrapper {
pub disk: Box<dyn Disk>,
pub pt: Option<PartitionTable>,
}
impl DiskWrapper {
fn pt(disk: &mut dyn Disk) -> Option<PartitionTable> {
let bs = match disk.block_length() {
Ok(512) => LogicalBlockSize::Lb512,
_ => return None,
};
struct Device<'a> {
disk: &'a mut dyn Disk,
offset: u64,
}
impl<'a> Seek for Device<'a> {
fn seek(&mut self, from: SeekFrom) -> io::Result<u64> {
let size = i64::try_from(self.disk.size()).or(Err(io::Error::new(
io::ErrorKind::Other,
"Disk larger than 2^63 - 1 bytes",
)))?;
self.offset = match from {
SeekFrom::Start(new_pos) => cmp::min(self.disk.size(), new_pos),
SeekFrom::Current(new_pos) => {
cmp::max(0, cmp::min(size, self.offset as i64 + new_pos)) as u64
}
SeekFrom::End(new_pos) => cmp::max(0, cmp::min(size + new_pos, size)) as u64,
};
Ok(self.offset)
}
}
// TODO: Perhaps this impl should be used in the rest of the scheme.
impl<'a> Read for Device<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let blksize = self
.disk
.block_length()
.map_err(|err| io::Error::from_raw_os_error(err.errno))?;
let size_in_blocks = self.disk.size() / u64::from(blksize);
let disk = &mut self.disk;
let read_block = |block: u64, block_bytes: &mut [u8]| {
if block >= size_in_blocks {
return Err(io::Error::from_raw_os_error(syscall::EOVERFLOW));
}
loop {
match disk.read(block, block_bytes) {
Ok(Some(bytes)) => {
assert_eq!(bytes, block_bytes.len());
return Ok(());
}
Ok(None) => {
std::thread::yield_now();
continue;
}
Err(err) => return Err(io::Error::from_raw_os_error(err.errno)),
}
}
};
let bytes_read = block_read(self.offset, blksize, buf, read_block)?;
self.offset += bytes_read as u64;
Ok(bytes_read)
}
}
partitionlib::get_partitions(&mut Device { disk, offset: 0 }, bs)
.ok()
.flatten()
}
pub fn new(mut disk: Box<dyn Disk>) -> Self {
Self {
pt: Self::pt(&mut *disk),
disk,
}
}
}
impl std::ops::Deref for DiskWrapper {
type Target = dyn Disk;
fn deref(&self) -> &Self::Target {
&*self.disk
}
}
impl std::ops::DerefMut for DiskWrapper {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.disk
}
}