/* * Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me> * * SPDX-License-Identifier: MPL-2.0 */ use alloc::sync::{Arc, Weak}; use ext2::fs::{sync::Synced, Ext2}; use ext2::sector::SectorSize; use ext2::sys::inode::TypePerm; use ext2::volume::Volume; use crate::handle::{Handle, HandleResource}; use super::errors::FsError; use super::vfs::{DirectoryEntry, FsFlags, VirtualFileSystem, VFS}; use super::{FsNode, FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice<S, V> where S: SectorSize, V: Volume<u8, S>, { fs: Synced<Ext2<S, V>>, device_handle: Handle, root_handle: Handle, } impl<S, V> Ext2StorageDevice<S, V> where S: SectorSize, V: Volume<u8, S>, { pub fn new(volume: V) -> Result<Self> { let fs = Synced::new(volume).map_err(|e| e.into())?; let root_inode = fs.root_inode(); let device_handle = Handle::new(HandleResource::StorageDevice); let root = Arc::new(FsNode::new( FsFlags::DIRECTORY, root_inode.size(), 2, device_handle, Weak::new(), )); let mut vfs = VFS.lock(); let root_handle = vfs.add_fs_node(root.clone()); Ok(Self { fs, device_handle, root_handle, }) } } impl<S, V> StorageDevice for Ext2StorageDevice<S, V> where S: SectorSize + Send, V: Volume<u8, S> + Send, { fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec<u8>) -> Result<()> { let inode = self .fs .inode_nth(node.inode() as usize) .ok_or_else(|| FsError::InodeNotFound)?; // FIXME: I don't really know how Ext2 works, so for now we don't // support non-zero offsets and buffer sizes that don't match // the file length. We always read the whole file. if offset > 0 || size != inode.size() { Err(FsError::UnsupportedOperation)?; } inode.read_to_end(buffer).map_err(|e| e.into())?; Ok(()) } fn write(&self, _node: &FsNode, _offset: usize, _buffer: &[u8]) -> Result<()> { todo!() } fn read_dir(&self, node: &FsNode, index: usize) -> Result<DirectoryEntry> { let inode = self .fs .inode_nth(node.inode() as usize) .ok_or_else(|| FsError::InodeNotFound)?; let mut dir = inode.directory().ok_or_else(|| FsError::NotADirectory)?; let entry = dir .nth(index) .ok_or_else(|| FsError::InodeNotFound)? .map_err(|e| e.into())?; let entry_inode = self .fs .inode_nth(entry.inode) .ok_or_else(|| FsError::InodeNotFound)?; let mut vfs = VFS.lock(); let entry_node_handle = vfs .find_fs_node(entry.inode, self.device_handle) .unwrap_or_else(|| { vfs.add_fs_node(Arc::new(FsNode::new( ext2_type_to_fs_flags(entry_inode.type_perm()), entry_inode.size(), entry.inode, self.device_handle, Weak::new(), ))) }); Ok(DirectoryEntry::new( entry.file_name_string(), entry_node_handle, )) } fn find_dir(&self, vfs: &mut VirtualFileSystem, node: &FsNode, name: &str) -> Result<Handle> { let inode = self .fs .inode_nth(node.inode() as usize) .ok_or_else(|| FsError::InodeNotFound)?; let dir = inode.directory().ok_or_else(|| FsError::NotADirectory)?; let mut found_node = Err(FsError::NotFound); for entry in dir { if entry.is_err() { continue; } let entry = entry.unwrap(); let entry_inode = self .fs .inode_nth(entry.inode as usize) .ok_or_else(|| FsError::InodeNotFound)?; if entry.file_name_string() == name { found_node = Ok(vfs .find_fs_node(entry.inode, self.device_handle) .unwrap_or_else(|| { vfs.add_fs_node(Arc::new(FsNode::new( ext2_type_to_fs_flags(entry_inode.type_perm()), entry_inode.size(), entry.inode, self.device_handle, Weak::new(), ))) })); break; } } found_node } fn root(&self) -> Handle { self.root_handle } fn device_handle(&self) -> Handle { self.device_handle } } fn ext2_type_to_fs_flags(type_perm: TypePerm) -> FsFlags { trace!("{type_perm:?}"); let is_directory = type_perm & TypePerm::DIRECTORY == TypePerm::DIRECTORY; let is_symlink = type_perm & TypePerm::SYMLINK == TypePerm::SYMLINK; let t = if is_directory { FsFlags::DIRECTORY } else { FsFlags::FILE }; let s = if is_symlink { FsFlags::SYMBOLIC_LINK } else { FsFlags::empty() }; t | s }