diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs index d0ce762e..3f2fb406 100644 --- a/ableos/src/filesystem/errors.rs +++ b/ableos/src/filesystem/errors.rs @@ -9,9 +9,12 @@ pub enum FsError { EndOfFile, InodeNotFound, InvalidDevice, + InvalidPath, IsDirectory, + NotAbsolute, NotADirectory, NotFound, + Recursion, UnsupportedOperation, } diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index 1140ecdb..82f93bd4 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -13,7 +13,7 @@ use ext2::volume::Volume; use crate::handle::{Handle, HandleResource}; use super::errors::FsError; -use super::vfs::{DirectoryEntry, FsFlags, VFS}; +use super::vfs::{DirectoryEntry, FsFlags, VirtualFileSystem, VFS}; use super::{FsNode, FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice @@ -23,7 +23,7 @@ where { fs: Synced>, device_handle: Handle, - root: Arc, + root_handle: Handle, } impl Ext2StorageDevice @@ -43,13 +43,13 @@ where Weak::new(), )); - let mut vfs = VFS.lock(); - vfs.add_fs_node(root.clone()); + let mut vfs = VFS.write(); + let root_handle = vfs.add_fs_node(root.clone()); Ok(Self { fs, device_handle, - root, + root_handle, }) } } @@ -94,7 +94,7 @@ where .fs .inode_nth(entry.inode) .ok_or_else(|| FsError::InodeNotFound)?; - let mut vfs = VFS.lock(); + let mut vfs = VFS.write(); let entry_node_handle = vfs .find_fs_node(entry.inode, self.device_handle) .unwrap_or_else(|| { @@ -113,14 +113,13 @@ where )) } - fn find_dir(&self, node: &FsNode, name: &str) -> Result { + fn find_dir(&self, vfs: &mut VirtualFileSystem, node: &FsNode, name: &str) -> Result { 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); - let mut vfs = VFS.lock(); for entry in dir { if entry.is_err() { continue; @@ -139,14 +138,15 @@ where Weak::new(), ))) })); + break; } } found_node } - fn root_node(&self) -> Arc { - self.root.clone() + fn root(&self) -> Handle { + self.root_handle } fn device_handle(&self) -> Handle { diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 76a529fc..bbd076a3 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -16,7 +16,7 @@ use crate::{filesystem::vfs::VFS, handle::Handle, KERNEL_STATE}; use self::{ errors::FsError, ext2::Ext2StorageDevice, - vfs::{DirectoryEntry, FsNode}, + vfs::{DirectoryEntry, FsNode, VirtualFileSystem}, }; use FsResult as Result; @@ -30,18 +30,18 @@ where fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec) -> Result<()>; fn write(&self, node: &FsNode, offset: usize, buffer: &[u8]) -> Result<()>; fn read_dir(&self, node: &FsNode, index: usize) -> Result; - fn find_dir(&self, node: &FsNode, name: &str) -> Result; + fn find_dir(&self, vfs: &mut VirtualFileSystem, node: &FsNode, name: &str) -> Result; // TODO: flush to disk - fn root_node(&self) -> Arc; + fn root(&self) -> Handle; fn device_handle(&self) -> Handle; } pub fn init() -> Result<()> { let mut state = KERNEL_STATE.lock(); let fs = load_fs()?; - let mut vfs = VFS.lock(); - vfs.init(fs.root_node()); + let mut vfs = VFS.write(); + vfs.set_root(fs.root())?; state.add_storage_device(fs); Ok(()) } @@ -53,19 +53,37 @@ fn load_fs() -> Result>> { Ext2StorageDevice::new(volume) } -pub fn walk>(dir: Arc, path: S) { +pub fn tree(path: &str) -> Result<()> { + let dir = { + let mut vfs = VFS.write(); + let handle = vfs.resolve(path)?; + vfs.fs_node(handle).ok_or_else(|| FsError::NotFound)? + }; + + tree_inner( + dir, + if path.starts_with('/') { + &path[1..] + } else { + path + }, + ); + Ok(()) +} + +fn tree_inner>(dir: Arc, path: S) { let path = path.into(); if let Some(dir) = dir.directory() { for entry in dir { let fs_node = { - let mut vfs = VFS.lock(); + let vfs = VFS.read(); vfs.fs_node(entry.node()).unwrap() }; println!("{}/{} => {}", path, entry.name(), fs_node.inode()); trace!("{entry:#?}"); if entry.name() != "." && entry.name() != ".." { - walk(fs_node, format!("{}/{}", path, entry.name())); + tree_inner(fs_node, format!("{}/{}", path, entry.name())); } } } diff --git a/ableos/src/filesystem/vfs.rs b/ableos/src/filesystem/vfs.rs index 5d6b4aec..e1cc3f94 100644 --- a/ableos/src/filesystem/vfs.rs +++ b/ableos/src/filesystem/vfs.rs @@ -9,7 +9,7 @@ use core::cmp; use alloc::sync::{Arc, Weak}; use hashbrown::HashMap; use lazy_static::lazy_static; -use spin::Mutex; +use spin::RwLock; use super::{errors::FsError, FsResult as Result}; use crate::{ @@ -17,18 +17,77 @@ use crate::{ KERNEL_STATE, }; +/// The limit for symlink recursion. In POSIX, this is at least 8. In Linux, this is 40. +const SYMLINK_RECURSION_LIMIT: u8 = 8; + lazy_static! { - pub static ref VFS: Mutex = Default::default(); + pub static ref VFS: RwLock = Default::default(); } pub struct VirtualFileSystem { fs_nodes: HashMap>, root_node: Weak, + root_handle: Option, } impl VirtualFileSystem { - pub fn init(&mut self, root_node: Arc) { - self.root_node = Arc::downgrade(&root_node) + /// Sets the VFS root to the given VFS node handle. + pub fn set_root(&mut self, handle: Handle) -> Result<()> { + let root_node = self.fs_node(handle).ok_or_else(|| FsError::NotFound)?; + self.root_node = Arc::downgrade(&root_node); + self.root_handle = Some(handle); + Ok(()) + } + + /// Resolves the path to a handle. If the resulting node is a symlink, + /// the symlink itself is returned. All symlinks but the resulting node + /// are traversed. + /// + /// Requires a mutable reference because internally it might open new VFS + /// nodes while resolving the path. + pub fn resolve>(&mut self, path: S) -> Result { + // TODO: caching + let path = path.as_ref(); + + if !path.starts_with('/') { + // FIXME: use current process working directory for relative paths? + Err(FsError::NotAbsolute)?; + } + + let mut components = path.split_terminator(path); + components.next(); // throw the empty string caused by the root / + // will be initialised beforehand so okay to unwrap + let mut resolved_node = self.root_handle.unwrap(); + // let mut symlink_recursion_level = 0; + for component in components { + // if symlink_recursion_level >= SYMLINK_RECURSION_LIMIT { + // Err(FsError::Recursion)?; + // } + + if component == "" { + Err(FsError::InvalidPath)?; + } + + // checked by previous iteration so okay to unwrap + let parent = self.fs_node(resolved_node).unwrap(); + + // TODO: permission checks + + // FIXME: find_dir checks that this is a directory already but + // that's just more boilerplate in StorageDevice impls + // we should probably check that here instead to reduce + // required boilerplate + // if !parent.is_dir() { + // Err(FsError::NotADirectory)?; + // } + + // FIXME: handle mount points + // FIXME: handle symlinks + + resolved_node = parent.find_dir(self, component)?; + } + + Ok(resolved_node) } pub fn add_fs_node(&mut self, fs_node: Arc) -> Handle { @@ -47,7 +106,7 @@ impl VirtualFileSystem { }) } - pub fn fs_node(&mut self, handle: Handle) -> Option> { + pub fn fs_node(&self, handle: Handle) -> Option> { self.fs_nodes.get(&handle).cloned() } @@ -62,6 +121,7 @@ impl Default for VirtualFileSystem { Self { fs_nodes: HashMap::new(), root_node: Weak::new(), + root_handle: None, } } } @@ -151,13 +211,13 @@ impl FsNode { device.read_dir(self, index) } - pub fn find_dir(&self, name: &str) -> Result { + pub fn find_dir(&self, vfs: &mut VirtualFileSystem, name: &str) -> Result { let state = KERNEL_STATE.lock(); let device = state .storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; - device.find_dir(self, name) + device.find_dir(vfs, self, name) } pub fn directory(self: Arc) -> Option { @@ -220,7 +280,7 @@ impl Iterator for Directory { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct DirectoryEntry { name: String, node: Handle, diff --git a/ableos/src/relib/time/kilotime.rs b/ableos/src/relib/time/kilotime.rs index bb39533e..06cae00d 100644 --- a/ableos/src/relib/time/kilotime.rs +++ b/ableos/src/relib/time/kilotime.rs @@ -5,57 +5,57 @@ use core::fmt::{Display, Error, Formatter}; #[repr(transparent)] pub struct Kilosecond(u32); impl Kilosecond { - pub fn from_ms(ms: u32) -> Self { - Self(ms) - } + pub fn from_ms(ms: u32) -> Self { + Self(ms) + } - pub fn from_sec(sec: u32) -> Self { - Self(sec * 1000) - } + pub fn from_sec(sec: u32) -> Self { + Self(sec * 1000) + } - pub fn from_minutes(min: u32) -> Self { - Self(min * 60 * 1000) - } + pub fn from_minutes(min: u32) -> Self { + Self(min * 60 * 1000) + } - pub fn from_hours(hrs: u32) -> Self { - Self(hrs * 60 * 60 * 1000) - } + pub fn from_hours(hrs: u32) -> Self { + Self(hrs * 60 * 60 * 1000) + } - pub fn from_days(days: u32) -> Self { - Self(days * 24 * 60 * 60 * 1000) - } + pub fn from_days(days: u32) -> Self { + Self(days * 24 * 60 * 60 * 1000) + } - pub fn s(&self) -> u32 { - (self.0 % 1000) - } + pub fn s(&self) -> u32 { + (self.0 % 1000) + } - pub fn k(&self) -> u32 { - self.0 / 1000 - } + pub fn k(&self) -> u32 { + self.0 / 1000 + } } impl Display for Kilosecond { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - write![f, "{}K {}S", self.k(), self.s()] - } + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write![f, "{}K {}S", self.k(), self.s()] + } } impl core::ops::Add for Kilosecond { - type Output = Self; - fn add(self, rhs: Self) -> Self { - Self(self.0 + rhs.0) - } + type Output = Self; + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } } impl core::ops::Sub for Kilosecond { - type Output = Self; - fn sub(self, rhs: Self) -> Self { - Self(self.0 - rhs.0) - } + type Output = Self; + fn sub(self, rhs: Self) -> Self { + Self(self.0 - rhs.0) + } } impl From