1use crate::{
2 file_metadata::{FileMetadata, ReadMetadataError, WriteMetadataError},
3 storage::{EraseStorageError, Storage, StorageError},
4};
5use std::{
6 fmt::Debug,
7 io::{SeekFrom, Write},
8 ops::Deref,
9 ptr::NonNull,
10 sync::RwLock,
11};
12use std::{io::Seek, marker::ConstParamTy};
13use thiserror::Error;
14use zerocopy::IntoBytes;
15
16#[derive(Error, Debug)]
18pub enum ReadFileError {
19 #[error(transparent)]
21 StorageError(#[from] StorageError),
22 #[error("No metadata found the metadata does not have the correct marker.")]
24 InvalidMetadataMarker,
25 #[error("File has already been deleted")]
27 FileWasDeleted,
28 #[error("File is not marked as ready. Maybe you lost power during writing?")]
30 FileNotReady,
31}
32
33#[derive(Error, Debug)]
35pub enum WriteFileError {
36 #[error(transparent)]
38 StorageError(#[from] StorageError),
39 #[error("No metadata found the metadata does not have the correct marker.")]
41 InvalidMetadataMarker,
42 #[error("File has already been deleted")]
44 FileWasDeleted,
45 #[error("File is already marked as ready. You should not write to this anymore.")]
47 FileIsAlreadyReady,
48 #[error("The backing storage for a new file needs to be empty")]
50 NotZeroed,
51}
52
53#[derive(Error, Debug)]
55pub enum ReadFileFromStorageError {
56 #[error(transparent)]
58 ReadMetadataError(#[from] ReadMetadataError),
59 #[error(transparent)]
61 ReadFileContentError(#[from] ReadFileError),
62}
63
64#[derive(Error, Debug)]
66pub enum WriteFileToStorageError {
67 #[error(transparent)]
69 WriteMetadataError(#[from] WriteMetadataError),
70 #[error(transparent)]
72 WriteFileContentError(#[from] WriteFileError),
73}
74
75#[derive(Error, Debug)]
77pub enum DeleteFileContentError {
78 #[error(transparent)]
80 EraseStorageError(#[from] EraseStorageError),
81}
82
83#[derive(Error, Debug)]
85pub enum CommitFileContentError {
86 #[error(transparent)]
88 StorageError(#[from] StorageError),
89}
90
91pub enum FileContentTransition {
93 DropLastReader,
99}
100
101struct InnerFile<T: Storage + 'static> {
103 weak_count: usize,
105 reader_count: usize,
107 writer_count: usize,
109 storage: &'static T,
111 storage_address: u32,
113 current_offset: u32,
115 transition: Box<dyn FnOnce(FileContentTransition) + 'static + Send + Sync>,
117 has_been_deleted: bool,
120}
121
122impl<T: Storage + 'static> std::fmt::Debug for InnerFile<T> {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 f.debug_struct("FileContentInfo")
125 .field("weak_count", &self.weak_count)
126 .field("reader_count", &self.reader_count)
127 .field("writer_count", &self.writer_count)
128 .finish()
129 }
130}
131
132#[derive(ConstParamTy, PartialEq, Eq, Clone, Debug)]
136pub enum FileState {
137 Writer,
139 Reader,
141 Weak,
143}
144
145pub struct File<T: Storage + 'static, const STATE: FileState = { FileState::Reader }> {
170 content: &'static [u8],
171 metadata: &'static FileMetadata,
172 info: NonNull<RwLock<InnerFile<T>>>,
173}
174
175impl<T: Storage + 'static> File<T, { FileState::Reader }> {
176 fn new(
181 data: &'static [u8],
182 metadata: &'static FileMetadata,
183 storage: &'static T,
184 storage_address: u32,
185 transition: impl FnOnce(FileContentTransition) + 'static + Send + Sync,
186 ) -> Result<Self, ReadFileError> {
187 if !metadata.valid_marker() {
188 return Err(ReadFileError::InvalidMetadataMarker);
189 }
190
191 if !metadata.ready() {
192 return Err(ReadFileError::FileNotReady);
193 }
194
195 let file = Self {
196 content: data,
197 metadata,
198 info: Box::into_non_null(Box::new(RwLock::new(InnerFile {
199 reader_count: 1,
200 weak_count: 0,
201 writer_count: 0,
202 storage,
203 storage_address,
204 current_offset: 0,
205 transition: Box::new(transition),
206 has_been_deleted: false,
207 }))),
208 };
209
210 if metadata.marked_for_deletion() {
211 unsafe {
213 let _ = file.internal_delete();
214 };
215 return Err(ReadFileError::FileWasDeleted);
216 }
217
218 if metadata.deleted() {
219 unsafe {
221 let _ = file.internal_delete();
222 };
223 return Err(ReadFileError::FileWasDeleted);
224 };
225
226 Ok(file)
227 }
228
229 pub fn from_storage(
233 storage: &'static T,
234 address: u32,
235 ) -> Result<Self, ReadFileFromStorageError> {
236 let metadata = FileMetadata::from_storage(storage, address)?;
237 let content = storage
238 .read(address + size_of::<FileMetadata>() as u32, metadata.length)
239 .map_err(ReadFileError::from)?;
240 let file_content =
241 File::<T, { FileState::Reader }>::new(content, metadata, storage, address, |_| ())?;
242
243 Ok(file_content)
244 }
245
246 pub fn name_str(&self) -> &str {
248 self.metadata.name_str()
249 }
250}
251
252impl<T: Storage + 'static> File<T, { FileState::Writer }> {
253 fn new_writer(
255 data: &'static [u8],
256 metadata: &'static FileMetadata,
257 storage: &'static T,
258 storage_address: u32,
259 transition: impl FnOnce(FileContentTransition) + 'static + Send + Sync,
260 ) -> Result<Self, WriteFileError> {
261 if !metadata.valid_marker() {
262 return Err(WriteFileError::InvalidMetadataMarker);
263 }
264
265 if metadata.deleted() {
266 return Err(WriteFileError::FileWasDeleted);
267 }
268
269 if metadata.ready() {
270 return Err(WriteFileError::FileIsAlreadyReady);
271 }
272
273 if metadata.marked_for_deletion() {
274 }
276
277 if !data.iter().all(|byte| *byte == 0xff) {
278 return Err(WriteFileError::NotZeroed);
279 }
280
281 Ok(Self {
282 content: data,
283 metadata,
284 info: Box::into_non_null(Box::new(RwLock::new(InnerFile {
285 reader_count: 0,
286 weak_count: 0,
287 writer_count: 1,
288 storage,
289 storage_address,
290 current_offset: 0,
291 transition: Box::new(transition),
292 has_been_deleted: false,
293 }))),
294 })
295 }
296
297 pub fn to_storage(
299 storage: &'static T,
300 address: u32,
301 length: u32,
302 name: &str,
303 ) -> Result<Self, WriteFileToStorageError> {
304 let metadata = FileMetadata::new_to_storage(storage, address, name, length)?;
305 let content = storage
306 .read(address + size_of::<FileMetadata>() as u32, metadata.length)
307 .map_err(WriteFileError::from)?;
308 let file_content = File::<T, { FileState::Writer }>::new_writer(
309 content,
310 metadata,
311 storage,
312 address,
313 |_| (),
314 )?;
315
316 Ok(file_content)
317 }
318
319 pub fn commit(self) -> Result<File<T, { FileState::Reader }>, CommitFileContentError> {
323 {
324 let mut info = unsafe { (self.info.as_ref()).write().unwrap() };
325 assert!(info.writer_count == 1);
326 assert!(info.reader_count == 0);
327 info.writer_count = 0;
328 info.reader_count = 1;
329 unsafe {
330 self.metadata
331 .set_ready(info.storage, info.storage_address)?;
332 }
333 }
334 unsafe {
335 Ok(std::mem::transmute::<
336 File<T, { FileState::Writer }>,
337 File<T, { FileState::Reader }>,
338 >(self))
339 }
340 }
341}
342
343impl<T: Storage + 'static, const STATE: FileState> File<T, STATE> {
344 pub fn downgrade(&self) -> File<T, { FileState::Weak }> {
346 unsafe {
347 self.info.as_ref().write().unwrap().weak_count += 1;
348 }
349 File::<T, { FileState::Weak }> {
350 content: self.content,
351 metadata: self.metadata,
352 info: self.info,
353 }
354 }
355
356 pub fn upgrade(&self) -> Option<File<T, { FileState::Reader }>> {
368 if STATE == FileState::Writer {
369 return None;
370 }
371 let mut info = unsafe { self.info.as_ref().write().unwrap() };
372 if info.has_been_deleted {
373 return None;
374 }
375 if !self.metadata.ready() {
376 return None;
377 }
378 if self.metadata.marked_for_deletion() {
379 return None;
380 }
381
382 info.reader_count += 1;
383 Some(File::<T, { FileState::Reader }> {
384 content: self.content,
385 metadata: self.metadata,
386 info: self.info,
387 })
388 }
389
390 pub fn is_last(&self) -> bool {
392 if STATE == FileState::Reader {
393 return unsafe { self.info.as_ref().read().unwrap().reader_count == 1 };
394 }
395 if STATE == FileState::Writer {
396 return unsafe { self.info.as_ref().read().unwrap().writer_count == 1 };
397 }
398
399 false
400 }
401
402 pub fn reader_count(&self) -> usize {
404 return unsafe { self.info.as_ref().read().unwrap().reader_count };
405 }
406
407 pub fn writer_count(&self) -> usize {
409 return unsafe { self.info.as_ref().read().unwrap().writer_count };
410 }
411
412 pub fn marked_for_deletion(&self) -> bool {
414 self.metadata.marked_for_deletion()
415 }
416
417 pub fn deleted(&self) -> bool {
419 let info = unsafe { self.info.as_ref().read().unwrap() };
420 if info.has_been_deleted {
421 return true;
422 }
423 self.metadata.deleted()
424 }
425
426 pub fn ready(&self) -> bool {
428 self.metadata.ready()
429 }
430
431 pub fn mark_for_deletion(&self) -> Result<(), DeleteFileContentError> {
437 let info = unsafe { self.info.as_ref().read().unwrap() };
438
439 unsafe {
440 self.metadata
441 .set_marked_for_deletion(info.storage, info.storage_address)
442 .unwrap();
443 };
444 if !info.has_been_deleted && info.writer_count == 0 && info.reader_count == 0 {
445 drop(info);
446 unsafe { self.internal_delete()? };
447 }
448 Ok(())
449 }
450
451 unsafe fn internal_delete(&self) -> Result<(), DeleteFileContentError> {
455 let mut info = unsafe { self.info.as_ref().write().unwrap() };
456
457 let previous_transition: &mut Box<
458 dyn FnOnce(FileContentTransition) + 'static + Send + Sync,
459 > = &mut info.transition;
460 let empty_transition: Box<dyn FnOnce(FileContentTransition) + 'static + Send + Sync> =
461 Box::new(|_| ());
462 let transition = std::mem::replace(previous_transition, empty_transition);
463 (transition)(FileContentTransition::DropLastReader);
464
465 self.metadata
466 .set_deleted(info.storage, info.storage_address)
467 .map_err(EraseStorageError::from)?;
468 info.has_been_deleted = true;
469
470 let full_file_length = self.metadata.length + size_of::<FileMetadata>() as u32;
471 let length = full_file_length.div_ceil(T::BLOCK_SIZE) * T::BLOCK_SIZE;
472
473 info.storage.erase(info.storage_address, length)?;
475 Ok(())
476 }
477
478 pub fn delete(self) -> Result<(), DeleteFileContentError> {
482 unsafe { self.internal_delete() }
483 }
484
485 pub unsafe fn erased(&self) -> bool {
489 let metadata_slice = FileMetadata::as_bytes(self.metadata);
490 if metadata_slice.iter().any(|i| *i != 0xff) {
491 return false;
492 }
493 if self.content.iter().any(|i| *i != 0xff) {
494 return false;
495 }
496 true
497 }
498}
499
500impl<T: Storage + 'static, const STATE: FileState> Debug for File<T, STATE> {
501 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
502 f.debug_struct("FileContent")
503 .field("metadata", &self.metadata)
505 .field("info", unsafe { self.info.as_ref() })
506 .finish()
507 }
508}
509
510impl<T: Storage + 'static> Deref for File<T, { FileState::Reader }> {
511 type Target = [u8];
512
513 fn deref(&self) -> &Self::Target {
514 self.content
515 }
516}
517
518impl<T: Storage + 'static> PartialEq<Self> for File<T, { FileState::Reader }> {
519 fn eq(&self, other: &Self) -> bool {
520 self.content == other.content
521 }
522}
523
524impl<T: Storage + 'static> Clone for File<T, { FileState::Reader }> {
525 fn clone(&self) -> Self {
526 let mut info = unsafe { self.info.as_ref().write().unwrap() };
527 info.reader_count += 1;
528 Self {
529 content: self.content,
530 metadata: self.metadata,
531 info: self.info,
532 }
533 }
534}
535
536impl<T: Storage + 'static> Clone for File<T, { FileState::Weak }> {
537 fn clone(&self) -> Self {
538 let mut info = unsafe { self.info.as_ref().write().unwrap() };
539 info.weak_count += 1;
540 Self {
541 content: self.content,
542 metadata: self.metadata,
543 info: self.info,
544 }
545 }
546}
547
548impl<T: Storage + 'static, const STATE: FileState> Drop for File<T, STATE> {
549 fn drop(&mut self) {
550 let mut info = unsafe { self.info.as_ref().write().unwrap() };
551
552 if STATE == { FileState::Weak } {
553 info.weak_count = info.weak_count.saturating_sub(1);
554 }
555 if STATE == { FileState::Writer } {
556 info.writer_count = info.writer_count.saturating_sub(1);
557 }
558 if STATE == { FileState::Reader } {
559 info.reader_count = info.reader_count.saturating_sub(1);
560 }
561
562 if info.reader_count != 0 || info.writer_count != 0 {
563 return;
564 }
565
566 let weak_count = info.weak_count;
567 let has_been_deleted = info.has_been_deleted;
568 drop(info);
569 if !has_been_deleted && self.metadata.marked_for_deletion() {
570 unsafe {
571 let _ = self.internal_delete();
574 };
575 }
576
577 if weak_count == 0 {
578 unsafe {
579 drop(Box::from_non_null(self.info));
580 };
581 }
582 }
583}
584
585impl<T: Storage + 'static + Send + Sync> Seek for File<T, { FileState::Writer }> {
586 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
587 let length = self.content.len() as u32;
588 let current_offset = unsafe {
589 &mut self
590 .info
591 .as_ref()
592 .write()
593 .map_err(|e| std::io::Error::other(e.to_string()))?
594 .current_offset
595 };
596 let new_offset = match pos {
597 SeekFrom::Start(offset) => offset.try_into().unwrap_or(u32::MAX).clamp(0, length),
598 SeekFrom::End(offset) => length
599 .saturating_add_signed(
600 offset
601 .clamp(isize::MIN as i64, isize::MAX as i64)
602 .try_into()
603 .unwrap(),
604 )
605 .clamp(0, length),
606 SeekFrom::Current(offset) => current_offset
607 .saturating_add_signed(
608 offset
609 .clamp(isize::MIN as i64, isize::MAX as i64)
610 .try_into()
611 .unwrap(),
612 )
613 .clamp(0, length),
614 };
615
616 *current_offset = new_offset;
617 Ok(*current_offset as u64)
618 }
619}
620
621impl<T: Storage + 'static + Send + Sync> Write for File<T, { FileState::Writer }> {
622 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
624 let length = self.content.len() as u32;
625 let info = unsafe {
626 &mut self
627 .info
628 .as_ref()
629 .write()
630 .map_err(|_| std::io::ErrorKind::ResourceBusy)?
631 };
632 let current_offset = info.current_offset;
633
634 let remaining_length = length.saturating_sub(current_offset);
635 let write_length = std::cmp::min(remaining_length, buf.len() as u32);
636
637 let writable_storage = info.storage;
638 writable_storage
639 .write(
640 info.storage_address + size_of::<FileMetadata>() as u32 + current_offset,
641 &buf[0..write_length as usize],
642 )
643 .map_err(std::io::Error::other)?;
644 info.current_offset += write_length;
645 Ok(write_length as usize)
646 }
647
648 fn flush(&mut self) -> std::io::Result<()> {
649 Ok(())
650 }
651}
652
653#[cfg(test)]
654mod tests {
655 use crate::storage::simulated::{get_test_storage, SimulatedStorage};
656
657 use super::*;
658
659 fn get_backing() -> (
660 &'static SimulatedStorage,
661 &'static mut [u8],
662 &'static FileMetadata,
663 ) {
664 let backing_storage = get_test_storage();
665 let metadata: &'static FileMetadata = dbg!(FileMetadata::new_to_storage(
666 backing_storage,
667 0,
668 "toast",
669 100
670 ))
671 .unwrap();
672 unsafe { metadata.set_ready(backing_storage, 0) }.unwrap();
673 let content: &'static [u8] = &backing_storage
674 .read(size_of::<FileMetadata>() as u32, 100)
675 .unwrap();
676 let content_ptr = content.as_ptr() as *mut u8;
677 let mut_content: &'static mut [u8] =
678 unsafe { std::slice::from_raw_parts_mut(content_ptr, 100) };
679
680 return (backing_storage, mut_content, metadata);
681 }
682
683 fn call_new() -> File<SimulatedStorage, { FileState::Reader }> {
684 let (storage, content, metadata) = get_backing();
685 let content = File::<_, { FileState::Reader }>::new(content, metadata, storage, 0, |_| ());
686 return content.unwrap();
687 }
688
689 #[test]
690 fn creating_and_dropping_a_file_does_not_panic() {
691 let content = call_new();
692 drop(content);
693 }
694
695 #[test]
696 fn equality_works() {
697 let (storage1, content1, metadata1) = get_backing();
698 let content1 =
699 File::<_, { FileState::Reader }>::new(content1, metadata1, storage1, 0, |_| ())
700 .unwrap();
701 let (storage2, content2, metadata2) = get_backing();
702 let content2 =
703 File::<_, { FileState::Reader }>::new(content2, metadata2, storage2, 0, |_| ())
704 .unwrap();
705 let (storage3, content3, metadata3) = get_backing();
706 content3[1] = 17;
707 let content3 =
708 File::<_, { FileState::Reader }>::new(content3, metadata3, storage3, 0, |_| ())
709 .unwrap();
710 assert_eq!(content1, content2);
711 assert_ne!(content2, content3);
712 }
713
714 #[test]
715 fn cloning_works() {
716 let (storage, content, metadata) = get_backing();
717 content[3] = 17;
718 let content =
719 File::<_, { FileState::Reader }>::new(content, metadata, storage, 0, |_| ()).unwrap();
720 let cloned_content = content.clone();
721 assert_eq!(content, cloned_content);
722 }
723
724 #[test]
725 fn is_last_works() {
726 let content = call_new();
727
728 assert!(File::is_last(&content));
729 let other_content = content.clone();
730 assert!(!File::is_last(&content));
731 assert!(!File::is_last(&other_content));
732 drop(content);
733 assert!(File::is_last(&other_content));
734 }
735
736 #[test]
737 fn downgrading_works() {
738 let content = call_new();
739 assert!(File::is_last(&content));
740 let weak_content = content.downgrade();
741 assert!(File::is_last(&content));
742 drop(weak_content);
743 assert!(File::is_last(&content));
744 }
745
746 #[test]
747 fn upgrading_works() {
748 let content = call_new();
749 assert!(File::is_last(&content));
750 let weak_content = content.downgrade();
751 assert!(File::is_last(&content));
752 let upgraded_content = weak_content.upgrade().unwrap();
753 assert!(!File::is_last(&content));
754 assert!(!File::is_last(&upgraded_content));
755 drop(content);
756 assert!(File::is_last(&upgraded_content));
757 }
758
759 #[test]
760 fn upgrading_works_even_if_there_are_no_readers_left() {
761 let content = call_new();
762 assert!(File::is_last(&content));
763 let weak_content = content.downgrade();
764 assert!(File::is_last(&content));
765 drop(content);
766 weak_content.upgrade().unwrap();
767 }
768
769 #[test]
770 fn upgrading_does_not_work_when_reader_was_marked_for_deletion() {
771 let content = call_new();
772 assert!(File::is_last(&content));
773 let weak_content = content.downgrade();
774 assert!(File::is_last(&content));
775 content.mark_for_deletion().unwrap();
776 drop(content);
777 let None = weak_content.upgrade() else {
778 panic!("Should not be able to upgrade when there are no strong references left");
779 };
780 }
781
782 #[test]
783 fn upgrading_does_not_work_when_weak_was_marked_for_deletion() {
784 let content = call_new();
785 assert!(File::is_last(&content));
786 let weak_content = content.downgrade();
787 assert!(File::is_last(&content));
788 drop(content);
789 weak_content.mark_for_deletion().unwrap();
790 let None = weak_content.upgrade() else {
791 panic!("Should not be able to upgrade when there are no strong references left");
792 };
793 }
794
795 #[test]
796 fn deleting_when_there_is_no_reader_works() {
797 let content = call_new();
798 let weak_content = content.downgrade();
799 drop(content);
800 weak_content.mark_for_deletion().unwrap();
801 assert!(weak_content.deleted() == true);
802 }
803
804 #[test]
805 fn deleting_is_deferred_until_the_last_reader_is_dropped() {
806 let content = call_new();
807 let weak_content = content.downgrade();
808 weak_content.mark_for_deletion().unwrap();
809 assert!(weak_content.deleted() == false);
810 assert!(content.deleted() == false);
811
812 drop(weak_content);
814 assert!(content.deleted() == false);
815
816 let weak_content2 = content.downgrade();
817 assert!(weak_content2.deleted() == false);
818 drop(content);
819 assert!(weak_content2.deleted() == true);
820 }
821
822 #[test]
823 fn upgrading_fails_when_marked_for_deletion() {
824 let content = call_new();
825 let weak_content = content.downgrade();
826 File::mark_for_deletion(&content).unwrap();
827 let None = content.upgrade() else {
828 panic!("Should not be able to upgrade when there are no strong references left");
829 };
830 let None = weak_content.upgrade() else {
831 panic!("Should not be able to upgrade when there are no strong references left");
832 };
833 }
834}