#![cfg_attr(not(feature="std"), no_std)]
#![cfg_attr(feature="nightly", feature(const_mut_refs))]
#[cfg(feature = "defmt")]
use defmt::{debug, info, trace, warn, error};
#[cfg(not(feature = "defmt"))]
use log::{debug, info, trace, warn, error};
use packing::{Packed, PackedSize};
use usbd_scsi::{BlockDevice, BlockDeviceError};
mod config;
pub use config::Config;
mod file;
pub use file::{File, FileContent, DynamicFile};
mod boot;
use boot::FatBootBlock;
mod dir;
use dir::DirectoryEntry;
const ASCII_SPACE: u8 = 0x20;
pub struct GhostFat<'a, const BLOCK_SIZE: usize = 512> {
config: Config<BLOCK_SIZE>,
fat_boot_block: FatBootBlock,
pub(crate) fat_files: &'a mut [File<'a, BLOCK_SIZE>],
}
impl <'a, const BLOCK_SIZE: usize> GhostFat<'a, BLOCK_SIZE> {
pub fn new(files: &'a mut [File<'a, BLOCK_SIZE>], config: Config<BLOCK_SIZE>) -> Self {
debug!("Configuring ghostfat with {} {} byte sectors ({} byte total), {} sector FATs", config.num_blocks, BLOCK_SIZE, config.num_blocks as usize * BLOCK_SIZE, config.sectors_per_fat());
Self {
fat_boot_block: FatBootBlock::new(&config),
fat_files: files,
config,
}
}
fn fat(id: usize, files: &[File<BLOCK_SIZE>], block: &mut [u8]){
let mut index = 0;
for b in block.iter_mut() {
*b = 0;
}
if id == 0 {
block[0] = 0xf0;
block[1] = 0xff;
block[2] = 0xff;
block[3] = 0xff;
index = 2;
}
let cluster_offset = id * BLOCK_SIZE / 2;
let mut block_index = 2;
for f in files.iter() {
let block_count = f.num_blocks();
if (block_index + block_count < cluster_offset) || (block_index > cluster_offset + BLOCK_SIZE/1) {
block_index += block_count;
continue;
}
if cluster_offset >= block_index + block_count {
block_index += block_count;
continue;
}
debug!("FAT {} File: '{}' {} clusters starting at cluster {}", id, f.name(), block_count, block_index);
let (file_offset, remainder) = if cluster_offset > block_index {
(cluster_offset - block_index, block_count + block_index - cluster_offset)
} else {
(0, block_count)
};
let blocks = usize::min(remainder, (BLOCK_SIZE / 2) - (index % BLOCK_SIZE));
debug!("FAT offset: {} file offset: {} remainder: {} clusters: {}", cluster_offset, file_offset, remainder, blocks);
for i in 0..blocks {
let j = i * 2;
let v: u16 = if remainder == blocks && i == blocks-1 {
0xFFFF
} else {
(block_index + file_offset + i + 1) as u16
};
block[index * 2 + j] = v as u8;
block[index * 2 + j + 1] = (v >> 8) as u8;
}
index += blocks;
block_index += blocks;
}
}
}
impl <'a, const BLOCK_SIZE: usize>BlockDevice for GhostFat<'a, BLOCK_SIZE> {
const BLOCK_BYTES: usize = BLOCK_SIZE;
fn read_block(&self, lba: u32, block: &mut [u8]) -> Result<(), BlockDeviceError> {
assert_eq!(block.len(), Self::BLOCK_BYTES);
trace!("GhostFAT reading lba: {} ({} bytes)", lba, block.len());
for b in block.iter_mut() {
*b = 0
}
if lba == 0 {
self.fat_boot_block
.pack(&mut block[..FatBootBlock::BYTES])
.unwrap();
block[510] = 0x55;
block[511] = 0xAA;
} else if lba < self.config.start_rootdir() {
let mut section_index = lba - self.config.start_fat0();
debug!("Read FAT section index: {} (lba: {})", section_index, lba);
if section_index >= self.config.sectors_per_fat() {
section_index -= self.config.sectors_per_fat();
}
Self::fat(section_index as usize, &self.fat_files, block);
trace!("FAT {}: {:?}", section_index, &block);
} else if lba < self.config.start_clusters() {
let section_index = lba - self.config.start_rootdir();
if section_index == 0 {
let mut dir = DirectoryEntry::default();
dir.name.copy_from_slice(&self.fat_boot_block.volume_label);
dir.attrs = 0x28;
let len = DirectoryEntry::BYTES;
dir.pack(&mut block[..len]).unwrap();
dir.attrs = 0;
let mut cluster_index = 2;
for (i, info) in self.fat_files.iter().enumerate() {
let mut block_count = info.len() / Self::BLOCK_BYTES;
if info.len() % Self::BLOCK_BYTES != 0 {
block_count += 1;
}
dir.start_cluster = cluster_index as u16;
dir.name.copy_from_slice(&info.short_name().unwrap());
dir.size = info.len() as u32;
dir.attrs = info.attrs().bits();
let start = (i + 1) * len;
dir.pack(&mut block[start..(start + len)]).unwrap();
cluster_index += block_count;
}
}
} else {
let section_index = (lba - self.config.start_clusters()) as usize;
debug!("Read cluster index: 0x{:04x} (lba: 0x{:04x})", section_index, lba);
let mut block_index = 0;
for f in self.fat_files.iter() {
let mut block_count = f.len() / Self::BLOCK_BYTES;
if f.len() % Self::BLOCK_BYTES != 0 {
block_count += 1;
}
if section_index < block_count + block_index {
let offset = section_index - block_index;
debug!("Read file: {} chunk: 0x{:02x}", f.name(), offset);
if f.chunk(offset, block) == 0 {
warn!("Failed to read file: {} chunk: {}", f.name(), offset);
}
return Ok(())
}
block_index += block_count;
}
warn!("Unhandled cluster read 0x{:04x} (lba: 0x{:04x})", section_index, lba);
}
Ok(())
}
fn write_block(&mut self, lba: u32, block: &[u8]) -> Result<(), BlockDeviceError> {
debug!("GhostFAT writing lba: {} ({} bytes)", lba, block.len());
if lba == 0 {
warn!("Attempted write to boot sector");
return Ok(());
} else if lba < self.config.start_rootdir() {
warn!("Attempted to write to FAT");
} else if lba < self.config.start_clusters() {
warn!("Attempted to write directory entries");
let section_index = lba - self.config.start_rootdir();
if section_index == 0 {
}
} else {
let section_index = (lba - self.config.start_clusters()) as usize;
let mut block_index = 0;
for f in self.fat_files.iter_mut() {
let mut block_count = f.len() / Self::BLOCK_BYTES;
if f.len() % Self::BLOCK_BYTES != 0 {
block_count += 1;
}
if section_index < block_count + block_index {
let offset = section_index - block_index;
debug!("Write file: {} block: {}, {} bytes", f.name(), offset, block.len());
if f.chunk_mut(offset, &block) == 0 {
error!("Attempted to write to read-only file");
return Err(BlockDeviceError::WriteError);
}
return Ok(())
}
block_index += block_count;
}
debug!("Unhandled write section: {}", section_index);
}
Ok(())
}
fn max_lba(&self) -> u32 {
self.config.num_blocks - 1
}
}
#[cfg(test)]
mod tests {
use crate::{GhostFat, File};
#[test]
fn file_offsets() {
let data = [0xAAu8; 64];
let f = [File::<8>::new_ro("test.bin", &data)];
assert_eq!(f[0].len(), data.len());
let mut block = [0u8; 8];
GhostFat::fat(0, &f, &mut block);
println!("FAT0: {:02x?}", block);
assert_eq!(&block, &[
0xf0, 0xff, 0xff, 0xff,
0x03, 0x00, 0x04, 0x00]);
GhostFat::fat(1, &f, &mut block);
println!("FAT1: {:02x?}", block);
assert_eq!(&block, &[
0x05, 0x00, 0x06, 0x00,
0x07, 0x00, 0x08, 0x00]);
GhostFat::fat(2, &f, &mut block);
println!("FAT2: {:02x?}", block);
assert_eq!(&block, &[
0x09, 0x00, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00]);
assert!(true);
}
}