use crate::block_io::{BlockDevice, CallbackDevice, FileDevice};
use crate::error::Error;
fn ext4_to_fs_core_error(e: Error) -> fs_core::Error {
match e {
Error::Io(io) => fs_core::Error::Io(io),
Error::ReadOnly => fs_core::Error::ReadOnly,
other => fs_core::Error::Custom(format!("{other:?}")),
}
}
fn fs_core_to_ext4_error(e: fs_core::Error) -> Error {
match e {
fs_core::Error::Io(io) => Error::Io(io),
fs_core::Error::ReadOnly => Error::ReadOnly,
fs_core::Error::OutOfBounds { .. } => Error::OutOfBounds,
fs_core::Error::ShortRead { offset, want, got } => Error::Io(std::io::Error::other(
format!("short read at {offset}: wanted {want} got {got}"),
)),
fs_core::Error::Custom(s) => Error::Io(std::io::Error::other(s)),
}
}
impl fs_core::BlockRead for FileDevice {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> fs_core::Result<()> {
BlockDevice::read_at(self, offset, buf).map_err(ext4_to_fs_core_error)
}
fn size_bytes(&self) -> u64 {
BlockDevice::size_bytes(self)
}
}
impl fs_core::BlockDevice for FileDevice {
fn write_at(&self, offset: u64, buf: &[u8]) -> fs_core::Result<()> {
if !BlockDevice::is_writable(self) {
return Err(fs_core::Error::ReadOnly);
}
BlockDevice::write_at(self, offset, buf).map_err(ext4_to_fs_core_error)
}
fn flush(&self) -> fs_core::Result<()> {
BlockDevice::flush(self).map_err(ext4_to_fs_core_error)
}
fn is_writable(&self) -> bool {
BlockDevice::is_writable(self)
}
}
impl fs_core::BlockRead for CallbackDevice {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> fs_core::Result<()> {
BlockDevice::read_at(self, offset, buf).map_err(ext4_to_fs_core_error)
}
fn size_bytes(&self) -> u64 {
BlockDevice::size_bytes(self)
}
}
impl fs_core::BlockDevice for CallbackDevice {
fn write_at(&self, offset: u64, buf: &[u8]) -> fs_core::Result<()> {
if !BlockDevice::is_writable(self) {
return Err(fs_core::Error::ReadOnly);
}
BlockDevice::write_at(self, offset, buf).map_err(ext4_to_fs_core_error)
}
fn flush(&self) -> fs_core::Result<()> {
BlockDevice::flush(self).map_err(ext4_to_fs_core_error)
}
fn is_writable(&self) -> bool {
BlockDevice::is_writable(self)
}
}
pub struct CoreDevice<T: fs_core::BlockDevice> {
inner: T,
}
impl<T: fs_core::BlockDevice> CoreDevice<T> {
pub fn new(inner: T) -> Self {
Self { inner }
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T: fs_core::BlockDevice> BlockDevice for CoreDevice<T> {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> crate::error::Result<()> {
fs_core::BlockRead::read_at(&self.inner, offset, buf).map_err(fs_core_to_ext4_error)
}
fn size_bytes(&self) -> u64 {
fs_core::BlockRead::size_bytes(&self.inner)
}
fn write_at(&self, offset: u64, buf: &[u8]) -> crate::error::Result<()> {
fs_core::BlockDevice::write_at(&self.inner, offset, buf).map_err(fs_core_to_ext4_error)
}
fn flush(&self) -> crate::error::Result<()> {
fs_core::BlockDevice::flush(&self.inner).map_err(fs_core_to_ext4_error)
}
fn is_writable(&self) -> bool {
fs_core::BlockDevice::is_writable(&self.inner)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
struct InMemoryFsCore(Mutex<Vec<u8>>);
impl fs_core::BlockRead for InMemoryFsCore {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> fs_core::Result<()> {
let b = self.0.lock().unwrap();
let start = offset as usize;
let end = start + buf.len();
if end > b.len() {
return Err(fs_core::Error::ShortRead {
offset,
want: buf.len(),
got: b.len().saturating_sub(start),
});
}
buf.copy_from_slice(&b[start..end]);
Ok(())
}
fn size_bytes(&self) -> u64 {
self.0.lock().unwrap().len() as u64
}
}
impl fs_core::BlockDevice for InMemoryFsCore {
fn write_at(&self, offset: u64, buf: &[u8]) -> fs_core::Result<()> {
let mut b = self.0.lock().unwrap();
let start = offset as usize;
let end = start + buf.len();
if end > b.len() {
return Err(fs_core::Error::OutOfBounds {
offset,
len: buf.len() as u64,
size: b.len() as u64,
});
}
b[start..end].copy_from_slice(buf);
Ok(())
}
fn is_writable(&self) -> bool {
true
}
}
#[test]
fn core_device_round_trip() {
let mem = InMemoryFsCore(Mutex::new(vec![0u8; 4096]));
let dev = CoreDevice::new(mem);
assert_eq!(BlockDevice::size_bytes(&dev), 4096);
BlockDevice::write_at(&dev, 100, &[0x11, 0x22, 0x33, 0x44]).unwrap();
let mut buf = [0u8; 4];
BlockDevice::read_at(&dev, 100, &mut buf).unwrap();
assert_eq!(buf, [0x11, 0x22, 0x33, 0x44]);
}
#[test]
fn core_device_propagates_short_read_as_oob() {
let mem = InMemoryFsCore(Mutex::new(vec![0u8; 64]));
let dev = CoreDevice::new(mem);
let mut buf = [0u8; 16];
let err = BlockDevice::read_at(&dev, 60, &mut buf).unwrap_err();
assert!(matches!(err, Error::Io(_)), "got: {err:?}");
}
}