use deku::{DekuRead, DekuWrite};
use super::error::SfsError;
use crate::celled::Celled;
use crate::dev::Device;
use crate::dev::address::Address;
use crate::error::Error;
use crate::fs::error::FsError;
pub const SFS_SIGNATURE: [u8; 3] = [0x53, 0x46, 0x53];
pub const SUPER_BLOCK_START_BYTE: usize = 0x0194;
pub const SUPER_BLOCK_SIZE: usize = 42;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Area {
SuperBlock,
Reserved,
Data,
Free,
Index,
}
#[derive(Debug, Clone, Copy, DekuRead, DekuWrite)]
pub struct SuperBlock {
pub time_stamp: i64,
pub data_size: u64,
pub index_size: u64,
pub magic: [u8; 3],
pub version: u8,
pub total_blocks: u64,
pub rsvd_blocks: u32,
pub block_size: u8,
pub crc: u8,
}
impl SuperBlock {
pub fn parse<Dev: Device>(celled_device: &Celled<Dev>) -> Result<Self, Error<SfsError>> {
let mut device = celled_device.lock();
let superblock = device.read_from_bytes::<Self>(Address::from(SUPER_BLOCK_START_BYTE), SUPER_BLOCK_SIZE)?;
if superblock.crc != superblock.checksum_control() {
Err(Error::Fs(FsError::Implementation(SfsError::BadChecksum {
expected: superblock.checksum_control(),
given: superblock.crc,
})))
} else if superblock.index_size.is_multiple_of(64) {
Err(Error::Fs(FsError::Implementation(SfsError::BadIndexAreaSize(superblock.index_size))))
} else if superblock.block_size == 0 {
Err(Error::Fs(FsError::Implementation(SfsError::BadBlockSize(superblock.block_size))))
} else {
Ok(superblock)
}
}
#[must_use]
pub const fn bytes_per_block(&self) -> u32 {
1 << ((self.block_size as u32) + 7)
}
#[must_use]
pub const fn superblock_area_size(&self) -> u32 {
1
}
#[must_use]
pub const fn reserved_area_size(&self) -> u32 {
self.rsvd_blocks - self.superblock_area_size()
}
#[must_use]
pub const fn filesystem_size(&self) -> u64 {
self.total_blocks * (self.bytes_per_block() as u64)
}
#[must_use]
pub const fn index_area_first_block(&self) -> u64 {
self.total_blocks
- (self.index_size / (self.bytes_per_block() as u64))
- if self.index_size.is_multiple_of(self.bytes_per_block() as u64) { 0 } else { 1 }
}
#[must_use]
pub const fn checksum_control(&self) -> u8 {
let mut checksum = 0_u8;
let mut i = 0;
while i < 3 {
checksum = checksum.wrapping_add(self.magic[i]);
i += 1;
}
checksum = checksum.wrapping_add(self.version);
let mut i = 0;
while i < 8 {
checksum = checksum.wrapping_add(((self.total_blocks >> (8 * i)) & 0xFF) as u8);
i += 1;
}
let mut i = 0;
while i < 4 {
checksum = checksum.wrapping_add(((self.rsvd_blocks >> (8 * i)) & 0xFF) as u8);
i += 1;
}
checksum = checksum.wrapping_add(self.block_size);
0_u8.wrapping_sub(checksum)
}
#[must_use]
pub const fn block_area(&self, block: u64) -> Option<Area> {
if block < (self.superblock_area_size() as u64) {
Some(Area::SuperBlock)
} else if block < (self.rsvd_blocks as u64) {
Some(Area::Reserved)
} else if block < (self.rsvd_blocks as u64) + self.data_size {
Some(Area::Data)
} else if block < self.index_area_first_block() {
Some(Area::Free)
} else if block < self.total_blocks {
Some(Area::Index)
} else {
None
}
}
#[must_use]
pub const fn is_block_in_data_area(&self, block: u64) -> bool {
matches!(self.block_area(block), Some(Area::Data))
}
#[must_use]
pub const fn index_area_starting_addr(&self) -> Address {
let total_bytes = self.total_blocks * (self.bytes_per_block() as u64);
Address::new(total_bytes - self.index_size)
}
}
#[cfg(test)]
mod test {
use spin::Lazy;
use crate::fs::sfs::super_block::{Area, SuperBlock};
use crate::fs::sfs::time_stamp::TimeStamp;
static TEST_SUPER_BLOCK: Lazy<SuperBlock> = Lazy::new(|| SuperBlock {
time_stamp: *TimeStamp::now().unwrap(),
data_size: 5,
index_size: 10 * 64,
magic: *b"SFS",
version: 0x10,
total_blocks: 20,
rsvd_blocks: 3,
block_size: 1,
crc: 0xEC,
});
#[test]
fn super_block_values() {
assert_eq!(TEST_SUPER_BLOCK.crc, TEST_SUPER_BLOCK.checksum_control());
assert_eq!(TEST_SUPER_BLOCK.bytes_per_block(), 256);
assert_eq!(TEST_SUPER_BLOCK.superblock_area_size(), 1);
assert_eq!(TEST_SUPER_BLOCK.reserved_area_size(), 2);
assert_eq!(TEST_SUPER_BLOCK.index_area_first_block(), 17);
}
#[test]
fn blocks_areas() {
assert_eq!(TEST_SUPER_BLOCK.block_area(0).unwrap(), Area::SuperBlock);
assert_eq!(TEST_SUPER_BLOCK.block_area(1).unwrap(), Area::Reserved);
assert_eq!(TEST_SUPER_BLOCK.block_area(2).unwrap(), Area::Reserved);
assert_eq!(TEST_SUPER_BLOCK.block_area(3).unwrap(), Area::Data);
assert_eq!(TEST_SUPER_BLOCK.block_area(7).unwrap(), Area::Data);
assert_eq!(TEST_SUPER_BLOCK.block_area(8).unwrap(), Area::Free);
assert_eq!(TEST_SUPER_BLOCK.block_area(16).unwrap(), Area::Free);
assert_eq!(TEST_SUPER_BLOCK.block_area(17).unwrap(), Area::Index);
assert_eq!(TEST_SUPER_BLOCK.block_area(19).unwrap(), Area::Index);
assert_eq!(TEST_SUPER_BLOCK.block_area(20), None);
}
}