152 lines
4.6 KiB
Rust
152 lines
4.6 KiB
Rust
#![feature(iterator_try_collect, iter_order_by)]
|
|
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
|
|
|
use core::cell::RefCell;
|
|
use core::fmt::Display;
|
|
|
|
use crate::{
|
|
boot_sector::Fat32BootSector,
|
|
consts::FAT32_CLUSTER_MASK,
|
|
dir::Dir,
|
|
entry::{DirEntry, FatEntry},
|
|
error::Error,
|
|
file::{File, RawFile},
|
|
path::Path,
|
|
};
|
|
|
|
use io::{Read, ReadLeExt, Seek, Write};
|
|
|
|
#[cfg(feature = "alloc")]
|
|
extern crate alloc;
|
|
|
|
pub mod boot_sector;
|
|
pub mod consts;
|
|
pub mod dir;
|
|
pub mod entry;
|
|
pub mod error;
|
|
pub mod file;
|
|
pub mod path;
|
|
|
|
pub trait ReadSeek: Read + Seek {}
|
|
impl<T: Read + Seek> ReadSeek for T {}
|
|
pub trait WriteSeek: Write + Seek {}
|
|
impl<T: Write + Seek> WriteSeek for T {}
|
|
pub trait ReadWriteSeek: Read + Write + Seek {}
|
|
impl<T: Read + Write + Seek> ReadWriteSeek for T {}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Fat32FsInfo {
|
|
pub lead_signature: u32,
|
|
pub reserved1: [u8; 480],
|
|
pub struct_signature: u32,
|
|
pub free_count: u32,
|
|
pub next_free: u32,
|
|
pub reserved2: [u8; 12],
|
|
pub trail_signature: u32,
|
|
}
|
|
|
|
impl Fat32FsInfo {
|
|
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, Error<T::Error>> {
|
|
let lead_signature = disk.read_u32_le()?;
|
|
let mut reserved1 = [0u8; _];
|
|
disk.read_exact(&mut reserved1)?;
|
|
let struct_signature = disk.read_u32_le()?;
|
|
let free_count = disk.read_u32_le()?;
|
|
let next_free = disk.read_u32_le()?;
|
|
let mut reserved2 = [0u8; _];
|
|
disk.read_exact(&mut reserved2)?;
|
|
let trail_signature = disk.read_u32_le()?;
|
|
Ok(Self {
|
|
lead_signature,
|
|
reserved1,
|
|
struct_signature,
|
|
free_count,
|
|
next_free,
|
|
reserved2,
|
|
trail_signature,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Fat32FileSystem<T> {
|
|
device: RefCell<T>,
|
|
pub boot_sector: Fat32BootSector,
|
|
}
|
|
|
|
impl<T> Display for Fat32FileSystem<T> {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
self.boot_sector.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl<T: ReadSeek> Fat32FileSystem<T> {
|
|
pub fn new(mut device: T) -> Result<Self, Error<T::Error>> {
|
|
device.seek(io::SeekFrom::Start(0))?;
|
|
|
|
let boot_sector = Fat32BootSector::deserialize(&mut device)?;
|
|
Ok(Self {
|
|
device: device.into(),
|
|
boot_sector,
|
|
})
|
|
}
|
|
/// Get the next cluster from the current one
|
|
fn get_next_cluster(&self, current_cluster: u32) -> Result<u32, Error<T::Error>> {
|
|
let fat_offset =
|
|
self.fat_start_offset() + (current_cluster as u64 * size_of::<FatEntry>() as u64);
|
|
self.device
|
|
.borrow_mut()
|
|
.seek(io::SeekFrom::Start(fat_offset))?;
|
|
|
|
let next = self.device.borrow_mut().read_u32_le()? & FAT32_CLUSTER_MASK;
|
|
Ok(next)
|
|
}
|
|
}
|
|
impl<T> Fat32FileSystem<T> {
|
|
/// Start offset of the FAT
|
|
pub fn fat_start_offset(&self) -> u64 {
|
|
(self.boot_sector.reserved_sector_count as u64) * (self.boot_sector.bytes_per_sector as u64)
|
|
}
|
|
/// Start offset for data
|
|
fn data_start_offset(&self) -> u64 {
|
|
let fat_size = self.boot_sector.fat_size_32 as u64;
|
|
let fats = (self.boot_sector.num_fats as u64) * fat_size;
|
|
|
|
fats * self.boot_sector.bytes_per_sector as u64 + self.fat_start_offset()
|
|
}
|
|
|
|
/// Convert a cluster number to an offset
|
|
fn cluster_to_offset(&self, cluster: u32) -> u64 {
|
|
let cluster_offset = (cluster.saturating_sub(2)) as u64
|
|
* self.boot_sector.sectors_per_cluster as u64
|
|
* self.boot_sector.bytes_per_sector as u64;
|
|
|
|
self.data_start_offset() + cluster_offset
|
|
}
|
|
|
|
fn cluster_size(&self) -> u64 {
|
|
(self.boot_sector.sectors_per_cluster as u64) * (self.boot_sector.bytes_per_sector as u64)
|
|
}
|
|
|
|
pub fn root_directory(&self) -> Dir<'_, T> {
|
|
Dir::new(
|
|
RawFile::new(self, self.boot_sector.root_cluster, None),
|
|
self,
|
|
)
|
|
}
|
|
}
|
|
impl<T: ReadSeek> Fat32FileSystem<T> {
|
|
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'_, T>, Error<T::Error>> {
|
|
let path = path.as_ref().as_str().trim_start_matches("/");
|
|
self.root_directory().open_entry(path)
|
|
}
|
|
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Dir<'_, T>, Error<T::Error>> {
|
|
let path = path.as_ref().as_str().trim_start_matches("/");
|
|
self.root_directory().open_dir(path)
|
|
}
|
|
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'_, T>, Error<T::Error>> {
|
|
let path = path.as_ref().as_str().trim_start_matches("/");
|
|
self.root_directory().open_file(path)
|
|
}
|
|
}
|