forked from AbleOS/ableos
178 lines
5.1 KiB
Rust
178 lines
5.1 KiB
Rust
/*
|
|
* 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
|
|
}
|