littlefs_rust/
block_device.rs1use alloc::{boxed::Box, vec::Vec};
2
3use crate::types::{Config, Error, Result};
4
5#[cfg(feature = "std")]
6use std::{
7 fs::{File, OpenOptions},
8 io::{Read, Seek, SeekFrom, Write},
9 path::Path,
10 sync::Mutex,
11};
12
13pub trait BlockDevice {
20 fn config(&self) -> Config;
21
22 fn read(&self, block: u32, off: usize, out: &mut [u8]) -> Result<()>;
23
24 fn prog(&mut self, block: u32, off: usize, data: &[u8]) -> Result<()>;
25
26 fn erase(&mut self, block: u32) -> Result<()>;
27
28 fn sync(&mut self) -> Result<()> {
29 Ok(())
30 }
31}
32
33#[derive(Debug, Clone)]
41pub struct MemoryBlockDevice {
42 cfg: Config,
43 storage: Vec<u8>,
44}
45
46#[cfg(feature = "std")]
54#[derive(Debug)]
55pub struct FileBlockDevice {
56 cfg: Config,
57 file: Mutex<File>,
58}
59
60impl MemoryBlockDevice {
61 pub fn new_erased(cfg: Config) -> Result<Self> {
62 let len = image_len(cfg)?;
63 Ok(Self {
64 cfg,
65 storage: alloc::vec![0xff; len],
66 })
67 }
68
69 pub fn from_bytes(cfg: Config, bytes: &[u8]) -> Result<Self> {
70 let len = image_len(cfg)?;
71 if bytes.len() != len {
72 return Err(Error::InvalidConfig);
73 }
74 Ok(Self {
75 cfg,
76 storage: bytes.to_vec(),
77 })
78 }
79
80 pub fn as_bytes(&self) -> &[u8] {
81 &self.storage
82 }
83
84 fn block_range(&self, block: u32, off: usize, len: usize) -> Result<core::ops::Range<usize>> {
85 let block = block as usize;
86 if block >= self.cfg.block_count {
87 return Err(Error::OutOfBounds);
88 }
89 let end_off = off.checked_add(len).ok_or(Error::OutOfBounds)?;
90 if end_off > self.cfg.block_size {
91 return Err(Error::OutOfBounds);
92 }
93 let start = block
94 .checked_mul(self.cfg.block_size)
95 .and_then(|base| base.checked_add(off))
96 .ok_or(Error::OutOfBounds)?;
97 let end = start.checked_add(len).ok_or(Error::OutOfBounds)?;
98 Ok(start..end)
99 }
100}
101
102#[cfg(feature = "std")]
103impl FileBlockDevice {
104 pub fn create_erased<P: AsRef<Path>>(path: P, cfg: Config) -> Result<Self> {
105 let len = image_len(cfg)?;
106 let mut file = OpenOptions::new()
107 .read(true)
108 .write(true)
109 .create(true)
110 .truncate(true)
111 .open(path)
112 .map_err(|_| Error::Io)?;
113
114 let erased = alloc::vec![0xff; cfg.block_size];
115 for _ in 0..cfg.block_count {
116 file.write_all(&erased).map_err(|_| Error::Io)?;
117 }
118 file.set_len(len as u64).map_err(|_| Error::Io)?;
119 Ok(Self {
120 cfg,
121 file: Mutex::new(file),
122 })
123 }
124
125 pub fn open<P: AsRef<Path>>(path: P, cfg: Config) -> Result<Self> {
126 let len = image_len(cfg)? as u64;
127 let file = OpenOptions::new()
128 .read(true)
129 .write(true)
130 .open(path)
131 .map_err(|_| Error::Io)?;
132 if file.metadata().map_err(|_| Error::Io)?.len() != len {
133 return Err(Error::InvalidConfig);
134 }
135 Ok(Self {
136 cfg,
137 file: Mutex::new(file),
138 })
139 }
140
141 pub fn from_bytes<P: AsRef<Path>>(path: P, cfg: Config, bytes: &[u8]) -> Result<Self> {
142 let len = image_len(cfg)?;
143 if bytes.len() != len {
144 return Err(Error::InvalidConfig);
145 }
146
147 let mut file = OpenOptions::new()
148 .read(true)
149 .write(true)
150 .create(true)
151 .truncate(true)
152 .open(path)
153 .map_err(|_| Error::Io)?;
154 file.write_all(bytes).map_err(|_| Error::Io)?;
155 Ok(Self {
156 cfg,
157 file: Mutex::new(file),
158 })
159 }
160
161 fn block_range(&self, block: u32, off: usize, len: usize) -> Result<core::ops::Range<usize>> {
162 let block = block as usize;
163 if block >= self.cfg.block_count {
164 return Err(Error::OutOfBounds);
165 }
166 let end_off = off.checked_add(len).ok_or(Error::OutOfBounds)?;
167 if end_off > self.cfg.block_size {
168 return Err(Error::OutOfBounds);
169 }
170 let start = block
171 .checked_mul(self.cfg.block_size)
172 .and_then(|base| base.checked_add(off))
173 .ok_or(Error::OutOfBounds)?;
174 let end = start.checked_add(len).ok_or(Error::OutOfBounds)?;
175 Ok(start..end)
176 }
177}
178
179impl BlockDevice for MemoryBlockDevice {
180 fn config(&self) -> Config {
181 self.cfg
182 }
183
184 fn read(&self, block: u32, off: usize, out: &mut [u8]) -> Result<()> {
185 let range = self.block_range(block, off, out.len())?;
186 out.copy_from_slice(&self.storage[range]);
187 Ok(())
188 }
189
190 fn prog(&mut self, block: u32, off: usize, data: &[u8]) -> Result<()> {
191 let range = self.block_range(block, off, data.len())?;
192 for (dst, src) in self.storage[range].iter_mut().zip(data) {
193 *dst &= *src;
194 }
195 Ok(())
196 }
197
198 fn erase(&mut self, block: u32) -> Result<()> {
199 let range = self.block_range(block, 0, self.cfg.block_size)?;
200 self.storage[range].fill(0xff);
201 Ok(())
202 }
203}
204
205impl<D: BlockDevice + ?Sized> BlockDevice for Box<D> {
206 fn config(&self) -> Config {
207 (**self).config()
208 }
209
210 fn read(&self, block: u32, off: usize, out: &mut [u8]) -> Result<()> {
211 (**self).read(block, off, out)
212 }
213
214 fn prog(&mut self, block: u32, off: usize, data: &[u8]) -> Result<()> {
215 (**self).prog(block, off, data)
216 }
217
218 fn erase(&mut self, block: u32) -> Result<()> {
219 (**self).erase(block)
220 }
221
222 fn sync(&mut self) -> Result<()> {
223 (**self).sync()
224 }
225}
226
227#[cfg(feature = "std")]
228impl BlockDevice for FileBlockDevice {
229 fn config(&self) -> Config {
230 self.cfg
231 }
232
233 fn read(&self, block: u32, off: usize, out: &mut [u8]) -> Result<()> {
234 let range = self.block_range(block, off, out.len())?;
235 let mut file = self.file.lock().map_err(|_| Error::Io)?;
236 file.seek(SeekFrom::Start(range.start as u64))
237 .map_err(|_| Error::Io)?;
238 file.read_exact(out).map_err(|_| Error::Io)
239 }
240
241 fn prog(&mut self, block: u32, off: usize, data: &[u8]) -> Result<()> {
242 let range = self.block_range(block, off, data.len())?;
243 let mut file = self.file.lock().map_err(|_| Error::Io)?;
244 let chunk_size = self.cfg.cache_size();
245 let mut old = alloc::vec![0xff; chunk_size];
246 let mut copied = 0usize;
247 while copied < data.len() {
248 let n = core::cmp::min(chunk_size, data.len() - copied);
249 let file_off = range.start + copied;
250 file.seek(SeekFrom::Start(file_off as u64))
251 .map_err(|_| Error::Io)?;
252 file.read_exact(&mut old[..n]).map_err(|_| Error::Io)?;
253 for (dst, src) in old[..n].iter_mut().zip(&data[copied..copied + n]) {
254 *dst &= *src;
255 }
256 file.seek(SeekFrom::Start(file_off as u64))
257 .map_err(|_| Error::Io)?;
258 file.write_all(&old[..n]).map_err(|_| Error::Io)?;
259 copied += n;
260 }
261 Ok(())
262 }
263
264 fn erase(&mut self, block: u32) -> Result<()> {
265 let range = self.block_range(block, 0, self.cfg.block_size)?;
266 let mut file = self.file.lock().map_err(|_| Error::Io)?;
267 let chunk_size = self.cfg.cache_size();
268 let erased = alloc::vec![0xff; chunk_size];
269 let mut copied = 0usize;
270 while copied < self.cfg.block_size {
271 let n = core::cmp::min(chunk_size, self.cfg.block_size - copied);
272 file.seek(SeekFrom::Start((range.start + copied) as u64))
273 .map_err(|_| Error::Io)?;
274 file.write_all(&erased[..n]).map_err(|_| Error::Io)?;
275 copied += n;
276 }
277 Ok(())
278 }
279
280 fn sync(&mut self) -> Result<()> {
281 let file = self.file.lock().map_err(|_| Error::Io)?;
282 file.sync_data().map_err(|_| Error::Io)
283 }
284}
285
286fn image_len(cfg: Config) -> Result<usize> {
287 if cfg.block_size == 0 || cfg.block_count == 0 {
288 return Err(Error::InvalidConfig);
289 }
290 cfg.block_size
291 .checked_mul(cfg.block_count)
292 .ok_or(Error::InvalidConfig)
293}