1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! Simple Rust File System (core library)
//!
//! A simple filesystem impremented in Rust \[no_std\].
//! 
//! This crate is a work-in-progress. It contains low-level
//! code to work directly with block devices (see trait SyncBlockDevice).
//! 
//! Higher-level API, dependent on \[std\], lives in crate srfs.
//! 
//! All basic filesystem features are implemented (see ```struct SyncFileSystem```),
//! with provisions for extensions.
//! 
//! At the moment only synchronous interface is provided.
//! See src/tests.rs for usage examples.
//! 
//! TODO:
//! 
//! * crash recovery
//! * timestamps
//! * async API
//! 
//! Contributions are welcome.

#![cfg_attr(not(all(feature = "std", test)), no_std)]

extern crate alloc;

#[cfg(any(feature = "std", test))]
pub mod file_block_device;

mod block_cache;
mod fs_sync;
mod layout;

#[cfg(test)]
extern crate std;

#[cfg(test)]
mod tests;

pub use fs_sync::*;
pub use layout::*;

pub const BLOCK_SIZE: u64 = 4096;

// The number below is somewhat arbitrary, but we don't want it to be
// too large, as having it at, say, 2^35 will make looking up an item
// take forever, and we don't want to stall an OS by having a bad/corrupted FS.
pub const MAX_DIR_ENTRIES: u64 = 65536;
pub const MAX_FILE_SIZE: u64 = MAX_BYTES_LIST_OF_LISTS_BLOCKS; // ~500G.

/// See <https://en.wikipedia.org/wiki/Partition_type>.
/// We use an arbitrary unused number here.
pub const PARTITION_ID: u8 = 0x2d;

/// Synchronous Block Device.
pub trait SyncBlockDevice {
    /// The number of blocks in this device.
    fn num_blocks(&self) -> u64;

    /// Read a single block into buf.
    /// buf must be aligned to BLOCK_SIZE and of length BLOCK_SIZE.
    fn read_block(&mut self, block_no: u64, buf: &mut [u8]) -> Result<(), FsError>;

    /// Write a single block. Same alignment requirements as in read_block.
    fn write_block(&mut self, block_no: u64, buf: &[u8]) -> Result<(), FsError>;
}

/// Initializes the block device so that it has an SFFS with a single/empty root dir.
pub fn format(block_device: &mut dyn SyncBlockDevice) -> Result<(), FsError> {
    fs_sync::format(block_device)
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FsError {
    AlreadyExists,
    FsFull,
    InvalidArgument,
    IoError,
    NotFound,
    TooLarge,
    UnsupportedVersion,
    Utf8Error,
    ValidationFailed,
}