rudelblinken_filesystem/
file.rs

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/// Represents an error that can occur while reading a file.
17#[derive(Error, Debug)]
18pub enum ReadFileError {
19    /// Error occurred in the storage layer.
20    #[error(transparent)]
21    StorageError(#[from] StorageError),
22    /// No metadata found or the metadata does not have the correct marker.
23    #[error("No metadata found the metadata does not have the correct marker.")]
24    InvalidMetadataMarker,
25    /// The file has already been deleted.
26    #[error("File has already been deleted")]
27    FileWasDeleted,
28    /// The file is not marked as ready, possibly due to a power loss during writing.
29    #[error("File is not marked as ready. Maybe you lost power during writing?")]
30    FileNotReady,
31}
32
33/// Represents an error that can occur while writing a file.
34#[derive(Error, Debug)]
35pub enum WriteFileError {
36    /// Error occurred in the storage layer.
37    #[error(transparent)]
38    StorageError(#[from] StorageError),
39    /// No metadata found or the metadata does not have the correct marker.
40    #[error("No metadata found the metadata does not have the correct marker.")]
41    InvalidMetadataMarker,
42    /// The file has already been deleted.
43    #[error("File has already been deleted")]
44    FileWasDeleted,
45    /// The file is already marked as ready and should not be written to anymore.
46    #[error("File is already marked as ready. You should not write to this anymore.")]
47    FileIsAlreadyReady,
48    /// The backing storage for a new file needs to be empty.
49    #[error("The backing storage for a new file needs to be empty")]
50    NotZeroed,
51}
52
53/// Represents an error that can occur while reading a file from storage.
54#[derive(Error, Debug)]
55pub enum ReadFileFromStorageError {
56    /// Error occurred while reading metadata.
57    #[error(transparent)]
58    ReadMetadataError(#[from] ReadMetadataError),
59    /// Error occurred while reading file content.
60    #[error(transparent)]
61    ReadFileContentError(#[from] ReadFileError),
62}
63
64/// Represents an error that can occur while writing a file to storage.
65#[derive(Error, Debug)]
66pub enum WriteFileToStorageError {
67    /// Error occurred while writing metadata.
68    #[error(transparent)]
69    WriteMetadataError(#[from] WriteMetadataError),
70    /// Error occurred while writing file content.
71    #[error(transparent)]
72    WriteFileContentError(#[from] WriteFileError),
73}
74
75/// Represents an error that can occur while deleting file content.
76#[derive(Error, Debug)]
77pub enum DeleteFileContentError {
78    /// Error occurred while erasing storage.
79    #[error(transparent)]
80    EraseStorageError(#[from] EraseStorageError),
81}
82
83/// Represents an error that can occur while committing file content.
84#[derive(Error, Debug)]
85pub enum CommitFileContentError {
86    /// Error occurred in the storage layer.
87    #[error(transparent)]
88    StorageError(#[from] StorageError),
89}
90
91/// Represents the transition state of file content.
92pub enum FileContentTransition {
93    // /// Writer gets committed
94    // Commit,
95    // /// Writer gets dropped without commit
96    // Abort,
97    /// Last reader gets dropped
98    DropLastReader,
99}
100
101/// Shared data about the current state of a file.
102struct InnerFile<T: Storage + 'static> {
103    /// Number of weak references.
104    weak_count: usize,
105    /// Number of strong references.
106    reader_count: usize,
107    /// Number of writer references.
108    writer_count: usize,
109    /// Reference to the storage.
110    storage: &'static T,
111    /// Reference to the address in storage.
112    storage_address: u32,
113    /// Offset from the base address; only used for writer.
114    current_offset: u32,
115    /// Destructor that will be called when the last strong reference is dropped.
116    transition: Box<dyn FnOnce(FileContentTransition) + 'static + Send + Sync>,
117    // We need to track this in memory because the flags in memory-mapped flash will be reset when a new file is created in the same place
118    /// Set if the file has been deleted.
119    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/// Represents the state of a file.
133///
134/// See [File] for more information.
135#[derive(ConstParamTy, PartialEq, Eq, Clone, Debug)]
136pub enum FileState {
137    /// Obtain a writer by creating a new file.
138    Writer,
139    /// Can be obtained by upgrading a weak reference.
140    Reader,
141    /// Can always be obtained by downgrading.
142    Weak,
143}
144
145/// Provides a safe interface to read and write files.
146///
147/// The interface resembles a reference counted smart pointer, but for files.
148///
149/// Depending on the STATE parameter, the file can be read from or written to.
150///
151/// A file has possible states:
152/// - Reader: Can be read from, but not written to.
153/// - Writer: Can be written to, but not read from.
154/// - Weak: Can be upgraded to a reader.
155///
156/// It resembles a reference-counted smart pointer with an mutex inside. A writer can
157/// only be obtained by creating a new file. By calling [commit] on a writer, you
158/// finalize the file. From this point onwards you can only read from the file.
159/// If you want to edit the file, you need to erase it and create a new file.
160///
161/// # Safety
162///
163/// Files are backed by memory-mapped storage and readers provide direct access to that
164/// storage. This creates a potential problem when the file is deleted and readers suddenly
165/// point to storage that does not contain valid data anymore. To prevent this from happening,
166/// File resembles a reference-counted smart pointer. Only when the last reader is dropped,
167/// the file is deleted. After a file has been deleted it is no longer possible to upgrade a
168/// weak reference to a reader.
169pub 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    /// Create a new file content with the given memory area.
177    ///
178    /// It is only safe to call this function if there are no other instances pointing to the file.
179    // TODO: Make this function unsafe or the argument a &'static [u8]
180    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            // Delete the file if it was marked for deletion
212            unsafe {
213                let _ = file.internal_delete();
214            };
215            return Err(ReadFileError::FileWasDeleted);
216        }
217
218        if metadata.deleted() {
219            // This should only happen if a deletion was interrupted by a crash or something.
220            unsafe {
221                let _ = file.internal_delete();
222            };
223            return Err(ReadFileError::FileWasDeleted);
224        };
225
226        Ok(file)
227    }
228
229    /// Read a file from storage.
230    ///
231    /// `address` is an address that can be used with storage.
232    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    /// Get the name of the file as a string slice.
247    pub fn name_str(&self) -> &str {
248        self.metadata.name_str()
249    }
250}
251
252impl<T: Storage + 'static> File<T, { FileState::Writer }> {
253    /// Create a new file writer with the given memory area.
254    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            // For now I allow this as this should never happen
275        }
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    /// Create a new file and return a writer.
298    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    /// Commit the file content and convert it to a reader.
320    ///
321    /// This will finalize the file and make it read-only.
322    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    /// Creates a new weak pointer to this data.
345    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    /// Creates a new strong pointer to this data.
357    ///
358    /// The file will not be deleted while you hold any strong reference to it. For this reason, it is best to only store the strong reference if you really need the file.
359    ///
360    /// Upgrading will always fail if the data has been marked for deletion.
361    ///
362    /// Upgrading weak references will fail if there are no strong references left.
363    ///
364    /// Upgrading a writer will always fail. Use commit instead.
365    ///
366    /// Upgrading will always fail while there is a writer alive.
367    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    /// Check if the data will be dropped if this reference is dropped.
391    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    /// Get the number of readers.
403    pub fn reader_count(&self) -> usize {
404        return unsafe { self.info.as_ref().read().unwrap().reader_count };
405    }
406
407    /// Get the number of writers.
408    pub fn writer_count(&self) -> usize {
409        return unsafe { self.info.as_ref().read().unwrap().writer_count };
410    }
411
412    /// Check if the file is marked for deletion.
413    pub fn marked_for_deletion(&self) -> bool {
414        self.metadata.marked_for_deletion()
415    }
416
417    /// Check if the file is deleted.
418    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    /// Check if the file is ready.
427    pub fn ready(&self) -> bool {
428        self.metadata.ready()
429    }
430
431    /// Mark this file for deletion.
432    ///
433    /// No new strong references can be created to a file that's marked for deletion, except with clone on a strong reference.
434    ///
435    /// If there are no strong references left, the file will be deleted right away.
436    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    /// Internal delete function that does not consume the file.
452    ///
453    /// Any access to this file afterwards is not safe.
454    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        // TODO: Make sure the block with the metadata gets erased last
474        info.storage.erase(info.storage_address, length)?;
475        Ok(())
476    }
477
478    /// Zero out the backing storage of this file and mark it as deleted.
479    ///
480    /// Only safe if no further reads or writes will be performed to the file.
481    pub fn delete(self) -> Result<(), DeleteFileContentError> {
482        unsafe { self.internal_delete() }
483    }
484
485    /// Check if the backing storage for the file is completely zeroed out.
486    ///
487    /// A valid file should never be zeroed, so this is marked as unsafe.
488    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("content", &self.content)
504            .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                // We cant really handle a failed deletion here
572                // TODO: maybe log it
573                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    /// The same as [std::io::Write::write] but you can only flip bits from 1 to 0.
623    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        // Dropping the weak does nothing
813        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}