fs_ext4/
fs_core_bridge.rs1use crate::block_io::{BlockDevice, CallbackDevice, FileDevice};
24use crate::error::Error;
25
26fn ext4_to_fs_core_error(e: Error) -> fs_core::Error {
35 match e {
36 Error::Io(io) => fs_core::Error::Io(io),
37 Error::ReadOnly => fs_core::Error::ReadOnly,
38 other => fs_core::Error::Custom(format!("{other:?}")),
39 }
40}
41
42fn fs_core_to_ext4_error(e: fs_core::Error) -> Error {
46 match e {
47 fs_core::Error::Io(io) => Error::Io(io),
48 fs_core::Error::ReadOnly => Error::ReadOnly,
49 fs_core::Error::OutOfBounds { .. } => Error::OutOfBounds,
50 fs_core::Error::ShortRead { offset, want, got } => Error::Io(std::io::Error::other(
51 format!("short read at {offset}: wanted {want} got {got}"),
52 )),
53 fs_core::Error::Custom(s) => Error::Io(std::io::Error::other(s)),
54 }
55}
56
57impl fs_core::BlockRead for FileDevice {
62 fn read_at(&self, offset: u64, buf: &mut [u8]) -> fs_core::Result<()> {
63 BlockDevice::read_at(self, offset, buf).map_err(ext4_to_fs_core_error)
64 }
65 fn size_bytes(&self) -> u64 {
66 BlockDevice::size_bytes(self)
67 }
68}
69
70impl fs_core::BlockDevice for FileDevice {
71 fn write_at(&self, offset: u64, buf: &[u8]) -> fs_core::Result<()> {
72 if !BlockDevice::is_writable(self) {
78 return Err(fs_core::Error::ReadOnly);
79 }
80 BlockDevice::write_at(self, offset, buf).map_err(ext4_to_fs_core_error)
81 }
82 fn flush(&self) -> fs_core::Result<()> {
83 BlockDevice::flush(self).map_err(ext4_to_fs_core_error)
84 }
85 fn is_writable(&self) -> bool {
86 BlockDevice::is_writable(self)
87 }
88}
89
90impl fs_core::BlockRead for CallbackDevice {
91 fn read_at(&self, offset: u64, buf: &mut [u8]) -> fs_core::Result<()> {
92 BlockDevice::read_at(self, offset, buf).map_err(ext4_to_fs_core_error)
93 }
94 fn size_bytes(&self) -> u64 {
95 BlockDevice::size_bytes(self)
96 }
97}
98
99impl fs_core::BlockDevice for CallbackDevice {
100 fn write_at(&self, offset: u64, buf: &[u8]) -> fs_core::Result<()> {
101 if !BlockDevice::is_writable(self) {
107 return Err(fs_core::Error::ReadOnly);
108 }
109 BlockDevice::write_at(self, offset, buf).map_err(ext4_to_fs_core_error)
110 }
111 fn flush(&self) -> fs_core::Result<()> {
112 BlockDevice::flush(self).map_err(ext4_to_fs_core_error)
113 }
114 fn is_writable(&self) -> bool {
115 BlockDevice::is_writable(self)
116 }
117}
118
119pub struct CoreDevice<T: fs_core::BlockDevice> {
129 inner: T,
130}
131
132impl<T: fs_core::BlockDevice> CoreDevice<T> {
133 pub fn new(inner: T) -> Self {
134 Self { inner }
135 }
136
137 pub fn inner(&self) -> &T {
139 &self.inner
140 }
141
142 pub fn into_inner(self) -> T {
144 self.inner
145 }
146}
147
148impl<T: fs_core::BlockDevice> BlockDevice for CoreDevice<T> {
149 fn read_at(&self, offset: u64, buf: &mut [u8]) -> crate::error::Result<()> {
150 fs_core::BlockRead::read_at(&self.inner, offset, buf).map_err(fs_core_to_ext4_error)
151 }
152 fn size_bytes(&self) -> u64 {
153 fs_core::BlockRead::size_bytes(&self.inner)
154 }
155 fn write_at(&self, offset: u64, buf: &[u8]) -> crate::error::Result<()> {
156 fs_core::BlockDevice::write_at(&self.inner, offset, buf).map_err(fs_core_to_ext4_error)
157 }
158 fn flush(&self) -> crate::error::Result<()> {
159 fs_core::BlockDevice::flush(&self.inner).map_err(fs_core_to_ext4_error)
160 }
161 fn is_writable(&self) -> bool {
162 fs_core::BlockDevice::is_writable(&self.inner)
163 }
164 }
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use std::sync::Mutex;
172
173 struct InMemoryFsCore(Mutex<Vec<u8>>);
176
177 impl fs_core::BlockRead for InMemoryFsCore {
178 fn read_at(&self, offset: u64, buf: &mut [u8]) -> fs_core::Result<()> {
179 let b = self.0.lock().unwrap();
180 let start = offset as usize;
181 let end = start + buf.len();
182 if end > b.len() {
183 return Err(fs_core::Error::ShortRead {
184 offset,
185 want: buf.len(),
186 got: b.len().saturating_sub(start),
187 });
188 }
189 buf.copy_from_slice(&b[start..end]);
190 Ok(())
191 }
192 fn size_bytes(&self) -> u64 {
193 self.0.lock().unwrap().len() as u64
194 }
195 }
196
197 impl fs_core::BlockDevice for InMemoryFsCore {
198 fn write_at(&self, offset: u64, buf: &[u8]) -> fs_core::Result<()> {
199 let mut b = self.0.lock().unwrap();
200 let start = offset as usize;
201 let end = start + buf.len();
202 if end > b.len() {
207 return Err(fs_core::Error::OutOfBounds {
208 offset,
209 len: buf.len() as u64,
210 size: b.len() as u64,
211 });
212 }
213 b[start..end].copy_from_slice(buf);
214 Ok(())
215 }
216 fn is_writable(&self) -> bool {
217 true
218 }
219 }
220
221 #[test]
222 fn core_device_round_trip() {
223 let mem = InMemoryFsCore(Mutex::new(vec![0u8; 4096]));
224 let dev = CoreDevice::new(mem);
225 assert_eq!(BlockDevice::size_bytes(&dev), 4096);
226
227 BlockDevice::write_at(&dev, 100, &[0x11, 0x22, 0x33, 0x44]).unwrap();
229 let mut buf = [0u8; 4];
230 BlockDevice::read_at(&dev, 100, &mut buf).unwrap();
231 assert_eq!(buf, [0x11, 0x22, 0x33, 0x44]);
232 }
233
234 #[test]
235 fn core_device_propagates_short_read_as_oob() {
236 let mem = InMemoryFsCore(Mutex::new(vec![0u8; 64]));
237 let dev = CoreDevice::new(mem);
238 let mut buf = [0u8; 16];
239 let err = BlockDevice::read_at(&dev, 60, &mut buf).unwrap_err();
243 assert!(matches!(err, Error::Io(_)), "got: {err:?}");
244 }
245}