1#![warn(missing_docs)]
19#![feature(adt_const_params)]
20#![feature(box_as_ptr)]
21#![feature(box_vec_non_null)]
22#![feature(allocator_api)]
23#![feature(doc_cfg)]
24#[cfg_attr(
25 feature = "simulated",
26 doc = r##"
27
28# Examples
29
30Create an in-memory filesystem for testing:
31
32```
33use rudelblinken_filesystem::storage::simulated::SimulatedStorage;
34use rudelblinken_filesystem::Filesystem;
35
36let storage = SimulatedStorage::new();
37// TODO: Improve the interface to allow storages with lifetimes
38let static_storage_ref = unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&storage) };
39let mut filesystem = Filesystem::new(static_storage_ref);
40```
41"##
42)]
43use file::{CommitFileContentError, File, FileState, WriteFileToStorageError};
44use file_information::FileInformation;
45use file_metadata::FileMetadata;
46use std::{collections::BTreeMap, io::Write, ops::Bound::Included};
47use storage::{EraseStorageError, Storage};
48use thiserror::Error;
49
50mod file;
51mod file_information;
52mod file_metadata;
53pub mod storage;
55
56#[derive(Error, Debug, Clone)]
58pub enum FindFreeSpaceError {
59 #[error("Error in filesystem structure")]
61 FilesystemError,
62 #[error("No free space")]
64 NoFreeSpace,
65 #[error("Not enough space")]
67 NotEnoughSpace,
68}
69
70#[derive(Error, Debug)]
72pub enum FilesystemWriteError {
73 #[error(transparent)]
75 FindFreeSpaceError(#[from] FindFreeSpaceError),
76 #[error(transparent)]
78 WriteFileToStorageError(#[from] WriteFileToStorageError),
79 #[error(transparent)]
81 IoError(#[from] std::io::Error),
82 #[error(transparent)]
84 CommitFileContentError(#[from] CommitFileContentError),
85}
86
87#[derive(Error, Debug)]
89pub enum FilesystemDeleteError {
90 #[error(transparent)]
92 EraseStorageError(#[from] EraseStorageError),
93 #[error("The file does not exist")]
95 FileNotFound,
96}
97
98pub struct Filesystem<T: Storage + 'static + Send + Sync> {
104 storage: &'static T,
105 files: Vec<FileInformation<T>>,
106}
107
108impl<T: Storage + 'static + Send + Sync> Filesystem<T> {
109 fn get_first_block(&self) -> Result<u16, std::io::Error> {
111 let first_block_slice: Box<[u8; 2]> = self
112 .storage
113 .read_metadata("first_block")?
114 .try_into()
115 .unwrap();
116 Ok(u16::from_le_bytes(*first_block_slice))
117 }
118 fn set_first_block(&mut self, first_block: u16) -> Result<(), std::io::Error> {
120 self.storage
121 .write_metadata("first_block", &first_block.to_le_bytes())?;
122 Ok(())
123 }
124
125 pub fn new(storage: &'static T) -> Self {
139 let mut filesystem = Self {
140 storage,
141 files: Vec::new(),
142 };
143 let first_block = filesystem.get_first_block();
144 let first_block = first_block.unwrap_or_else(|_| {
145 filesystem.set_first_block(0).unwrap();
146 0
147 });
148
149 let mut block_number = 0;
150
151 while block_number < T::BLOCKS {
152 let current_block_number = (block_number + first_block as u32) % T::BLOCKS;
153 let file_information = FileInformation::from_storage(
154 filesystem.storage,
155 current_block_number * T::BLOCK_SIZE,
156 );
157 let file_information = match file_information {
158 Ok(file_information) => file_information,
159 Err(_) => {
160 block_number += 1;
161 let Ok(current_block) = filesystem
162 .storage
163 .read(current_block_number * T::BLOCK_SIZE, T::BLOCK_SIZE)
164 else {
165 continue;
166 };
167 if current_block.iter().any(|b| *b != 0xff) {
168 println!(
169 "Erasing block {} because it is not zeroed",
170 current_block_number
171 );
172 filesystem
173 .storage
174 .erase(current_block_number * T::BLOCK_SIZE, T::BLOCK_SIZE)
175 .unwrap();
176 };
177 continue;
178 }
179 };
180 block_number += ((file_information.length + 64) / T::BLOCK_SIZE) + 1;
181 filesystem.files.push(file_information);
182 }
183
184 filesystem
185 }
186
187 pub fn read_file(&self, name: &str) -> Option<File<T, { FileState::Weak }>> {
189 let file = self
190 .files
191 .iter()
192 .find(|file| file.name == name && file.valid())?;
193 Some(file.read())
194 }
195
196 fn find_free_space(&self, length: u32) -> Result<u32, FindFreeSpaceError> {
200 let mut free_ranges: BTreeMap<u16, u16> = Default::default();
201 free_ranges.insert(0, T::BLOCKS as u16 * 2);
202
203 for file in &self.files {
204 let start_block = (file.address / T::BLOCK_SIZE) as u16;
205 let length_in_blocks =
206 (file.length + size_of::<FileMetadata>() as u32).div_ceil(T::BLOCK_SIZE) as u16;
207 let end_block = start_block + length_in_blocks;
208
209 let Some((&surrounding_start, &surrounding_length)) = free_ranges
210 .range((Included(0), Included(start_block)))
211 .last()
212 else {
213 return Err(FindFreeSpaceError::FilesystemError);
215 };
216
217 let space_before = start_block - surrounding_start;
218 let space_after = (surrounding_start + surrounding_length) - (end_block);
219
220 match (space_before, space_after) {
221 (0, 0) => {
222 free_ranges.remove(&surrounding_start);
223 }
224 (0, space_after) => {
225 free_ranges.remove(&surrounding_start);
226 free_ranges.insert(end_block, space_after);
227 }
228 (space_before, 0) => {
229 free_ranges.insert(start_block, space_before);
230 }
231 (space_before, space_after) => {
232 free_ranges.insert(start_block, space_before);
233 free_ranges.insert(end_block, space_after);
234 }
235 }
236 }
237
238 let last_free_space_start = free_ranges.last_key_value().map_or(0, |(start, _)| *start);
240 let wraparound_length: i64 = last_free_space_start as i64 - T::BLOCKS as i64;
241 if wraparound_length >= 0 {
242 let wraparound_length = wraparound_length as u16;
243 let Some((0, &first_range_length)) = free_ranges.first_key_value() else {
244 return Err(FindFreeSpaceError::FilesystemError);
246 };
247 free_ranges.remove(&0);
248 let new_first_range_length = first_range_length - wraparound_length;
249 if new_first_range_length > 0 {
250 free_ranges.insert(wraparound_length, new_first_range_length);
251 }
252 free_ranges.insert(
253 last_free_space_start,
254 T::BLOCKS as u16 - last_free_space_start,
255 );
256 }
257
258 let Some(longest_range) = free_ranges
259 .into_iter()
260 .max_by(|(_, length_a), (_, length_b)| length_a.cmp(length_b))
261 .map(|(a, b)| (a as u32, b as u32))
262 else {
263 return Err(FindFreeSpaceError::NoFreeSpace);
264 };
265
266 if (longest_range.1 * T::BLOCK_SIZE) < length {
267 return Err(FindFreeSpaceError::NotEnoughSpace);
268 }
269
270 let longest_range_start = longest_range.0 % (T::BLOCKS);
271
272 Ok(longest_range_start * T::BLOCK_SIZE)
273 }
274
275 pub fn write_file(
277 &mut self,
278 name: &str,
279 content: &[u8],
280 _hash: &[u8; 32],
281 ) -> Result<(), FilesystemWriteError> {
282 let mut writer = self.get_file_writer(name, content.len() as u32, _hash)?;
283
284 writer.write_all(content)?;
285 writer.commit()?;
286 Ok(())
287 }
288
289 pub fn get_file_writer(
293 &mut self,
294 name: &str,
295 length: u32,
296 _hash: &[u8; 32],
297 ) -> Result<File<T, { FileState::Writer }>, FilesystemWriteError> {
298 self.cleanup_files();
299 let free_location = self.find_free_space(length + size_of::<FileMetadata>() as u32)?;
300
301 let (file, writer) =
302 FileInformation::to_storage(self.storage, free_location, length, name)?;
303 self.files.push(file);
304 Ok(writer)
305 }
306
307 pub fn delete_file(&mut self, filename: &str) -> Result<(), FilesystemDeleteError> {
311 let Some((index, _)) = self
312 .files
313 .iter()
314 .enumerate()
315 .find(|(_, file)| file.name == filename)
316 else {
317 return Err(FilesystemDeleteError::FileNotFound);
318 };
319 let file = &mut self.files[index];
320 if !file.marked_for_deletion() {
321 file.mark_for_deletion().unwrap();
322 }
323 if file.deleted() {
324 self.files.swap_remove(index);
325 }
326 Ok(())
327 }
328
329 fn cleanup_files(&mut self) {
331 let mut remove_indices: Vec<usize> = Vec::new();
332 for index in 0..self.files.len() {
333 if self.files[index].deleted() {
334 remove_indices.push(index);
335 }
336 }
337 for index in remove_indices.into_iter().rev() {
338 self.files.swap_remove(index);
339 }
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use crate::storage::simulated::SimulatedStorage;
346
347 use super::*;
348
349 #[test]
350 fn writing_and_reading_a_simple_file_works() {
351 let owned_storage = SimulatedStorage::new();
352 let storage =
353 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
354 let mut filesystem = Filesystem::new(storage);
355 let file = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
356 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
357 let result = filesystem.read_file("fancy").unwrap();
358 assert_eq!(result.upgrade().unwrap().as_ref(), file);
359 }
360
361 #[test]
362 fn can_read_a_file_from_an_old_storage() {
363 let owned_storage = SimulatedStorage::new();
364 let storage =
365 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
366 let file = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
367 let mut filesystem = Filesystem::new(storage);
368 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
369 let filesystem = Filesystem::new(storage);
370 let result = filesystem.read_file("fancy").unwrap();
371 assert_eq!(result.upgrade().unwrap().as_ref(), file);
372 }
373
374 #[test]
375 fn writing_multiple_files() {
376 let owned_storage = SimulatedStorage::new();
377 let storage =
378 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
379 let mut filesystem = Filesystem::new(storage);
380 let file = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
381 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
382 filesystem.write_file("fancy2", &file, &[0u8; 32]).unwrap();
383 let result = filesystem.read_file("fancy").unwrap();
384 assert_eq!(result.upgrade().unwrap().as_ref(), file);
385 let result = filesystem.read_file("fancy2").unwrap();
386 assert_eq!(result.upgrade().unwrap().as_ref(), file);
387 }
388
389 #[test]
390 fn deleting_a_file_works() {
391 let owned_storage = SimulatedStorage::new();
392 let storage =
393 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
394 let mut filesystem = Filesystem::new(storage);
395 let file = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
396 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
397 filesystem.delete_file("fancy").unwrap();
398 let None = filesystem.read_file("fancy") else {
399 panic!("Should not be able to read a deleted file");
400 };
401 }
402
403 #[test]
404 fn deleting_a_file_actually_works() {
405 let owned_storage = SimulatedStorage::new();
406 let storage =
407 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
408 let mut filesystem = Filesystem::new(storage);
409 let file = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
410 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
411 filesystem.delete_file("fancy").unwrap();
412 let None = filesystem.read_file("fancy") else {
413 panic!("Should not be able to read a deleted file");
414 };
415
416 let filesystem = Filesystem::new(storage);
417 let None = filesystem.read_file("fancy") else {
418 panic!("Should not be able to read a deleted file");
419 };
420 }
421
422 #[test]
423 fn file_cant_be_upgraded_if_it_has_been_deleted_and_there_are_only_weak_references() {
424 let owned_storage = SimulatedStorage::new();
425 let storage =
426 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
427 let mut filesystem = Filesystem::new(storage);
428 let content = vec![0; SimulatedStorage::SIZE as usize - size_of::<FileMetadata>()];
429 filesystem
430 .write_file("fancy", &content, &[0u8; 32])
431 .unwrap();
432 let file = filesystem.read_file("fancy").unwrap();
433 let weak_ref = file;
434 filesystem.delete_file("fancy").unwrap();
435 let None = weak_ref.upgrade() else {
436 panic!("Should not be able to upgrade deleted file");
437 };
438 let None = filesystem.read_file("fancy") else {
439 panic!("Should not be able to read a deleted file");
440 };
441 }
442
443 #[test]
444 fn no_new_references_can_be_created_to_a_file_marked_for_deletion() {
445 let owned_storage = SimulatedStorage::new();
446 let storage =
447 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
448 let mut filesystem = Filesystem::new(storage);
449 let content = vec![0; SimulatedStorage::SIZE as usize - size_of::<FileMetadata>()];
450 filesystem
451 .write_file("fancy", &content, &[0u8; 32])
452 .unwrap();
453 let file = filesystem.read_file("fancy").unwrap();
454 let strong_ref = file.upgrade().unwrap();
455 filesystem.delete_file("fancy").unwrap();
456 let None = filesystem.read_file("fancy").unwrap().upgrade() else {
457 panic!(
458 "Should not be able to create a new reference to a file marked for deletion file"
459 );
460 };
461 assert_eq!(strong_ref.as_ref(), content);
463 }
464
465 #[test]
466 fn writing_a_maximum_size_file_works() {
467 let owned_storage = SimulatedStorage::new();
468 let storage =
469 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
470 let mut filesystem = Filesystem::new(storage);
471 let file = [0u8; SimulatedStorage::SIZE as usize - size_of::<FileMetadata>()];
472 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
473 let result = filesystem.read_file("fancy").unwrap();
474 assert_eq!(result.upgrade().unwrap().as_ref(), file);
475 }
476
477 #[test]
478 fn deleting_a_file_makes_space_for_a_new_file() {
479 let owned_storage = SimulatedStorage::new();
480 let storage =
481 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
482 let mut filesystem = Filesystem::new(storage);
483 let file = [0u8; SimulatedStorage::SIZE as usize - size_of::<FileMetadata>()];
484 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
485 filesystem.delete_file("fancy").unwrap();
486 filesystem.write_file("fancy2", &file, &[0u8; 32]).unwrap();
487 let result = filesystem.read_file("fancy2").unwrap();
488 assert_eq!(result.upgrade().unwrap().as_ref(), file);
489 }
490
491 #[test]
492 fn deleting_a_file_does_not_make_space_for_a_new_file_if_there_are_still_strong_references_to_its_content(
493 ) {
494 let owned_storage = SimulatedStorage::new();
495 let storage =
496 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
497 let mut filesystem = Filesystem::new(storage);
498 let file = [0u8; SimulatedStorage::SIZE as usize - size_of::<FileMetadata>()];
499 filesystem.write_file("fancy", &file, &[0u8; 32]).unwrap();
500 let fancy_file = filesystem.read_file("fancy").unwrap();
501 let strong_ref = fancy_file.upgrade().unwrap();
502 filesystem.delete_file("fancy").unwrap();
503 let Err(_) = filesystem.write_file("fancy2", &file, &[0u8; 32]) else {
504 panic!("Should fail because the file was not yet deleted");
505 };
506 assert_eq!(strong_ref.as_ref(), file);
507 drop(strong_ref);
508 filesystem.write_file("fancy2", &file, &[0u8; 32]).unwrap();
510 }
511
512 #[test]
513 fn writing_a_file_thats_too_big_fails() {
514 let owned_storage = SimulatedStorage::new();
515 let storage =
516 unsafe { std::mem::transmute::<_, &'static SimulatedStorage>(&owned_storage) };
517 let mut filesystem = Filesystem::new(storage);
518 let file = [0u8; SimulatedStorage::SIZE as usize + 1];
519 let Err(_) = filesystem.write_file("fancy", &file, &[0u8; 32]) else {
520 panic!("Should fail when there is not enough space");
521 };
522 }
523}