use alloc::{boxed::Box, sync::Arc};
use core::cell::OnceCell;
use ax_fs_vfs::{
VfsDirEntry, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, VfsOps,
VfsResult,
};
use axfatfs::{Dir, File, LossyOemCpConverter, NullTimeProvider, Read, Seek, SeekFrom, Write};
use spin::Mutex;
use crate::dev::{Disk, Partition};
const BLOCK_SIZE: usize = 512;
pub struct FatFileSystem {
inner: axfatfs::FileSystem<PartitionWrapper, NullTimeProvider, LossyOemCpConverter>,
root_dir: OnceCell<VfsNodeRef>,
}
pub struct PartitionWrapper {
partition: Partition,
}
impl PartitionWrapper {
pub fn new(partition: Partition) -> Self {
Self { partition }
}
}
impl axfatfs::IoBase for PartitionWrapper {
type Error = ();
}
impl axfatfs::Read for PartitionWrapper {
fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Self::Error> {
let mut read_len = 0;
while !buf.is_empty() {
match self.partition.read_one(buf) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
read_len += n;
}
Err(_) => return Err(()),
}
}
Ok(read_len)
}
}
impl axfatfs::Write for PartitionWrapper {
fn write(&mut self, mut buf: &[u8]) -> Result<usize, Self::Error> {
let mut write_len = 0;
while !buf.is_empty() {
match self.partition.write_one(buf) {
Ok(0) => break,
Ok(n) => {
buf = &buf[n..];
write_len += n;
}
Err(_) => return Err(()),
}
}
Ok(write_len)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl axfatfs::Seek for PartitionWrapper {
fn seek(&mut self, pos: axfatfs::SeekFrom) -> Result<u64, Self::Error> {
let size = self.partition.size();
let new_pos = match pos {
axfatfs::SeekFrom::Start(pos) => Some(pos),
axfatfs::SeekFrom::Current(off) => self.partition.position().checked_add_signed(off),
axfatfs::SeekFrom::End(off) => size.checked_add_signed(off),
}
.ok_or(())?;
if new_pos > size {
warn!("Seek beyond the end of the partition");
}
self.partition.set_position(new_pos);
Ok(new_pos)
}
}
pub struct FileWrapper<'a>(
Mutex<File<'a, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
);
pub struct DirWrapper<'a>(Dir<'a, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>);
unsafe impl Sync for FatFileSystem {}
unsafe impl Send for FatFileSystem {}
unsafe impl Send for FileWrapper<'_> {}
unsafe impl Sync for FileWrapper<'_> {}
unsafe impl Send for DirWrapper<'_> {}
unsafe impl Sync for DirWrapper<'_> {}
impl FatFileSystem {
#[allow(dead_code)]
pub fn new(disk: Disk) -> Self {
let disk_size = disk.size();
let wrapper = PartitionWrapper::new(crate::dev::Partition::new(disk, 0, disk_size / 512));
let inner = axfatfs::FileSystem::new(wrapper, axfatfs::FsOptions::new())
.expect("failed to initialize FAT filesystem");
Self {
inner,
root_dir: OnceCell::new(),
}
}
pub fn from_partition(partition: Partition) -> Self {
let wrapper = PartitionWrapper::new(partition);
let inner = axfatfs::FileSystem::new(wrapper, axfatfs::FsOptions::new())
.expect("failed to initialize FAT filesystem on partition");
Self {
inner,
root_dir: OnceCell::new(),
}
}
#[allow(dead_code)]
pub fn init(&'static self) {
}
fn new_file(
file: File<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>,
) -> VfsNodeRef {
let file_box = Box::new(file);
let file_static = unsafe {
core::mem::transmute::<
Box<File<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
Box<File<'static, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
>(file_box)
};
let file_wrapper = FileWrapper(Mutex::new(*file_static));
Arc::new(file_wrapper) as VfsNodeRef
}
fn new_dir(
dir: Dir<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>,
) -> VfsNodeRef {
let dir_box = Box::new(dir);
let dir_static = unsafe {
core::mem::transmute::<
Box<Dir<'_, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
Box<Dir<'static, PartitionWrapper, NullTimeProvider, LossyOemCpConverter>>,
>(dir_box)
};
let dir_wrapper = DirWrapper(*dir_static);
Arc::new(dir_wrapper) as VfsNodeRef
}
}
impl VfsNodeOps for FileWrapper<'static> {
ax_fs_vfs::impl_vfs_non_dir_default! {}
fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
let size = self.0.lock().seek(SeekFrom::End(0)).map_err(as_vfs_err)?;
let blocks = size.div_ceil(BLOCK_SIZE as u64);
let perm = VfsNodePerm::from_bits_truncate(0o755);
Ok(VfsNodeAttr::new(perm, VfsNodeType::File, size, blocks))
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult<usize> {
let mut file = self.0.lock();
file.seek(SeekFrom::Start(offset)).map_err(as_vfs_err)?; file.read(buf).map_err(as_vfs_err)
}
fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult<usize> {
let mut file = self.0.lock();
file.seek(SeekFrom::Start(offset)).map_err(as_vfs_err)?; file.write(buf).map_err(as_vfs_err)
}
fn truncate(&self, size: u64) -> VfsResult {
let mut file = self.0.lock();
let current_size = file.seek(SeekFrom::End(0)).map_err(as_vfs_err)?;
if size <= current_size {
file.seek(SeekFrom::Start(size)).map_err(as_vfs_err)?; file.truncate().map_err(as_vfs_err)
} else {
let mut zeros_needed = size - current_size;
let zeros = [0u8; 4096];
while zeros_needed > 0 {
let to_write = core::cmp::min(zeros_needed, zeros.len() as u64);
let write_buf = &zeros[..to_write as usize];
file.write(write_buf).map_err(as_vfs_err)?;
zeros_needed -= to_write;
}
Ok(())
}
}
}
impl VfsNodeOps for DirWrapper<'static> {
ax_fs_vfs::impl_vfs_dir_default! {}
fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
Ok(VfsNodeAttr::new(
VfsNodePerm::from_bits_truncate(0o755),
VfsNodeType::Dir,
BLOCK_SIZE as u64,
1,
))
}
fn parent(&self) -> Option<VfsNodeRef> {
self.0
.open_dir("..")
.map_or(None, |dir| Some(FatFileSystem::new_dir(dir)))
}
fn lookup(self: Arc<Self>, path: &str) -> VfsResult<VfsNodeRef> {
debug!("lookup at axfatfs: {}", path);
let path = path.trim_matches('/');
if path.is_empty() || path == "." {
return Ok(self.clone());
}
if let Some(rest) = path.strip_prefix("./") {
return self.lookup(rest);
}
if let Ok(file) = self.0.open_file(path) {
Ok(FatFileSystem::new_file(file))
} else if let Ok(dir) = self.0.open_dir(path) {
Ok(FatFileSystem::new_dir(dir))
} else {
Err(VfsError::NotFound)
}
}
fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult {
debug!("create {:?} at axfatfs: {}", ty, path);
let path = path.trim_matches('/');
if path.is_empty() || path == "." {
return Ok(());
}
if let Some(rest) = path.strip_prefix("./") {
return self.create(rest, ty);
}
match ty {
VfsNodeType::File => {
self.0.create_file(path).map_err(as_vfs_err)?;
Ok(())
}
VfsNodeType::Dir => {
self.0.create_dir(path).map_err(as_vfs_err)?;
Ok(())
}
_ => Err(VfsError::Unsupported),
}
}
fn remove(&self, path: &str) -> VfsResult {
debug!("remove at axfatfs: {}", path);
let path = path.trim_matches('/');
assert!(!path.is_empty()); if let Some(rest) = path.strip_prefix("./") {
return self.remove(rest);
}
self.0.remove(path).map_err(as_vfs_err)
}
fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult<usize> {
let mut iter = self.0.iter().skip(start_idx);
for (i, out_entry) in dirents.iter_mut().enumerate() {
let x = iter.next();
match x {
Some(Ok(entry)) => {
let ty = if entry.is_dir() {
VfsNodeType::Dir
} else if entry.is_file() {
VfsNodeType::File
} else {
unreachable!()
};
*out_entry = VfsDirEntry::new(&entry.file_name(), ty);
}
_ => return Ok(i),
}
}
Ok(dirents.len())
}
fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult {
debug!(
"rename at axfatfs, src_path: {}, dst_path: {}",
src_path, dst_path
);
self.0
.rename(src_path, &self.0, dst_path)
.map_err(as_vfs_err)
}
}
impl VfsOps for FatFileSystem {
fn root_dir(&self) -> VfsNodeRef {
self.root_dir
.get_or_init(|| {
debug!("Creating root directory for FAT filesystem");
let root_dir = self.inner.root_dir();
debug!("Successfully got root directory from FAT filesystem");
Self::new_dir(root_dir)
})
.clone()
}
}
impl axfatfs::IoBase for Disk {
type Error = ();
}
impl Read for Disk {
fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Self::Error> {
let mut read_len = 0;
while !buf.is_empty() {
match self.read_one(buf) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
read_len += n;
}
Err(_) => return Err(()),
}
}
Ok(read_len)
}
}
impl Write for Disk {
fn write(&mut self, mut buf: &[u8]) -> Result<usize, Self::Error> {
let mut write_len = 0;
while !buf.is_empty() {
match self.write_one(buf) {
Ok(0) => break,
Ok(n) => {
buf = &buf[n..];
write_len += n;
}
Err(_) => return Err(()),
}
}
Ok(write_len)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl Seek for Disk {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
let size = self.size();
let new_pos = match pos {
SeekFrom::Start(pos) => Some(pos),
SeekFrom::Current(off) => self.position().checked_add_signed(off),
SeekFrom::End(off) => size.checked_add_signed(off),
}
.ok_or(())?;
if new_pos > size {
warn!("Seek beyond the end of the block device");
}
self.set_position(new_pos);
Ok(new_pos)
}
}
const fn as_vfs_err(err: axfatfs::Error<()>) -> VfsError {
use axfatfs::Error::*;
match err {
AlreadyExists => VfsError::AlreadyExists,
CorruptedFileSystem => VfsError::InvalidData,
DirectoryIsNotEmpty => VfsError::DirectoryNotEmpty,
InvalidInput | InvalidFileNameLength | UnsupportedFileNameCharacter => {
VfsError::InvalidInput
}
NotEnoughSpace => VfsError::StorageFull,
NotFound => VfsError::NotFound,
UnexpectedEof => VfsError::UnexpectedEof,
WriteZero => VfsError::WriteZero,
Io(_) => VfsError::Io,
_ => VfsError::Io,
}
}