buf-fs 0.1.3

A buffer based, in-memory filesystem.
Documentation
use core::cell::RefCell;

use alloc::{rc::Rc, vec, vec::Vec};
use embedded_sdmmc::{
    Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp, VolumeManager,
};

use crate::mbr;

/// A MBR FAT-16 compatible device emulator.
#[derive(Default, Clone)]
pub struct Device {
    bytes: Rc<RefCell<Vec<u8>>>,
    resize_chunk: usize,
    min_len: usize,
    max_blocks: usize,
}

impl Device {
    /// The minimum size in bytes supported by the FAT-16.
    pub const MIN_SIZE: usize = 2_141_184;

    /// The maximum size in bytes supported by the FAT-16.
    pub const MAX_SIZE: usize = 2 * 1024 * 1024 * 1024;

    /// Attempts to create a new device with the provided amount of bytes.
    pub fn new(size: usize) -> anyhow::Result<Self> {
        anyhow::ensure!(
            Self::MIN_SIZE <= size,
            "the provided amount of bytes `{size}` is below the lower limit of `{}`",
            Self::MIN_SIZE
        );

        anyhow::ensure!(
            size <= Self::MAX_SIZE,
            "the provided amount of bytes `{size}` is above the upper limit of `{}`",
            Self::MAX_SIZE
        );

        let bytes = mbr::create_mbr_with_fat(size)?;

        Ok(Self {
            bytes: Rc::new(RefCell::new(bytes)),
            resize_chunk: 0,
            min_len: 0,
            max_blocks: 0,
        })
    }

    /// Attempts to serialize the structure into raw device (MBR+part0) bytes.
    ///
    /// The only failure is if the device is borrowed mutably elsewhere.
    pub fn try_to_raw_bytes(&self) -> anyhow::Result<Vec<u8>> {
        Ok(self.bytes.try_borrow()?.clone())
    }

    /// Attempts to deserialize the structure from raw device (MBR+FAT16).
    ///
    /// This device can be mounted using any fstools such as fdisk and mkfs.fat.
    pub fn from_raw_bytes_unchecked(bytes: Vec<u8>) -> Self {
        Self {
            bytes: Rc::new(RefCell::new(bytes)),
            resize_chunk: 0,
            min_len: 0,
            max_blocks: 0,
        }
    }

    /// Serializes the structure into bytes.
    pub fn try_to_bytes(&self) -> anyhow::Result<Vec<u8>> {
        let bytes = self.bytes.try_borrow()?;
        let mut buffer = vec![0u8; bytes.len() + 24];

        buffer[0..8].copy_from_slice(&(self.resize_chunk as u64).to_le_bytes());
        buffer[8..16].copy_from_slice(&(self.min_len as u64).to_le_bytes());
        buffer[16..24].copy_from_slice(&(self.max_blocks as u64).to_le_bytes());
        buffer[24..].copy_from_slice(&bytes);

        Ok(buffer)
    }

    /// Deserializes the structure into bytes.
    pub fn try_from_bytes(buffer: &[u8]) -> anyhow::Result<Self> {
        anyhow::ensure!(buffer.len() > 24, "buffer is too small.");

        let mut resize_chunk = [0u8; 8];
        let mut min_len = [0u8; 8];
        let mut max_blocks = [0u8; 8];

        resize_chunk.copy_from_slice(&buffer[..8]);
        min_len.copy_from_slice(&buffer[8..16]);
        max_blocks.copy_from_slice(&buffer[16..24]);

        let bytes = buffer[24..].to_vec();

        Ok(Self {
            bytes: Rc::new(RefCell::new(bytes)),
            resize_chunk: u64::from_le_bytes(resize_chunk) as usize,
            min_len: u64::from_le_bytes(min_len) as usize,
            max_blocks: u64::from_le_bytes(max_blocks) as usize,
        })
    }

    /// Creates a device with the provided minimum length of the buffer.
    pub fn with_min_len(mut self, min_len: usize) -> Self {
        self.min_len = min_len;
        self
    }

    /// Creates a device with the provided maximum length of the buffer.
    pub fn with_max_len(mut self, max_len: usize) -> Self {
        self.max_blocks = max_len / Block::LEN;
        self
    }

    /// Creates a device with the provided minimum chunk length increase.
    pub fn with_resize_chunk(mut self, resize_chunk: usize) -> Self {
        self.resize_chunk = resize_chunk;
        self
    }

    /// Opens the device into a MBR volumes manager.
    pub fn open(self) -> VolumeManager<Self, Clock> {
        VolumeManager::new(self, Clock::default())
    }
}

impl BlockDevice for Device {
    type Error = anyhow::Error;

    fn read(
        &self,
        blocks: &mut [Block],
        start_block_idx: BlockIdx,
        reason: &str,
    ) -> anyhow::Result<()> {
        let idx = start_block_idx.0 as usize;
        let idx = idx
            .checked_mul(Block::LEN)
            .ok_or_else(|| anyhow::anyhow!("block idx overflow: {reason}"))?;

        let bytes = self
            .bytes
            .try_borrow()
            .map_err(|e| anyhow::anyhow!("device blocked `{e}`: {reason}"))?;

        for i in 0..blocks.len() {
            let ofs = idx + i * Block::LEN;
            let end = ofs + Block::LEN;

            if end <= bytes.len() {
                blocks[i].contents.copy_from_slice(&bytes[ofs..end]);
            } else {
                blocks[i].contents.fill(0);
            }
        }

        Ok(())
    }

    fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> anyhow::Result<()> {
        let idx = start_block_idx.0 as usize;
        let idx = idx
            .checked_mul(Block::LEN)
            .ok_or_else(|| anyhow::anyhow!("block idx overflow"))?;

        let len = blocks
            .len()
            .checked_mul(Block::LEN)
            .ok_or_else(|| anyhow::anyhow!("block len overflow"))?;

        let last = idx
            .checked_add(len)
            .ok_or_else(|| anyhow::anyhow!("block last overflow"))?;

        let mut bytes = self.bytes.try_borrow_mut()?;

        if last > bytes.len() {
            anyhow::ensure!(
                self.max_blocks == 0 || last <= self.max_blocks * Block::LEN,
                "write attempt to overflow maximum length"
            );

            let last = last.max(self.min_len).max(bytes.len() + self.resize_chunk);

            bytes.resize(last, 0);
        }

        for i in 0..blocks.len() {
            let ofs = idx + i * Block::LEN;

            bytes[ofs..ofs + Block::LEN].copy_from_slice(&blocks[i].contents);
        }

        Ok(())
    }

    fn num_blocks(&self) -> anyhow::Result<BlockCount> {
        let len = self.bytes.try_borrow()?.len();
        let len = len / Block::LEN;

        Ok(BlockCount(len as u32))
    }
}

/// A monotonically increasing clock emulator.
///
/// We don't use real time as it is incompatible with several embedded systesm.
#[derive(Debug)]
pub struct Clock {
    ts: RefCell<Timestamp>,
    def: Timestamp,
}

impl Default for Clock {
    fn default() -> Self {
        let ts = Timestamp::from_calendar(1980, 01, 01, 13, 30, 05).expect("invalid initial ts");

        Self {
            ts: RefCell::new(ts),
            def: ts,
        }
    }
}

impl TimeSource for Clock {
    fn get_timestamp(&self) -> Timestamp {
        let ts = { self.ts.try_borrow().map(|t| *t).unwrap_or(self.def) };

        {
            let mut tsp = ts;

            tsp.seconds += 1;
            if tsp.seconds == 60 {
                tsp.minutes += 1;
                tsp.seconds = 0;
            }
            if tsp.minutes == 60 {
                tsp.hours += 1;
                tsp.minutes = 0;
            }
            if tsp.hours == 24 {
                tsp.zero_indexed_day += 1;
                tsp.hours = 0;
            }
            if tsp.zero_indexed_day == 27 {
                tsp.zero_indexed_month += 1;
                tsp.zero_indexed_day = 0;
            }
            if tsp.zero_indexed_month == 12 {
                tsp.year_since_1970 += 1;
                tsp.zero_indexed_month = 0;
            }

            if let Ok(mut t) = self.ts.try_borrow_mut() {
                *t = tsp;
            }
        }

        ts
    }
}