VFS: path resolution

This commit is contained in:
TheOddGarlic 2022-08-07 15:35:55 +03:00
parent 960da43383
commit 0cc1aaa64f
7 changed files with 325 additions and 250 deletions

View file

@ -9,9 +9,12 @@ pub enum FsError {
EndOfFile,
InodeNotFound,
InvalidDevice,
InvalidPath,
IsDirectory,
NotAbsolute,
NotADirectory,
NotFound,
Recursion,
UnsupportedOperation,
}

View file

@ -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<S, V>
@ -23,7 +23,7 @@ where
{
fs: Synced<Ext2<S, V>>,
device_handle: Handle,
root: Arc<FsNode>,
root_handle: Handle,
}
impl<S, V> Ext2StorageDevice<S, V>
@ -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<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);
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<FsNode> {
self.root.clone()
fn root(&self) -> Handle {
self.root_handle
}
fn device_handle(&self) -> Handle {

View file

@ -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<u8>) -> Result<()>;
fn write(&self, node: &FsNode, offset: usize, buffer: &[u8]) -> Result<()>;
fn read_dir(&self, node: &FsNode, index: usize) -> Result<DirectoryEntry>;
fn find_dir(&self, node: &FsNode, name: &str) -> Result<Handle>;
fn find_dir(&self, vfs: &mut VirtualFileSystem, node: &FsNode, name: &str) -> Result<Handle>;
// TODO: flush to disk
fn root_node(&self) -> Arc<FsNode>;
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<Size1024, Vec<u8>>> {
Ext2StorageDevice::new(volume)
}
pub fn walk<S: Into<String>>(dir: Arc<FsNode>, 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<S: Into<String>>(dir: Arc<FsNode>, 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()));
}
}
}

View file

@ -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<VirtualFileSystem> = Default::default();
pub static ref VFS: RwLock<VirtualFileSystem> = Default::default();
}
pub struct VirtualFileSystem {
fs_nodes: HashMap<Handle, Arc<FsNode>>,
root_node: Weak<FsNode>,
root_handle: Option<Handle>,
}
impl VirtualFileSystem {
pub fn init(&mut self, root_node: Arc<FsNode>) {
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<S: AsRef<str>>(&mut self, path: S) -> Result<Handle> {
// 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<FsNode>) -> Handle {
@ -47,7 +106,7 @@ impl VirtualFileSystem {
})
}
pub fn fs_node(&mut self, handle: Handle) -> Option<Arc<FsNode>> {
pub fn fs_node(&self, handle: Handle) -> Option<Arc<FsNode>> {
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<Handle> {
pub fn find_dir(&self, vfs: &mut VirtualFileSystem, name: &str) -> Result<Handle> {
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<Self>) -> Option<Directory> {
@ -220,7 +280,7 @@ impl Iterator for Directory {
}
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct DirectoryEntry {
name: String,
node: Handle,

View file

@ -7,14 +7,14 @@
use crate::arch::drivers::sysinfo::master;
use crate::arch::interrupts::{reset_pit_for_cpu, set_pit_2};
use crate::devices::pci::brute_force_scan;
use crate::filesystem::vfs::VFS;
use crate::filesystem;
use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE};
use crate::time::fetch_time;
use crate::{filesystem, KERNEL_STATE};
use crate::{
arch::shutdown,
rhai_shell::KEYBUFF,
vterm::VTerm,
KERNEL_STATE,
// wasm_jumploader::run_program,
};
@ -85,7 +85,7 @@ pub fn scratchpad() {
disable();
let tick_time = fetch_time();
let hostname = &KERNEL_STATE.lock().hostname;
let hostname = KERNEL_STATE.lock().hostname.clone();
let allocator = ALLOCATOR.lock();
let size = allocator.size();
@ -122,13 +122,6 @@ pub fn scratchpad() {
BANNER_WIDTH
);
let root = {
let vfs = VFS.lock();
vfs.root_node()
};
filesystem::walk(root, "");
real_shell();
}
@ -248,6 +241,7 @@ pub fn command_parser(user: String, command: String) {
// }
"test" => {}
"quit" => shutdown(),
"tree" => filesystem::tree("/").unwrap(),
_ => {
// let mut options = OpenOptions::new();