From b01ce11fab69495083a247fa88e2b812cc53c5c3 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Fri, 5 Aug 2022 09:16:34 +0300 Subject: [PATCH] recursively walk directories --- ableos/src/filesystem/ext2.rs | 23 ---- ableos/src/filesystem/mod.rs | 16 ++- ableos/src/filesystem/vfs.rs | 219 ++++++++++++++++++++++++++++++++++ ableos/src/kernel_state.rs | 2 +- ableos/src/scratchpad.rs | 10 +- 5 files changed, 244 insertions(+), 26 deletions(-) create mode 100644 ableos/src/filesystem/vfs.rs diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index 7e8e44b1..5fd9928b 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -166,26 +166,3 @@ fn ext2_type_to_fs_flags(type_perm: TypePerm) -> FsFlags { t | s } - -// pub fn walk>( -// fs: &Synced>, -// inode: Inode, -// name: String, -// ) { -// if let Some(dir) = inode.directory() { -// for entry in dir { -// assert!(entry.is_ok()); -// let entry = entry.unwrap(); -// let entry_name = String::from_utf8_lossy(&entry.name); - -// println!("{}/{} => {}", name, entry_name, entry.inode,); -// if entry_name != "." && entry_name != ".." { -// walk( -// fs, -// fs.inode_nth(entry.inode).unwrap(), -// format!("{}/{}", name, entry_name), -// ); -// } -// } -// } -// } diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 284f226c..bc382016 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -11,7 +11,7 @@ pub mod vfs; use ::ext2::sector::Size1024; use alloc::sync::Arc; -use crate::{handle::Handle, KERNEL_STATE}; +use crate::{filesystem::vfs::fs_node, handle::Handle, KERNEL_STATE}; use self::{ errors::FsError, @@ -49,3 +49,17 @@ fn load_fs() -> Result>> { Ext2StorageDevice::new(volume) } + +pub fn walk>(dir: Arc, path: S) { + let path = path.into(); + if let Some(dir) = dir.directory() { + for entry in dir { + let fs_node = 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())); + } + } + } +} diff --git a/ableos/src/filesystem/vfs.rs b/ableos/src/filesystem/vfs.rs new file mode 100644 index 00000000..00624d5a --- /dev/null +++ b/ableos/src/filesystem/vfs.rs @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2022, Umut İnan Erdoğan + * + * SPDX-License-Identifier: MPL-2.0 + */ + +use core::cmp; + +use alloc::sync::{Arc, Weak}; +use hashbrown::HashMap; +use lazy_static::lazy_static; +use spin::Mutex; + +use super::{errors::FsError, FsResult as Result}; +use crate::{ + handle::{Handle, HandleResource}, + KERNEL_STATE, +}; + +lazy_static! { + static ref FS_NODES: Mutex>> = Default::default(); +} + +pub fn add_fs_node(fs_node: Arc) -> Handle { + let handle = Handle::new(HandleResource::FsNode); + let mut nodes = FS_NODES.lock(); + nodes.insert(handle, fs_node); + handle +} + +pub fn find_fs_node(inode: usize, device_handle: Handle) -> Option { + let nodes = FS_NODES.lock(); + nodes.iter().find_map(|(handle, fs_node)| { + if fs_node.inode == inode && fs_node.device_handle == device_handle { + Some(*handle) + } else { + None + } + }) +} + +pub fn fs_node(handle: Handle) -> Option> { + let nodes = FS_NODES.lock(); + nodes.get(&handle).cloned() +} + +/// A VFS node, that can either be a file or a directory. +/// +/// VFS nodes are created whenever a file that doesn't have an open VFS node is +/// opened. When there are no open file descriptors to a file, the associated +/// VFS node is dropped. +#[derive(Debug)] +pub struct FsNode { + flags: FsFlags, + length: usize, // in bytes + inode: usize, // implementation specific identifier for the node + device_handle: Handle, // uniquely assigned device handle + ptr: Weak, // used by mountpoints and symlinks + // TODO: permissions mask + // TODO: owning user/group +} + +impl FsNode { + pub fn new( + flags: FsFlags, + length: usize, + inode: usize, + device_handle: Handle, + ptr: Weak, + ) -> Self { + Self { + flags, + length, + inode, + device_handle, + ptr, + } + } + + /// This method opens a new file descriptor for this VFS node. + // TODO: make this take flags + pub fn open(self: Arc) -> Result { + let mut state = KERNEL_STATE.lock(); + let handle = state.open_file_descriptor(self); + + Ok(handle) + } + + /// This method reads from the VFS node without opening a new file + /// descriptor. This is intended to be used internally, if you're trying to + /// read a file then you probably want to read from a file descriptor. + pub fn read(&self, offset: usize, size: usize, buffer: &mut Vec) -> Result { + let state = KERNEL_STATE.lock(); + let device = state + .storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + if self.is_dir() { + Err(FsError::IsDirectory)?; + } + + if offset > self.length { + Err(FsError::EndOfFile)?; + } + + let readable_size = cmp::min(size, self.length - offset); + device.read(self, offset, readable_size, buffer)?; + Ok(readable_size) + } + + /// This method writes to the VFS node without opening a new file + /// descriptor. This is intended to be used internally, if you're trying to + /// write to a file then you probably want to write to a file descriptor. + pub fn write(&self, offset: usize, buffer: &[u8]) -> Result<()> { + let state = KERNEL_STATE.lock(); + let device = state + .storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.write(self, offset, buffer) + } + + pub fn read_dir(&self, index: usize) -> Result { + let state = KERNEL_STATE.lock(); + let device = state + .storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.read_dir(self, index) + } + + pub fn find_dir(&self, 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) + } + + pub fn directory(self: Arc) -> Option { + if self.is_dir() { + Some(Directory::new(self)) + } else { + None + } + } + + pub fn is_dir(&self) -> bool { + (self.flags & FsFlags::DIRECTORY) == FsFlags::DIRECTORY + } + + pub fn inode(&self) -> usize { + self.inode + } +} + +impl Drop for FsNode { + fn drop(&mut self) { + trace!("dropping VFS node: {self:#?}"); + // TODO: flush to disk here + } +} + +bitflags! { + /// Flags associated with VFS nodes and file descriptors + /// + /// 0x00000MST \ + /// T is set to 0 for files, 1 for directories \ + /// S is set when the file is a symbolic link \ + /// M is set if the file is an active mount point + pub struct FsFlags: u8 { + const FILE = 0b00000000; + const DIRECTORY = 0b00000001; + const SYMBOLIC_LINK = 0b00000010; + const MOUNT_POINT = 0b00000100; + } +} + +pub struct Directory { + node: Arc, + index: usize, +} + +impl Directory { + fn new(node: Arc) -> Self { + Self { node, index: 0 } + } +} + +impl Iterator for Directory { + type Item = DirectoryEntry; + + fn next(&mut self) -> Option { + let value = self.node.read_dir(self.index).ok(); + self.index += 1; + value + } +} + +#[derive(Debug)] +pub struct DirectoryEntry { + name: String, + node: Handle, +} + +impl DirectoryEntry { + pub(super) fn new(name: String, node: Handle) -> Self { + Self { name, node } + } + + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn node(&self) -> Handle { + self.node + } +} diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index 3a7736da..9b40ece0 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -18,7 +18,7 @@ pub static KERNEL_STATE: Lazy> = pub struct KernelInternalState { pub hostname: String, - storage_devices: HashMap>, + pub storage_devices: HashMap>, // FIXME: should this be per-process? file_table: HashMap, should_shutdown: bool, diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index b7b7fe6e..f545120b 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -16,6 +16,7 @@ use crate::{ vterm::Term, // wasm_jumploader::run_program, }; +use crate::{filesystem, KERNEL_STATE}; use acpi::{AcpiTables, PlatformInfo}; use cpuio::{inb, outb}; @@ -121,7 +122,14 @@ pub fn scratchpad() { BANNER_WIDTH ); - real_shell(); + let root = { + let state = KERNEL_STATE.lock(); + state.storage_devices.iter().next().unwrap().1.root_node() + }; + + filesystem::walk(root, ""); + + // real_shell(); } pub fn acpi() {