From 95004ae7aa57e6d82a9f05ba0d3cf8161e0e5c4b Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 12:53:02 +0300 Subject: [PATCH 01/20] feat: start VFS implementation --- ableos/Cargo.toml | 2 +- ableos/src/filesystem/errors.rs | 9 ++++ ableos/src/filesystem/mod.rs | 88 +++++++++++++++++++++++++++++++++ ableos/src/handle.rs | 15 ++++-- 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 ableos/src/filesystem/errors.rs diff --git a/ableos/Cargo.toml b/ableos/Cargo.toml index 665889b5..a50c735c 100644 --- a/ableos/Cargo.toml +++ b/ableos/Cargo.toml @@ -58,7 +58,7 @@ test-args = [ [dependencies] lazy_static = { version = "1.4.0", features = ["spin_no_std"] } qrcode = { path = "../qrcode-rust" } -bitflags = "1.2.1" +bitflags = "1.3" lliw = "0.2.0" spin = "0.9" pretty-hex = "0.2.1" diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs new file mode 100644 index 00000000..01b40a02 --- /dev/null +++ b/ableos/src/filesystem/errors.rs @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022, Umut İnan Erdoğan + * + * SPDX-License-Identifier: MPL-2.0 + */ + +pub enum FsError { + UnsupportedOperation, +} diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index b712e683..dfec8916 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2022, Umut İnan Erdoğan + * + * SPDX-License-Identifier: MPL-2.0 + */ + +pub mod errors; + +use alloc::rc::Weak; +use bitflags::bitflags; use ext2::{ fs::{ sync::{Inode, Synced}, @@ -8,6 +18,84 @@ use ext2::{ }; use spin::Lazy; +use crate::handle::Handle; + +use self::errors::FsError; + +pub type FsOpenOperation = fn(/* TODO: flags */) -> Result; +pub type FsCloseOperation = fn(fd: Handle) -> Result<(), FsError>; +pub type FsReadOperation = fn(fd: Handle, offset: u32, size: u32) -> Result, FsError>; +pub type FsWriteOperation = fn(fd: Handle, offset: u32, buffer: Box<[u8]>) -> Result<(), FsError>; + +/// A VFS node, that can either be a file or a directory. +pub struct FsNode { + // FIXME: move the file name into the directory listing to implement hard + // links + name: String, + flags: FsNodeFlags, + length: u32, // in bytes + inode: u32, // implementation specific identifier for the node + device_handle: Handle, // uniquely assigned device handle + ptr: Weak, // used by mountpoints and symlinks + open: Option, + close: Option, + read: Option, + write: Option, + // todo: permissions mask + // todo: owning user/group + // todo: readdir, finddir fn pointers +} + +impl FsNode { + // TODO: make this take flags + fn open(&self) -> Result { + if let Some(open) = self.open { + open() + } else { + Err(FsError::UnsupportedOperation) + } + } + + fn close(&self, fd: Handle) -> Result<(), FsError> { + if let Some(close) = self.close { + close(fd) + } else { + Err(FsError::UnsupportedOperation) + } + } + + fn read(&self, fd: Handle, offset: u32, size: u32) -> Result, FsError> { + if let Some(read) = self.read { + read(fd, offset, size) + } else { + Err(FsError::UnsupportedOperation) + } + } + + fn write(&self, fd: Handle, offset: u32, buffer: Box<[u8]>) -> Result<(), FsError> { + if let Some(write) = self.write { + write(fd, offset, buffer) + } else { + Err(FsError::UnsupportedOperation) + } + } +} + +bitflags! { + /// Flags associated with VFS nodes. + /// + /// 0x00000MST + /// T is set to 0 for files, 1 for directories + /// S is set when the node is a symbolic link + /// M is set if the node is an active mount point + pub struct FsNodeFlags: u8 { + const FILE = 0b00000000; + const DIRECTORY = 0b00000001; + const SYMBOLIC_LINK = 0b00000010; + const MOUNT_POINT = 0b00000100; + } +} + pub static FILE_SYSTEM: Lazy>>>> = Lazy::new(|| spin::Mutex::new(load_fs())); diff --git a/ableos/src/handle.rs b/ableos/src/handle.rs index dda62396..fb9f31db 100644 --- a/ableos/src/handle.rs +++ b/ableos/src/handle.rs @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022, Umut İnan Erdoğan + * + * SPDX-License-Identifier: MPL-2.0 + */ + //! A handle is a u128 with a set of permissions //! and a resource connected to it @@ -14,8 +20,9 @@ pub struct BinaryData { pub enum HandleResource { Channel, Socket, - BinaryData, + StorageDevice, + FileDescriptor, } #[derive(Debug, Eq, Hash, PartialEq, Clone, Copy)] @@ -30,16 +37,18 @@ impl Display for Handle { match &self.res { HandleResource::Channel => write!(f, "-Channel")?, HandleResource::BinaryData => write!(f, "-Binary")?, - Socket => write!(f, "-Socket")?, + HandleResource::Socket => write!(f, "-Socket")?, + HandleResource::StorageDevice => write!(f, "-StorageDevice")?, + HandleResource::FileDescriptor => write!(f, "-FileDescriptor")?, } Ok(()) } } -use crate::handle::HandleResource::*; impl Handle { pub fn new(htype: HandleResource) -> Self { + // FIXME: check if inner collides Self { inner: generate_process_pass(), res: htype, From 330c3cc7c2fe6a7256465a61705500591963e0f3 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 14:23:58 +0300 Subject: [PATCH 02/20] vfs: readdir and finddir operations in FsNode --- ableos/src/filesystem/mod.rs | 55 +++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index dfec8916..e32084d7 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -21,11 +21,18 @@ use spin::Lazy; use crate::handle::Handle; use self::errors::FsError; +use FsResult as Result; -pub type FsOpenOperation = fn(/* TODO: flags */) -> Result; -pub type FsCloseOperation = fn(fd: Handle) -> Result<(), FsError>; -pub type FsReadOperation = fn(fd: Handle, offset: u32, size: u32) -> Result, FsError>; -pub type FsWriteOperation = fn(fd: Handle, offset: u32, buffer: Box<[u8]>) -> Result<(), FsError>; +pub type FsResult = core::result::Result; + +pub type FsOpenOperation = fn(node: &FsNode /* TODO: flags */) -> Result; +pub type FsCloseOperation = fn(node: &FsNode) -> Result<()>; +pub type FsReadOperation = fn(node: &FsNode, offset: u32, size: u32) + -> Result>; +pub type FsWriteOperation = fn(node: &FsNode, offset: u32, buffer: Box<[u8]>) + -> Result<()>; +pub type FsReaddirOperation = fn(node: &FsNode, index: u32) -> Result; +pub type FsFinddirOperation = fn(node: &FsNode, name: &str) -> Result; /// A VFS node, that can either be a file or a directory. pub struct FsNode { @@ -41,40 +48,57 @@ pub struct FsNode { close: Option, read: Option, write: Option, + readdir: Option, + finddir: Option, // todo: permissions mask // todo: owning user/group - // todo: readdir, finddir fn pointers } impl FsNode { // TODO: make this take flags - fn open(&self) -> Result { + fn open(&self) -> Result { if let Some(open) = self.open { - open() + open(self) } else { Err(FsError::UnsupportedOperation) } } - fn close(&self, fd: Handle) -> Result<(), FsError> { + fn close(&self) -> Result<()> { if let Some(close) = self.close { - close(fd) + close(self) } else { Err(FsError::UnsupportedOperation) } } - fn read(&self, fd: Handle, offset: u32, size: u32) -> Result, FsError> { + fn read(&self, offset: u32, size: u32) -> Result> { if let Some(read) = self.read { - read(fd, offset, size) + read(self, offset, size) } else { Err(FsError::UnsupportedOperation) } } - fn write(&self, fd: Handle, offset: u32, buffer: Box<[u8]>) -> Result<(), FsError> { + fn write(&self, offset: u32, buffer: Box<[u8]>) -> Result<()> { if let Some(write) = self.write { - write(fd, offset, buffer) + write(self, offset, buffer) + } else { + Err(FsError::UnsupportedOperation) + } + } + + fn readdir(&self, index: u32) -> Result { + if let Some(readdir) = self.readdir { + readdir(self, index) + } else { + Err(FsError::UnsupportedOperation) + } + } + + fn finddir(&self, name: &str) -> Result { + if let Some(finddir) = self.finddir { + finddir(self, name) } else { Err(FsError::UnsupportedOperation) } @@ -96,6 +120,11 @@ bitflags! { } } +pub struct DirectoryEntry { + name: String, + inode: u32, +} + pub static FILE_SYSTEM: Lazy>>>> = Lazy::new(|| spin::Mutex::new(load_fs())); From a1f6bae9dfaf286ccdca75523d4604143354b11f Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 15:45:49 +0300 Subject: [PATCH 03/20] vfs: move operations into trait StorageDevice, hold StorageDevices in KERNEL_STATE --- ableos/src/filesystem/errors.rs | 17 ++++ ableos/src/filesystem/ext2.rs | 85 ++++++++++++++++ ableos/src/filesystem/mod.rs | 140 ++++++++++---------------- ableos/src/kernel_state.rs | 15 +++ ableos/src/rhai_shell/mod.rs | 112 ++++++++++----------- ableos/src/scratchpad.rs | 69 ++++++++----- ableos/src/wasm_jumploader/mod.rs | 158 +++++++++++++++--------------- ext2-rs/src/error.rs | 4 +- 8 files changed, 350 insertions(+), 250 deletions(-) create mode 100644 ableos/src/filesystem/ext2.rs diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs index 01b40a02..827bd956 100644 --- a/ableos/src/filesystem/errors.rs +++ b/ableos/src/filesystem/errors.rs @@ -6,4 +6,21 @@ pub enum FsError { UnsupportedOperation, + InvalidDevice, +} + +impl Into for ext2::error::Error { + fn into(self) -> FsError { + match self { + ext2::error::Error::Other(_) => todo!(), + ext2::error::Error::BadMagic { magic: _ } => todo!(), + ext2::error::Error::OutOfBounds { index: _ } => todo!(), + ext2::error::Error::AddressOutOfBounds { sector: _, offset: _, size: _ } => todo!(), + ext2::error::Error::BadBlockGroupCount { by_blocks: _, by_inodes: _ } => todo!(), + ext2::error::Error::InodeNotFound { inode: _ } => todo!(), + ext2::error::Error::NotADirectory { inode: _, name: _ } => todo!(), + ext2::error::Error::NotAbsolute { name: _ } => todo!(), + ext2::error::Error::NotFound { name: _ } => todo!(), + } + } } diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs new file mode 100644 index 00000000..831f4750 --- /dev/null +++ b/ableos/src/filesystem/ext2.rs @@ -0,0 +1,85 @@ +use ext2::fs::Ext2; +use ext2::sector::SectorSize; +use ext2::volume::Volume; + +use super::{FsResult as Result, StorageDevice}; + +pub struct Ext2StorageDevice +where + S: SectorSize, + V: Volume, +{ + fs: Ext2, +} + +impl Ext2StorageDevice +where + S: SectorSize, + V: Volume, +{ + pub fn new(volume: V) -> Result { + Ok(Self { + fs: Ext2::new(volume).map_err(|e| e.into())?, + }) + } +} + +impl StorageDevice for Ext2StorageDevice +where + S: SectorSize + Send, + V: Volume + Send, +{ + fn open(&self, node: &super::FsNode /* TODO: flags */) -> Result { + todo!() + } + + fn close(&self, node: &super::FsNode) -> Result<()> { + todo!() + } + + fn read(&self, node: &super::FsNode, offset: u32, size: u32) -> Result> { + todo!() + } + + fn write(&self, node: &super::FsNode, offset: u32, buffer: Box<[u8]>) -> Result<()> { + todo!() + } + + fn read_dir(&self, node: &super::FsNode, index: u32) -> Result { + todo!() + } + + fn find_dir(&self, node: &super::FsNode, name: &str) -> Result { + todo!() + } +} + +// fn load_fs() -> Arc>>> { +// let mut volume = Vec::new(); +// volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img")); + +// Arc::>::new(volume).unwrap() +// } + +// 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 e32084d7..36f3e571 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -5,34 +5,29 @@ */ pub mod errors; +pub mod ext2; use alloc::rc::Weak; use bitflags::bitflags; -use ext2::{ - fs::{ - sync::{Inode, Synced}, - Ext2, - }, - sector::{SectorSize, Size1024}, - volume::Volume, -}; -use spin::Lazy; -use crate::handle::Handle; +use crate::{handle::Handle, KERNEL_STATE}; use self::errors::FsError; use FsResult as Result; pub type FsResult = core::result::Result; -pub type FsOpenOperation = fn(node: &FsNode /* TODO: flags */) -> Result; -pub type FsCloseOperation = fn(node: &FsNode) -> Result<()>; -pub type FsReadOperation = fn(node: &FsNode, offset: u32, size: u32) - -> Result>; -pub type FsWriteOperation = fn(node: &FsNode, offset: u32, buffer: Box<[u8]>) - -> Result<()>; -pub type FsReaddirOperation = fn(node: &FsNode, index: u32) -> Result; -pub type FsFinddirOperation = fn(node: &FsNode, name: &str) -> Result; +pub trait StorageDevice +where + Self: Send, +{ + fn open(&self, node: &FsNode /* TODO: flags */) -> Result; + fn close(&self, node: &FsNode) -> Result<()>; + fn read(&self, node: &FsNode, offset: u32, size: u32) -> Result>; + fn write(&self, node: &FsNode, offset: u32, buffer: Box<[u8]>) -> Result<()>; + fn read_dir(&self, node: &FsNode, index: u32) -> Result; + fn find_dir(&self, node: &FsNode, name: &str) -> Result; +} /// A VFS node, that can either be a file or a directory. pub struct FsNode { @@ -44,12 +39,6 @@ pub struct FsNode { inode: u32, // implementation specific identifier for the node device_handle: Handle, // uniquely assigned device handle ptr: Weak, // used by mountpoints and symlinks - open: Option, - close: Option, - read: Option, - write: Option, - readdir: Option, - finddir: Option, // todo: permissions mask // todo: owning user/group } @@ -57,51 +46,57 @@ pub struct FsNode { impl FsNode { // TODO: make this take flags fn open(&self) -> Result { - if let Some(open) = self.open { - open(self) - } else { - Err(FsError::UnsupportedOperation) - } + let state = KERNEL_STATE.lock(); + let device = state + .get_storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.open(self) } fn close(&self) -> Result<()> { - if let Some(close) = self.close { - close(self) - } else { - Err(FsError::UnsupportedOperation) - } + let state = KERNEL_STATE.lock(); + let device = state + .get_storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.close(self) } fn read(&self, offset: u32, size: u32) -> Result> { - if let Some(read) = self.read { - read(self, offset, size) - } else { - Err(FsError::UnsupportedOperation) - } + let state = KERNEL_STATE.lock(); + let device = state + .get_storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.read(self, offset, size) } fn write(&self, offset: u32, buffer: Box<[u8]>) -> Result<()> { - if let Some(write) = self.write { - write(self, offset, buffer) - } else { - Err(FsError::UnsupportedOperation) - } + let state = KERNEL_STATE.lock(); + let device = state + .get_storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.write(self, offset, buffer) } - fn readdir(&self, index: u32) -> Result { - if let Some(readdir) = self.readdir { - readdir(self, index) - } else { - Err(FsError::UnsupportedOperation) - } + fn read_dir(&self, index: u32) -> Result { + let state = KERNEL_STATE.lock(); + let device = state + .get_storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.read_dir(self, index) } - fn finddir(&self, name: &str) -> Result { - if let Some(finddir) = self.finddir { - finddir(self, name) - } else { - Err(FsError::UnsupportedOperation) - } + fn find_dir(&self, name: &str) -> Result { + let state = KERNEL_STATE.lock(); + let device = state + .get_storage_device(self.device_handle) + .ok_or_else(|| FsError::InvalidDevice)?; + + device.find_dir(self, name) } } @@ -124,36 +119,3 @@ pub struct DirectoryEntry { name: String, inode: u32, } - -pub static FILE_SYSTEM: Lazy>>>> = - Lazy::new(|| spin::Mutex::new(load_fs())); - -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), - ); - } - } - } -} - -fn load_fs() -> Synced>> { - let mut volume = Vec::new(); - volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img")); - - Synced::>::new(volume).unwrap() -} diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index 52ec3712..25b27f48 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -1,10 +1,14 @@ +use hashbrown::HashMap; use spin::Lazy; +use crate::{handle::{Handle, HandleResource}, filesystem::StorageDevice}; + pub static KERNEL_STATE: Lazy> = Lazy::new(|| spin::Mutex::new(KernelInternalState::new())); pub struct KernelInternalState { pub hostname: String, + storage_devices: HashMap>, should_shutdown: bool, } @@ -12,6 +16,7 @@ impl KernelInternalState { pub fn new() -> Self { Self { should_shutdown: false, + storage_devices: HashMap::new(), hostname: "".to_string(), } } @@ -19,9 +24,19 @@ impl KernelInternalState { pub fn set_hostname(&mut self, hostname: String) { self.hostname = hostname; } + + pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) { + self.storage_devices.insert(Handle::new(HandleResource::StorageDevice), Box::new(device)); + } + + pub fn get_storage_device(&self, handle: Handle) -> Option<&dyn StorageDevice> { + self.storage_devices.get(&handle).map(|d| &**d) + } + pub fn shutdown(&mut self) { self.should_shutdown = true; } + pub fn update_state(&mut self) { if self.should_shutdown { crate::arch::shutdown(); diff --git a/ableos/src/rhai_shell/mod.rs b/ableos/src/rhai_shell/mod.rs index 2e1f20b9..9873aaec 100644 --- a/ableos/src/rhai_shell/mod.rs +++ b/ableos/src/rhai_shell/mod.rs @@ -1,7 +1,7 @@ use crate::arch::drivers::sysinfo::master; use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE}; -use crate::{filesystem::FILE_SYSTEM, time::fetch_time, KERNEL_STATE}; -use genfs::{Fs, OpenOptions}; +use crate::{time::fetch_time, KERNEL_STATE}; +// use genfs::{Fs, OpenOptions}; use kernel::allocator::ALLOCATOR; // use rhai::Engine; use spin::Lazy; @@ -103,23 +103,23 @@ pub fn poke_memory(ptr: i64, val: i64) { } } -pub fn ls() { - let current_dir = CURRENT_DIR.lock(); +// pub fn ls() { +// let current_dir = CURRENT_DIR.lock(); - let fs = &*FILE_SYSTEM.lock(); +// let fs = &*FILE_SYSTEM.lock(); - let file = fs - .open(current_dir.as_bytes(), OpenOptions::new().read(true)) - .unwrap(); +// let file = fs +// .open(current_dir.as_bytes(), OpenOptions::new().read(true)) +// .unwrap(); - let mut files = file.directory().unwrap(); - println!("current dir: {}", *current_dir); - while let Some(Ok(entry)) = files.next() { - let inode_name = entry.name; - let s = String::from_utf8_lossy(&inode_name); - println!("{}", s); - } -} +// let mut files = file.directory().unwrap(); +// println!("current dir: {}", *current_dir); +// while let Some(Ok(entry)) = files.next() { +// let inode_name = entry.name; +// let s = String::from_utf8_lossy(&inode_name); +// println!("{}", s); +// } +// } pub fn log_dump() { use crate::network::socket::SimpleSock; @@ -144,55 +144,55 @@ pub fn log_dump() { } } -pub fn echo_file(path: String) { - let mut current_dir = CURRENT_DIR.lock(); +// pub fn echo_file(path: String) { +// let mut current_dir = CURRENT_DIR.lock(); - let fs = &*FILE_SYSTEM.lock(); +// let fs = &*FILE_SYSTEM.lock(); - current_dir.push_str(&path); +// current_dir.push_str(&path); - let file = fs - .open(current_dir.as_bytes(), OpenOptions::new().read(true)) - .unwrap(); +// let file = fs +// .open(current_dir.as_bytes(), OpenOptions::new().read(true)) +// .unwrap(); - if file.is_dir() { - println!("{} is a directory", path); - } else { - let mut file_contents = Vec::new(); +// if file.is_dir() { +// println!("{} is a directory", path); +// } else { +// let mut file_contents = Vec::new(); - let _ret = file.read_to_end(&mut file_contents).unwrap(); +// let _ret = file.read_to_end(&mut file_contents).unwrap(); - let file_contents_str = String::from_utf8_lossy(&file_contents); +// let file_contents_str = String::from_utf8_lossy(&file_contents); - println!("{}", file_contents_str); - } -} +// println!("{}", file_contents_str); +// } +// } -pub fn change_directory(path: String) { - let mut current_dir = CURRENT_DIR.lock(); +// pub fn change_directory(path: String) { +// let mut current_dir = CURRENT_DIR.lock(); - let _fs = &*FILE_SYSTEM.lock(); - if path == "." || path == ".." { - let mut split_dir = current_dir.split('/').collect::>(); - let mut new_dir = String::new(); - split_dir.remove(split_dir.len() - 1); - println!("{:?}", split_dir); - if split_dir.is_empty() { - new_dir = "/".to_string(); - } else { - for x in split_dir { - new_dir.push_str(x); - new_dir.push('/'); - } - } - *current_dir = new_dir; - } else { - if !current_dir.ends_with('/') { - current_dir.push('/'); - } - current_dir.push_str(&path); - } -} +// let _fs = &*FILE_SYSTEM.lock(); +// if path == "." || path == ".." { +// let mut split_dir = current_dir.split('/').collect::>(); +// let mut new_dir = String::new(); +// split_dir.remove(split_dir.len() - 1); +// println!("{:?}", split_dir); +// if split_dir.is_empty() { +// new_dir = "/".to_string(); +// } else { +// for x in split_dir { +// new_dir.push_str(x); +// new_dir.push('/'); +// } +// } +// *current_dir = new_dir; +// } else { +// if !current_dir.ends_with('/') { +// current_dir.push('/'); +// } +// current_dir.push_str(&path); +// } +// } // fn engine_construction() -> Engine { // let mut engine = rhai::Engine::new(); diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index 2948cc9f..00056064 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -11,8 +11,8 @@ use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE}; use crate::time::fetch_time; use crate::KERNEL_STATE; use crate::{ - arch::shutdown, filesystem::FILE_SYSTEM, rhai_shell::KEYBUFF, vterm::VTerm, - wasm_jumploader::run_program, + arch::shutdown, rhai_shell::KEYBUFF, vterm::Term, + // wasm_jumploader::run_program, }; use acpi::{AcpiTables, PlatformInfo}; @@ -193,7 +193,7 @@ pub fn real_shell() { } pub fn command_parser(user: String, command: String) { - let fs = &*FILE_SYSTEM.lock(); + // let fs = &*FILE_SYSTEM.lock(); let mut iter = command.split_whitespace(); // TODO: update the open() function to take either a ableOS path or a b"/" type path @@ -225,41 +225,48 @@ pub fn command_parser(user: String, command: String) { // drop(fs); // shell(); // } - "list" | "ls" => { - for dir_entry in list_files_in_dir(fs, current_path) { - println!("{}", dir_entry.file_name_string()); - } - } + // "list" | "ls" => { + // for dir_entry in list_files_in_dir(fs, current_path) { + // println!("{}", dir_entry.file_name_string()); + // } + // } +<<<<<<< HEAD "echo" => match conf_args.1.arguments.get("p") { Some(path) => echo_file(path.to_string(), fs), None => println!("No path provided"), }, "test" => {} +======= + // "echo" => { + // echo_file(iter.next().unwrap().to_string(), fs); + // } +>>>>>>> 5149f26... vfs: move operations into trait StorageDevice, hold StorageDevices in KERNEL_STATE "quit" => shutdown(), _ => { - let mut options = OpenOptions::new(); - options.read(true); - let file = { - let path = format!("/home/{user}/bins/{bin_name}.wasm"); - if let Ok(file) = fs.open(&path.as_bytes(), &options) { - file - } else { - let path = format!("/shared/bins/{bin_name}.wasm"); - if let Ok(file) = fs.open(&path.as_bytes(), &options) { - file - } else { - let path = format!("/system/bins/{bin_name}.wasm"); - match fs.open(&path.as_bytes(), &options) { - Ok(file) => file, - Err(error) => { - trace!("{:?}", error); + // let mut options = OpenOptions::new(); + // options.read(true); + // let file = { + // let path = format!("/home/{user}/bins/{bin_name}.wasm"); + // if let Ok(file) = fs.open(&path.as_bytes(), &options) { + // file + // } else { + // let path = format!("/shared/bins/{bin_name}.wasm"); + // if let Ok(file) = fs.open(&path.as_bytes(), &options) { + // file + // } else { + // let path = format!("/system/bins/{bin_name}.wasm"); + // match fs.open(&path.as_bytes(), &options) { + // Ok(file) => file, + // Err(error) => { + // trace!("{:?}", error); println!("No such binary: {}", bin_name); error!("No such binary: {}", bin_name); return; +<<<<<<< HEAD } } } @@ -272,6 +279,20 @@ pub fn command_parser(user: String, command: String) { // let args = iter.collect::>(); // println!("{:?}", args); run_program(&binary); +======= + // } + // } + // } + // } + // }; + + // let mut binary = vec![]; + // file.read_to_end(&mut binary).unwrap(); + + // let args = iter.collect::>(); + // println!("{:?}", args); + // run_program(&binary); +>>>>>>> 5149f26... vfs: move operations into trait StorageDevice, hold StorageDevices in KERNEL_STATE } } } diff --git a/ableos/src/wasm_jumploader/mod.rs b/ableos/src/wasm_jumploader/mod.rs index ab5a674d..06177dca 100644 --- a/ableos/src/wasm_jumploader/mod.rs +++ b/ableos/src/wasm_jumploader/mod.rs @@ -1,101 +1,101 @@ pub mod host_functions; -use crate::{filesystem::FILE_SYSTEM, wasm_jumploader::host_functions::HostExternals}; -use genfs::{Fs, OpenOptions}; +use crate::wasm_jumploader::host_functions::HostExternals; +// use genfs::{Fs, OpenOptions}; use wasmi::{ImportsBuilder, ModuleInstance}; pub fn interp() { trace!("Interpreting..."); - let fs = &*FILE_SYSTEM.lock(); - trace!("Got filesystem"); - let file = fs - .open( - b"/home/able/bins/aos_test.wasm", - OpenOptions::new().read(true), - ) - .unwrap(); + // let fs = &*FILE_SYSTEM.lock(); + // trace!("Got filesystem"); + // let file = fs + // .open( + // b"/home/able/bins/aos_test.wasm", + // OpenOptions::new().read(true), + // ) + // .unwrap(); - let mut wasm_binary = Vec::new(); + // let mut wasm_binary = Vec::new(); - let ret = file.read_to_end(&mut wasm_binary).unwrap(); - trace!("Binary size {}", ret); - // Load wasm binary and prepare it for instantiation. - let module = wasmi::Module::from_buffer(&wasm_binary).expect("failed to load wasm"); - trace!("Loaded wasm binary"); - let imports = ImportsBuilder::new().with_resolver("env", &host_functions::HostExternals {}); - trace!("Created imports"); + // let ret = file.read_to_end(&mut wasm_binary).unwrap(); + // trace!("Binary size {}", ret); + // // Load wasm binary and prepare it for instantiation. + // let module = wasmi::Module::from_buffer(&wasm_binary).expect("failed to load wasm"); + // trace!("Loaded wasm binary"); + // let imports = ImportsBuilder::new().with_resolver("env", &host_functions::HostExternals {}); + // trace!("Created imports"); - // Instantiate a module with empty imports and - // assert that there is no `start` function. - let instance = ModuleInstance::new(&module, &imports); // .expect("failed to instantiate wasm module") + // // Instantiate a module with empty imports and + // // assert that there is no `start` function. + // let instance = ModuleInstance::new(&module, &imports); // .expect("failed to instantiate wasm module") - match instance { - Ok(inst) => { - let instance = inst.assert_no_start(); - let mut is_driver = false; - let _is_program = false; - let mut has_driver_entry = false; - let mut has_driver_exit = false; - let mut has_start = false; + // match instance { + // Ok(inst) => { + // let instance = inst.assert_no_start(); + // let mut is_driver = false; + // let _is_program = false; + // let mut has_driver_entry = false; + // let mut has_driver_exit = false; + // let mut has_start = false; - if let Some(_val) = instance.export_by_name("driver_entry") { - has_driver_entry = true; - } + // if let Some(_val) = instance.export_by_name("driver_entry") { + // has_driver_entry = true; + // } - if let Some(_val) = instance.export_by_name("driver_exit") { - has_driver_exit = true; - } + // if let Some(_val) = instance.export_by_name("driver_exit") { + // has_driver_exit = true; + // } - match instance.export_by_name("start") { - Some(_val) => { - trace!("Program start function found"); - has_start = true; - } - None => debug!("No start function found"), - } + // match instance.export_by_name("start") { + // Some(_val) => { + // trace!("Program start function found"); + // has_start = true; + // } + // None => debug!("No start function found"), + // } - match instance.export_by_name("main") { - Some(_val) => { - trace!("Program main function found"); - has_start = true; - } - None => debug!("No main function found"), - } + // match instance.export_by_name("main") { + // Some(_val) => { + // trace!("Program main function found"); + // has_start = true; + // } + // None => debug!("No main function found"), + // } - match (has_driver_entry, has_driver_exit) { - (true, true) => { - trace!("Valid driver entry and exit functions found"); - is_driver = true; - } - (true, false) => error!("Driver entry function found but no driver exit function"), - (false, true) => error!("Driver exit function found but no driver entry function"), - (false, false) => { - trace!("No driver entry or exit functions found"); - } - } + // match (has_driver_entry, has_driver_exit) { + // (true, true) => { + // trace!("Valid driver entry and exit functions found"); + // is_driver = true; + // } + // (true, false) => error!("Driver entry function found but no driver exit function"), + // (false, true) => error!("Driver exit function found but no driver entry function"), + // (false, false) => { + // trace!("No driver entry or exit functions found"); + // } + // } - if has_start && has_driver_entry { - error!( - "A program should not have both a start function and a driver entry function. It Will be treated as a program." - ); - } + // if has_start && has_driver_entry { + // error!( + // "A program should not have both a start function and a driver entry function. It Will be treated as a program." + // ); + // } - if has_start { - let ret = instance - .invoke_export("start", &[], &mut HostExternals {}) - .expect("failed to execute export"); + // if has_start { + // let ret = instance + // .invoke_export("start", &[], &mut HostExternals {}) + // .expect("failed to execute export"); - println!("collected wasm return value: {:?}", ret); - } else if is_driver { - let ret = instance - .invoke_export("driver_entry", &[], &mut HostExternals {}) - .expect("failed to execute export"); + // println!("collected wasm return value: {:?}", ret); + // } else if is_driver { + // let ret = instance + // .invoke_export("driver_entry", &[], &mut HostExternals {}) + // .expect("failed to execute export"); - println!("collected wasm return value: {:?}", ret); - } - } - Err(err) => error!("{}", err), - } + // println!("collected wasm return value: {:?}", ret); + // } + // } + // Err(err) => error!("{}", err), + // } } pub fn run_program(program: &[u8]) { diff --git a/ext2-rs/src/error.rs b/ext2-rs/src/error.rs index 10cb16fd..f506ae9d 100644 --- a/ext2-rs/src/error.rs +++ b/ext2-rs/src/error.rs @@ -62,8 +62,8 @@ pub enum Error { /// inode name name: String, }, - #[cfg(any(test, not(feature = "no_std")))] - Io { inner: io::Error }, + // #[cfg(any(test, not(feature = "no_std")))] + // Io { inner: io::Error }, } impl Display for Error { From 81fc36df8bde37ce19796af05ba4f05264f6dd6e Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 19:39:09 +0300 Subject: [PATCH 04/20] vfs: remove name from VFS nodes --- ableos/src/filesystem/ext2.rs | 6 ++++++ ableos/src/filesystem/mod.rs | 3 --- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index 831f4750..f1f071dc 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2022, Umut İnan Erdoğan + * + * SPDX-License-Identifier: MPL-2.0 + */ + use ext2::fs::Ext2; use ext2::sector::SectorSize; use ext2::volume::Volume; diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 36f3e571..d2c629e0 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -31,9 +31,6 @@ where /// A VFS node, that can either be a file or a directory. pub struct FsNode { - // FIXME: move the file name into the directory listing to implement hard - // links - name: String, flags: FsNodeFlags, length: u32, // in bytes inode: u32, // implementation specific identifier for the node From fd832dbb260feb8e1b5541d69cc9e8edda4f6744 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 19:45:04 +0300 Subject: [PATCH 05/20] vfs: FsNode::new() --- ableos/src/filesystem/mod.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index d2c629e0..1f661c64 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -41,8 +41,24 @@ pub struct FsNode { } impl FsNode { + pub fn new( + flags: FsNodeFlags, + length: u32, + inode: u32, + device_handle: Handle, + ptr: Weak, + ) -> Self { + Self { + flags, + length, + inode, + device_handle, + ptr, + } + } + // TODO: make this take flags - fn open(&self) -> Result { + pub fn open(&self) -> Result { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -51,7 +67,7 @@ impl FsNode { device.open(self) } - fn close(&self) -> Result<()> { + pub fn close(&self) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -60,7 +76,7 @@ impl FsNode { device.close(self) } - fn read(&self, offset: u32, size: u32) -> Result> { + pub fn read(&self, offset: u32, size: u32) -> Result> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -69,7 +85,7 @@ impl FsNode { device.read(self, offset, size) } - fn write(&self, offset: u32, buffer: Box<[u8]>) -> Result<()> { + pub fn write(&self, offset: u32, buffer: Box<[u8]>) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -78,7 +94,7 @@ impl FsNode { device.write(self, offset, buffer) } - fn read_dir(&self, index: u32) -> Result { + pub fn read_dir(&self, index: u32) -> Result { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -87,7 +103,7 @@ impl FsNode { device.read_dir(self, index) } - fn find_dir(&self, name: &str) -> Result { + pub fn find_dir(&self, name: &str) -> Result { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) From 92ac2e9b9a9b2c3aeac65aa11a487112721cd216 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 20:30:05 +0300 Subject: [PATCH 06/20] vfs: documentation and a few minor changes --- ableos/src/filesystem/errors.rs | 16 ++++++++++---- ableos/src/filesystem/ext2.rs | 12 +++++++--- ableos/src/filesystem/mod.rs | 30 ++++++++++++++++++++----- ableos/src/kernel_state.rs | 8 +++++-- ableos/src/scratchpad.rs | 39 ++++++++------------------------- 5 files changed, 60 insertions(+), 45 deletions(-) diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs index 827bd956..db6c8874 100644 --- a/ableos/src/filesystem/errors.rs +++ b/ableos/src/filesystem/errors.rs @@ -5,8 +5,9 @@ */ pub enum FsError { - UnsupportedOperation, + InodeNotFound, InvalidDevice, + UnsupportedOperation, } impl Into for ext2::error::Error { @@ -15,9 +16,16 @@ impl Into for ext2::error::Error { ext2::error::Error::Other(_) => todo!(), ext2::error::Error::BadMagic { magic: _ } => todo!(), ext2::error::Error::OutOfBounds { index: _ } => todo!(), - ext2::error::Error::AddressOutOfBounds { sector: _, offset: _, size: _ } => todo!(), - ext2::error::Error::BadBlockGroupCount { by_blocks: _, by_inodes: _ } => todo!(), - ext2::error::Error::InodeNotFound { inode: _ } => todo!(), + ext2::error::Error::AddressOutOfBounds { + sector: _, + offset: _, + size: _, + } => todo!(), + ext2::error::Error::BadBlockGroupCount { + by_blocks: _, + by_inodes: _, + } => todo!(), + ext2::error::Error::InodeNotFound { inode: _ } => FsError::InodeNotFound, ext2::error::Error::NotADirectory { inode: _, name: _ } => todo!(), ext2::error::Error::NotAbsolute { name: _ } => todo!(), ext2::error::Error::NotFound { name: _ } => todo!(), diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index f1f071dc..b1ad1c98 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -4,10 +4,11 @@ * SPDX-License-Identifier: MPL-2.0 */ -use ext2::fs::Ext2; +use ext2::fs::{sync::Synced, Ext2}; use ext2::sector::SectorSize; use ext2::volume::Volume; +use super::errors::FsError; use super::{FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice @@ -15,7 +16,7 @@ where S: SectorSize, V: Volume, { - fs: Ext2, + fs: Synced>, } impl Ext2StorageDevice @@ -25,7 +26,7 @@ where { pub fn new(volume: V) -> Result { Ok(Self { - fs: Ext2::new(volume).map_err(|e| e.into())?, + fs: Synced::new(volume).map_err(|e| e.into())?, }) } } @@ -36,6 +37,11 @@ where V: Volume + Send, { fn open(&self, node: &super::FsNode /* TODO: flags */) -> Result { + let inode = self + .fs + .inode_nth(node.inode as usize) + .ok_or_else(|| FsError::InodeNotFound)?; + todo!() } diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 1f661c64..f62122c7 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -30,14 +30,19 @@ where } /// 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. pub struct FsNode { flags: FsNodeFlags, - length: u32, // in bytes - inode: u32, // implementation specific identifier for the node + length: u32, // in bytes + fd_count: u32, // count of open file descriptors + inode: u32, // 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 + ptr: Weak, // used by mountpoints and symlinks + // todo: permissions mask + // todo: owning user/group } impl FsNode { @@ -51,12 +56,14 @@ impl FsNode { Self { flags, length, + fd_count: 0, inode, device_handle, ptr, } } + /// This method opens a new file descriptor for this VFS node. // TODO: make this take flags pub fn open(&self) -> Result { let state = KERNEL_STATE.lock(); @@ -67,6 +74,8 @@ impl FsNode { device.open(self) } + /// This method is for closing the VFS node, which is done when no open file + /// descriptors for this file are left. pub fn close(&self) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state @@ -76,6 +85,9 @@ impl FsNode { device.close(self) } + /// 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: u32, size: u32) -> Result> { let state = KERNEL_STATE.lock(); let device = state @@ -85,6 +97,9 @@ impl FsNode { device.read(self, offset, 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: u32, buffer: Box<[u8]>) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state @@ -115,7 +130,7 @@ impl FsNode { bitflags! { /// Flags associated with VFS nodes. - /// + /// /// 0x00000MST /// T is set to 0 for files, 1 for directories /// S is set when the node is a symbolic link @@ -128,6 +143,9 @@ bitflags! { } } +/// A file descriptor. +pub struct FileDescriptor {} + pub struct DirectoryEntry { name: String, inode: u32, diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index 25b27f48..29fcda5f 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -1,7 +1,10 @@ use hashbrown::HashMap; use spin::Lazy; -use crate::{handle::{Handle, HandleResource}, filesystem::StorageDevice}; +use crate::{ + filesystem::StorageDevice, + handle::{Handle, HandleResource}, +}; pub static KERNEL_STATE: Lazy> = Lazy::new(|| spin::Mutex::new(KernelInternalState::new())); @@ -26,7 +29,8 @@ impl KernelInternalState { } pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) { - self.storage_devices.insert(Handle::new(HandleResource::StorageDevice), Box::new(device)); + self.storage_devices + .insert(Handle::new(HandleResource::StorageDevice), Box::new(device)); } pub fn get_storage_device(&self, handle: Handle) -> Option<&dyn StorageDevice> { diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index 00056064..b7b7fe6e 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -11,7 +11,9 @@ use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE}; use crate::time::fetch_time; use crate::KERNEL_STATE; use crate::{ - arch::shutdown, rhai_shell::KEYBUFF, vterm::Term, + arch::shutdown, + rhai_shell::KEYBUFF, + vterm::Term, // wasm_jumploader::run_program, }; @@ -231,19 +233,12 @@ pub fn command_parser(user: String, command: String) { // } // } -<<<<<<< HEAD - "echo" => match conf_args.1.arguments.get("p") { - Some(path) => echo_file(path.to_string(), fs), + // "echo" => match conf_args.1.arguments.get("p") { + // Some(path) => echo_file(path.to_string(), fs), - None => println!("No path provided"), - }, - "test" => {} -======= - // "echo" => { - // echo_file(iter.next().unwrap().to_string(), fs); + // None => println!("No path provided"), // } ->>>>>>> 5149f26... vfs: move operations into trait StorageDevice, hold StorageDevices in KERNEL_STATE - + "test" => {} "quit" => shutdown(), _ => { @@ -263,23 +258,8 @@ pub fn command_parser(user: String, command: String) { // Ok(file) => file, // Err(error) => { // trace!("{:?}", error); - println!("No such binary: {}", bin_name); - error!("No such binary: {}", bin_name); - return; -<<<<<<< HEAD - } - } - } - } - }; - - let mut binary = vec![]; - file.read_to_end(&mut binary).unwrap(); - - // let args = iter.collect::>(); - // println!("{:?}", args); - run_program(&binary); -======= + println!("No such binary: {}", bin_name); + error!("No such binary: {}", bin_name); // } // } // } @@ -292,7 +272,6 @@ pub fn command_parser(user: String, command: String) { // let args = iter.collect::>(); // println!("{:?}", args); // run_program(&binary); ->>>>>>> 5149f26... vfs: move operations into trait StorageDevice, hold StorageDevices in KERNEL_STATE } } } From 72883e847b7d32ac0a5bacc22a5f2ef034826253 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 21:16:18 +0300 Subject: [PATCH 07/20] vfs+ext2: full implementation of FsNode::open() --- ableos/src/filesystem/ext2.rs | 14 +++---- ableos/src/filesystem/mod.rs | 70 +++++++++++++++++++++++------------ ableos/src/kernel_state.rs | 17 +++++++-- 3 files changed, 66 insertions(+), 35 deletions(-) diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index b1ad1c98..1430d2a0 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -9,7 +9,7 @@ use ext2::sector::SectorSize; use ext2::volume::Volume; use super::errors::FsError; -use super::{FsResult as Result, StorageDevice}; +use super::{FsResult as Result, StorageDevice, DirectoryEntry, FsNode, FileDescriptor}; pub struct Ext2StorageDevice where @@ -36,32 +36,32 @@ where S: SectorSize + Send, V: Volume + Send, { - fn open(&self, node: &super::FsNode /* TODO: flags */) -> Result { + fn open(&self, node: &super::FsNode /* TODO: flags */) -> Result { let inode = self .fs .inode_nth(node.inode as usize) .ok_or_else(|| FsError::InodeNotFound)?; - todo!() + Ok(FileDescriptor::new(node.flags, inode.size(), node.inode)) } fn close(&self, node: &super::FsNode) -> Result<()> { todo!() } - fn read(&self, node: &super::FsNode, offset: u32, size: u32) -> Result> { + fn read(&self, node: &super::FsNode, offset: usize, size: usize) -> Result> { todo!() } - fn write(&self, node: &super::FsNode, offset: u32, buffer: Box<[u8]>) -> Result<()> { + fn write(&self, node: &super::FsNode, offset: usize, buffer: Box<[u8]>) -> Result<()> { todo!() } - fn read_dir(&self, node: &super::FsNode, index: u32) -> Result { + fn read_dir(&self, node: &super::FsNode, index: usize) -> Result { todo!() } - fn find_dir(&self, node: &super::FsNode, name: &str) -> Result { + fn find_dir(&self, node: &super::FsNode, name: &str) -> Result { todo!() } } diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index f62122c7..2217d192 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -21,11 +21,11 @@ pub trait StorageDevice where Self: Send, { - fn open(&self, node: &FsNode /* TODO: flags */) -> Result; + fn open(&self, node: &FsNode /* TODO: flags */) -> Result; fn close(&self, node: &FsNode) -> Result<()>; - fn read(&self, node: &FsNode, offset: u32, size: u32) -> Result>; - fn write(&self, node: &FsNode, offset: u32, buffer: Box<[u8]>) -> Result<()>; - fn read_dir(&self, node: &FsNode, index: u32) -> Result; + fn read(&self, node: &FsNode, offset: usize, size: usize) -> Result>; + fn write(&self, node: &FsNode, offset: usize, buffer: Box<[u8]>) -> Result<()>; + fn read_dir(&self, node: &FsNode, index: usize) -> Result; fn find_dir(&self, node: &FsNode, name: &str) -> Result; } @@ -35,21 +35,21 @@ where /// opened. When there are no open file descriptors to a file, the associated /// VFS node is dropped. pub struct FsNode { - flags: FsNodeFlags, - length: u32, // in bytes - fd_count: u32, // count of open file descriptors - inode: u32, // implementation specific identifier for the node + flags: FsFlags, + length: usize, // in bytes + fd_count: usize, // count of open file descriptors + 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 + // TODO: permissions mask + // TODO: owning user/group } impl FsNode { pub fn new( - flags: FsNodeFlags, - length: u32, - inode: u32, + flags: FsFlags, + length: usize, + inode: usize, device_handle: Handle, ptr: Weak, ) -> Self { @@ -65,13 +65,16 @@ impl FsNode { /// This method opens a new file descriptor for this VFS node. // TODO: make this take flags - pub fn open(&self) -> Result { + pub fn open(&mut self) -> Result { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; + let fd = device.open(self)?; + let mut kernel_state = KERNEL_STATE.lock(); + let handle = kernel_state.add_file_descriptor(fd); - device.open(self) + Ok(handle) } /// This method is for closing the VFS node, which is done when no open file @@ -88,7 +91,7 @@ impl FsNode { /// 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: u32, size: u32) -> Result> { + pub fn read(&self, offset: usize, size: usize) -> Result> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -100,7 +103,7 @@ impl FsNode { /// 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: u32, buffer: Box<[u8]>) -> Result<()> { + pub fn write(&self, offset: usize, buffer: Box<[u8]>) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -109,7 +112,7 @@ impl FsNode { device.write(self, offset, buffer) } - pub fn read_dir(&self, index: u32) -> Result { + pub fn read_dir(&self, index: usize) -> Result { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -129,13 +132,13 @@ impl FsNode { } bitflags! { - /// Flags associated with VFS nodes. + /// Flags associated with VFS nodes and file descriptors /// /// 0x00000MST /// T is set to 0 for files, 1 for directories - /// S is set when the node is a symbolic link - /// M is set if the node is an active mount point - pub struct FsNodeFlags: u8 { + /// 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; @@ -144,9 +147,28 @@ bitflags! { } /// A file descriptor. -pub struct FileDescriptor {} +pub struct FileDescriptor { + flags: FsFlags, + length: usize, // in bytes + inode: usize, // implementation specific identifier for the node + // TODO: permissions mask + // TODO: owning user/group + // TODO: read file position? + // FIXME: I'm not sure if this needs to have a ptr to the VFS node, + // figure that out. +} + +impl FileDescriptor { + fn new(flags: FsFlags, length: usize, inode: usize) -> Self { + Self { + flags, + length, + inode, + } + } +} pub struct DirectoryEntry { name: String, - inode: u32, + inode: usize, } diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index 29fcda5f..b66db2e1 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -2,7 +2,7 @@ use hashbrown::HashMap; use spin::Lazy; use crate::{ - filesystem::StorageDevice, + filesystem::{StorageDevice, FileDescriptor}, handle::{Handle, HandleResource}, }; @@ -12,6 +12,7 @@ pub static KERNEL_STATE: Lazy> = pub struct KernelInternalState { pub hostname: String, storage_devices: HashMap>, + fd_table: HashMap, should_shutdown: bool, } @@ -20,6 +21,7 @@ impl KernelInternalState { Self { should_shutdown: false, storage_devices: HashMap::new(), + fd_table: HashMap::new(), hostname: "".to_string(), } } @@ -28,15 +30,22 @@ impl KernelInternalState { self.hostname = hostname; } - pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) { - self.storage_devices - .insert(Handle::new(HandleResource::StorageDevice), Box::new(device)); + pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) -> Handle { + let handle = Handle::new(HandleResource::StorageDevice); + self.storage_devices.insert(handle, Box::new(device)); + handle } pub fn get_storage_device(&self, handle: Handle) -> Option<&dyn StorageDevice> { self.storage_devices.get(&handle).map(|d| &**d) } + pub fn add_file_descriptor(&mut self, fd: FileDescriptor) -> Handle { + let handle = Handle::new(HandleResource::FileDescriptor); + self.fd_table.insert(handle, fd); + handle + } + pub fn shutdown(&mut self) { self.should_shutdown = true; } From dd9ea18f095b3b39c8aee2429ddbcee238996fbd Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Wed, 3 Aug 2022 21:17:28 +0300 Subject: [PATCH 08/20] vfs: rename FsNode::close() to FsNode::release() FsNode::close() will instead become a method that takes a closes a file descriptor, while release() will close the whole VFS node. --- ableos/src/filesystem/ext2.rs | 2 +- ableos/src/filesystem/mod.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index 1430d2a0..9277b87a 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -45,7 +45,7 @@ where Ok(FileDescriptor::new(node.flags, inode.size(), node.inode)) } - fn close(&self, node: &super::FsNode) -> Result<()> { + fn release(&self, node: &super::FsNode) -> Result<()> { todo!() } diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 2217d192..7bd17554 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -22,7 +22,7 @@ where Self: Send, { fn open(&self, node: &FsNode /* TODO: flags */) -> Result; - fn close(&self, node: &FsNode) -> Result<()>; + fn release(&self, node: &FsNode) -> Result<()>; fn read(&self, node: &FsNode, offset: usize, size: usize) -> Result>; fn write(&self, node: &FsNode, offset: usize, buffer: Box<[u8]>) -> Result<()>; fn read_dir(&self, node: &FsNode, index: usize) -> Result; @@ -79,13 +79,13 @@ impl FsNode { /// This method is for closing the VFS node, which is done when no open file /// descriptors for this file are left. - pub fn close(&self) -> Result<()> { + pub fn release(&self) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; - device.close(self) + device.release(self) } /// This method reads from the VFS node without opening a new file From 8a175132d8ad5ea60af97664e5badbd67bdc6eb6 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Thu, 4 Aug 2022 09:01:34 +0300 Subject: [PATCH 09/20] vfs: FsNode::release() implementation and weak ref to VFS node in file descriptors --- ableos/src/filesystem/ext2.rs | 24 +++++++------- ableos/src/filesystem/mod.rs | 62 ++++++++++++++++++++++++----------- ableos/src/kernel_state.rs | 8 +++-- 3 files changed, 61 insertions(+), 33 deletions(-) diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index 9277b87a..c983dec7 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -4,12 +4,13 @@ * SPDX-License-Identifier: MPL-2.0 */ +use alloc::sync::Arc; use ext2::fs::{sync::Synced, Ext2}; use ext2::sector::SectorSize; use ext2::volume::Volume; use super::errors::FsError; -use super::{FsResult as Result, StorageDevice, DirectoryEntry, FsNode, FileDescriptor}; +use super::{DirectoryEntry, FileDescriptor, FsNode, FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice where @@ -36,32 +37,33 @@ where S: SectorSize + Send, V: Volume + Send, { - fn open(&self, node: &super::FsNode /* TODO: flags */) -> Result { + fn open(&self, node: Arc /* TODO: flags */) -> Result { let inode = self .fs .inode_nth(node.inode as usize) .ok_or_else(|| FsError::InodeNotFound)?; - Ok(FileDescriptor::new(node.flags, inode.size(), node.inode)) + Ok(FileDescriptor::new(Arc::downgrade(&node), node.flags, inode.size(), node.inode)) } - fn release(&self, node: &super::FsNode) -> Result<()> { + fn release(&self, _node: FsNode) -> Result<()> { + // TODO: flush to disk and whatnot + Ok(()) + } + + fn read(&self, _node: &FsNode, _offset: usize, _size: usize) -> Result> { todo!() } - fn read(&self, node: &super::FsNode, offset: usize, size: usize) -> Result> { + fn write(&self, _node: &FsNode, _offset: usize, _buffer: Box<[u8]>) -> Result<()> { todo!() } - fn write(&self, node: &super::FsNode, offset: usize, buffer: Box<[u8]>) -> Result<()> { + fn read_dir(&self, _node: &FsNode, _index: usize) -> Result { todo!() } - fn read_dir(&self, node: &super::FsNode, index: usize) -> Result { - todo!() - } - - fn find_dir(&self, node: &super::FsNode, name: &str) -> Result { + fn find_dir(&self, _node: &FsNode, _name: &str) -> Result { todo!() } } diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 7bd17554..78f7d307 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -7,7 +7,7 @@ pub mod errors; pub mod ext2; -use alloc::rc::Weak; +use alloc::sync::{Weak, Arc}; use bitflags::bitflags; use crate::{handle::Handle, KERNEL_STATE}; @@ -21,8 +21,8 @@ pub trait StorageDevice where Self: Send, { - fn open(&self, node: &FsNode /* TODO: flags */) -> Result; - fn release(&self, node: &FsNode) -> Result<()>; + fn open(&self, node: Arc /* TODO: flags */) -> Result; + fn release(&self, node: FsNode) -> Result<()>; fn read(&self, node: &FsNode, offset: usize, size: usize) -> Result>; fn write(&self, node: &FsNode, offset: usize, buffer: Box<[u8]>) -> Result<()>; fn read_dir(&self, node: &FsNode, index: usize) -> Result; @@ -34,10 +34,10 @@ where /// 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 - fd_count: usize, // count of open file descriptors inode: usize, // implementation specific identifier for the node device_handle: Handle, // uniquely assigned device handle ptr: Weak, // used by mountpoints and symlinks @@ -56,7 +56,6 @@ impl FsNode { Self { flags, length, - fd_count: 0, inode, device_handle, ptr, @@ -65,27 +64,31 @@ impl FsNode { /// This method opens a new file descriptor for this VFS node. // TODO: make this take flags - pub fn open(&mut self) -> Result { - let state = KERNEL_STATE.lock(); + pub fn open(self: Arc) -> Result { + let mut state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; let fd = device.open(self)?; - let mut kernel_state = KERNEL_STATE.lock(); - let handle = kernel_state.add_file_descriptor(fd); + let handle = state.open_file_descriptor(fd); Ok(handle) } - /// This method is for closing the VFS node, which is done when no open file - /// descriptors for this file are left. - pub fn release(&self) -> Result<()> { + /// This method is for closing the VFS node, which is usually done when no + /// open file descriptors for this file are left. File descriptors have + /// weak references to the VFS node, meaning it is okay to close the VFS + /// node before all file descriptors are closed. + pub fn release(self) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; - device.release(self) + device.release(self)?; + // self is moved into device.release, and at the end of it, self should + // be dropped. + Ok(()) } /// This method reads from the VFS node without opening a new file @@ -131,6 +134,12 @@ impl FsNode { } } +impl Drop for FsNode { + fn drop(&mut self) { + trace!("dropping: {self:#?}") + } +} + bitflags! { /// Flags associated with VFS nodes and file descriptors /// @@ -147,25 +156,38 @@ bitflags! { } /// A file descriptor. +#[derive(Debug)] pub struct FileDescriptor { + vfs_node: Weak, // ptr to the associated VFS node flags: FsFlags, length: usize, // in bytes - inode: usize, // implementation specific identifier for the node - // TODO: permissions mask - // TODO: owning user/group - // TODO: read file position? - // FIXME: I'm not sure if this needs to have a ptr to the VFS node, - // figure that out. + inode: usize, // implementation specific identifier for the node + // TODO: permissions mask + // TODO: owning user/group + // TODO: read file position? } impl FileDescriptor { - fn new(flags: FsFlags, length: usize, inode: usize) -> Self { + fn new(vfs_node: Weak, flags: FsFlags, length: usize, inode: usize) -> Self { Self { + vfs_node, flags, length, inode, } } + + /// This method is for closing the file descriptor. + pub fn close(fd_handle: Handle) { + let mut state = KERNEL_STATE.lock(); + state.close_file_descriptor(fd_handle); + } +} + +impl Drop for FileDescriptor { + fn drop(&mut self) { + trace!("dropping: {self:#?}") + } } pub struct DirectoryEntry { diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index b66db2e1..2ca4bcdc 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -2,7 +2,7 @@ use hashbrown::HashMap; use spin::Lazy; use crate::{ - filesystem::{StorageDevice, FileDescriptor}, + filesystem::{FileDescriptor, StorageDevice}, handle::{Handle, HandleResource}, }; @@ -40,12 +40,16 @@ impl KernelInternalState { self.storage_devices.get(&handle).map(|d| &**d) } - pub fn add_file_descriptor(&mut self, fd: FileDescriptor) -> Handle { + pub fn open_file_descriptor(&mut self, fd: FileDescriptor) -> Handle { let handle = Handle::new(HandleResource::FileDescriptor); self.fd_table.insert(handle, fd); handle } + pub fn close_file_descriptor(&mut self, fd_handle: Handle) { + self.fd_table.remove(&fd_handle); + } + pub fn shutdown(&mut self) { self.should_shutdown = true; } From e53d855fa69d4e0450d3e344c74790af552bf7a7 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Thu, 4 Aug 2022 10:19:35 +0300 Subject: [PATCH 10/20] vfs+ext2: initial FsNode::read() implementation The VFS side of things for read() should be done, however due to my limited ext2 knowledge, I've not implemented partially reading a file, and in this state only full files can be read. --- ableos/src/filesystem/errors.rs | 2 ++ ableos/src/filesystem/ext2.rs | 19 ++++++++++++++++--- ableos/src/filesystem/mod.rs | 27 ++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs index db6c8874..4e54c84c 100644 --- a/ableos/src/filesystem/errors.rs +++ b/ableos/src/filesystem/errors.rs @@ -7,6 +7,8 @@ pub enum FsError { InodeNotFound, InvalidDevice, + IsDirectory, + EndOfFile, UnsupportedOperation, } diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index c983dec7..bf590c4a 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -51,11 +51,24 @@ where Ok(()) } - fn read(&self, _node: &FsNode, _offset: usize, _size: usize) -> Result> { - todo!() + fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec) -> 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: Box<[u8]>) -> Result<()> { + fn write(&self, _node: &FsNode, _offset: usize, _buffer: &[u8]) -> Result<()> { todo!() } diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 78f7d307..96be6596 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -7,6 +7,8 @@ pub mod errors; pub mod ext2; +use core::cmp; + use alloc::sync::{Weak, Arc}; use bitflags::bitflags; @@ -17,14 +19,15 @@ use FsResult as Result; pub type FsResult = core::result::Result; +/// The methods on this trait are to be used internally. pub trait StorageDevice where Self: Send, { fn open(&self, node: Arc /* TODO: flags */) -> Result; fn release(&self, node: FsNode) -> Result<()>; - fn read(&self, node: &FsNode, offset: usize, size: usize) -> Result>; - fn write(&self, node: &FsNode, offset: usize, buffer: Box<[u8]>) -> Result<()>; + 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; } @@ -94,19 +97,29 @@ impl FsNode { /// 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) -> Result> { + pub fn read(&self, offset: usize, size: usize, buffer: &mut Vec) -> Result { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; - device.read(self, offset, size) + 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: Box<[u8]>) -> Result<()> { + pub fn write(&self, offset: usize, buffer: &[u8]) -> Result<()> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) @@ -132,6 +145,10 @@ impl FsNode { device.find_dir(self, name) } + + pub fn is_dir(&self) -> bool { + (self.flags & FsFlags::DIRECTORY) == FsFlags::DIRECTORY + } } impl Drop for FsNode { From d8802f0968eca9e2a38b155c0bbe2485e6e9bd43 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Thu, 4 Aug 2022 13:19:05 +0300 Subject: [PATCH 11/20] VFS: simplify everything --- ableos/src/filesystem/ext2.rs | 17 +--------- ableos/src/filesystem/mod.rs | 63 +++-------------------------------- ableos/src/kernel_state.rs | 34 +++++++++++++++---- 3 files changed, 32 insertions(+), 82 deletions(-) diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index bf590c4a..adcc0ebc 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -4,13 +4,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -use alloc::sync::Arc; use ext2::fs::{sync::Synced, Ext2}; use ext2::sector::SectorSize; use ext2::volume::Volume; use super::errors::FsError; -use super::{DirectoryEntry, FileDescriptor, FsNode, FsResult as Result, StorageDevice}; +use super::{DirectoryEntry, FsNode, FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice where @@ -37,20 +36,6 @@ where S: SectorSize + Send, V: Volume + Send, { - fn open(&self, node: Arc /* TODO: flags */) -> Result { - let inode = self - .fs - .inode_nth(node.inode as usize) - .ok_or_else(|| FsError::InodeNotFound)?; - - Ok(FileDescriptor::new(Arc::downgrade(&node), node.flags, inode.size(), node.inode)) - } - - fn release(&self, _node: FsNode) -> Result<()> { - // TODO: flush to disk and whatnot - Ok(()) - } - fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec) -> Result<()> { let inode = self .fs diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 96be6596..8793191c 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -24,12 +24,11 @@ pub trait StorageDevice where Self: Send, { - fn open(&self, node: Arc /* TODO: flags */) -> Result; - fn release(&self, node: FsNode) -> Result<()>; 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; + // TODO: flush to disk } /// A VFS node, that can either be a file or a directory. @@ -69,31 +68,11 @@ impl FsNode { // TODO: make this take flags pub fn open(self: Arc) -> Result { let mut state = KERNEL_STATE.lock(); - let device = state - .get_storage_device(self.device_handle) - .ok_or_else(|| FsError::InvalidDevice)?; - let fd = device.open(self)?; - let handle = state.open_file_descriptor(fd); + let handle = state.open_file_descriptor(self); Ok(handle) } - /// This method is for closing the VFS node, which is usually done when no - /// open file descriptors for this file are left. File descriptors have - /// weak references to the VFS node, meaning it is okay to close the VFS - /// node before all file descriptors are closed. - pub fn release(self) -> Result<()> { - let state = KERNEL_STATE.lock(); - let device = state - .get_storage_device(self.device_handle) - .ok_or_else(|| FsError::InvalidDevice)?; - - device.release(self)?; - // self is moved into device.release, and at the end of it, self should - // be dropped. - Ok(()) - } - /// 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. @@ -153,7 +132,8 @@ impl FsNode { impl Drop for FsNode { fn drop(&mut self) { - trace!("dropping: {self:#?}") + trace!("dropping VFS node: {self:#?}"); + // TODO: flush to disk here } } @@ -172,41 +152,6 @@ bitflags! { } } -/// A file descriptor. -#[derive(Debug)] -pub struct FileDescriptor { - vfs_node: Weak, // ptr to the associated VFS node - flags: FsFlags, - length: usize, // in bytes - inode: usize, // implementation specific identifier for the node - // TODO: permissions mask - // TODO: owning user/group - // TODO: read file position? -} - -impl FileDescriptor { - fn new(vfs_node: Weak, flags: FsFlags, length: usize, inode: usize) -> Self { - Self { - vfs_node, - flags, - length, - inode, - } - } - - /// This method is for closing the file descriptor. - pub fn close(fd_handle: Handle) { - let mut state = KERNEL_STATE.lock(); - state.close_file_descriptor(fd_handle); - } -} - -impl Drop for FileDescriptor { - fn drop(&mut self) { - trace!("dropping: {self:#?}") - } -} - pub struct DirectoryEntry { name: String, inode: usize, diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index 2ca4bcdc..4cb69363 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -1,8 +1,9 @@ +use alloc::sync::Arc; use hashbrown::HashMap; use spin::Lazy; use crate::{ - filesystem::{FileDescriptor, StorageDevice}, + filesystem::{StorageDevice, FsNode}, handle::{Handle, HandleResource}, }; @@ -12,7 +13,8 @@ pub static KERNEL_STATE: Lazy> = pub struct KernelInternalState { pub hostname: String, storage_devices: HashMap>, - fd_table: HashMap, + // FIXME: should this be per-process? + file_table: HashMap, should_shutdown: bool, } @@ -21,7 +23,7 @@ impl KernelInternalState { Self { should_shutdown: false, storage_devices: HashMap::new(), - fd_table: HashMap::new(), + file_table: HashMap::new(), hostname: "".to_string(), } } @@ -40,14 +42,19 @@ impl KernelInternalState { self.storage_devices.get(&handle).map(|d| &**d) } - pub fn open_file_descriptor(&mut self, fd: FileDescriptor) -> Handle { + // TODO: implement flags here + pub fn open_file_descriptor(&mut self, fs_node: Arc) -> Handle { let handle = Handle::new(HandleResource::FileDescriptor); - self.fd_table.insert(handle, fd); + self.file_table.insert(handle, FileTableEntry::new(fs_node)); handle } - pub fn close_file_descriptor(&mut self, fd_handle: Handle) { - self.fd_table.remove(&fd_handle); + pub fn get_file_descriptor(&self, handle: Handle) -> Option<&FileTableEntry> { + self.file_table.get(&handle) + } + + pub fn close_file_descriptor(&mut self, handle: Handle) { + self.file_table.remove(&handle); } pub fn shutdown(&mut self) { @@ -66,3 +73,16 @@ impl Default for KernelInternalState { Self::new() } } + +pub struct FileTableEntry { + fs_node: Arc, + // TODO: permissions, flags, owner, maybe cache stuff here? +} + +impl FileTableEntry { + fn new(fs_node: Arc) -> Self { + Self { + fs_node + } + } +} From 379b4deb91eaf4eacb04bafcba01c8858fed8ccb Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Thu, 4 Aug 2022 13:31:29 +0300 Subject: [PATCH 12/20] Ext2: read_dir implementation --- ableos/src/filesystem/errors.rs | 5 +++-- ableos/src/filesystem/ext2.rs | 13 +++++++++++-- ableos/src/filesystem/mod.rs | 9 +++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs index 4e54c84c..23864691 100644 --- a/ableos/src/filesystem/errors.rs +++ b/ableos/src/filesystem/errors.rs @@ -5,10 +5,11 @@ */ pub enum FsError { + EndOfFile, InodeNotFound, InvalidDevice, IsDirectory, - EndOfFile, + NotADirectory, UnsupportedOperation, } @@ -28,7 +29,7 @@ impl Into for ext2::error::Error { by_inodes: _, } => todo!(), ext2::error::Error::InodeNotFound { inode: _ } => FsError::InodeNotFound, - ext2::error::Error::NotADirectory { inode: _, name: _ } => todo!(), + ext2::error::Error::NotADirectory { inode: _, name: _ } => FsError::NotADirectory, ext2::error::Error::NotAbsolute { name: _ } => todo!(), ext2::error::Error::NotFound { name: _ } => todo!(), } diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index adcc0ebc..0d351da2 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -57,8 +57,17 @@ where todo!() } - fn read_dir(&self, _node: &FsNode, _index: usize) -> Result { - todo!() + fn read_dir(&self, node: &FsNode, index: usize) -> Result { + 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())?; + + Ok(DirectoryEntry::new(entry.file_name_string(), entry.inode)) } fn find_dir(&self, _node: &FsNode, _name: &str) -> Result { diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 8793191c..dabf90d5 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -156,3 +156,12 @@ pub struct DirectoryEntry { name: String, inode: usize, } + +impl DirectoryEntry { + fn new(name: String, inode: usize) -> Self { + Self { + name, + inode, + } + } +} From e6aa9a36514e67740683d00f3eb5918d6f5ac1be Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Thu, 4 Aug 2022 13:56:37 +0300 Subject: [PATCH 13/20] Ext2: find_dir implementation --- ableos/src/filesystem/errors.rs | 3 +- ableos/src/filesystem/ext2.rs | 62 ++++++++++++++++++++++++++++++--- ableos/src/filesystem/mod.rs | 13 +++---- ableos/src/kernel_state.rs | 6 ++-- ext2-rs/src/fs/sync.rs | 7 ++++ 5 files changed, 73 insertions(+), 18 deletions(-) diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs index 23864691..49a08b05 100644 --- a/ableos/src/filesystem/errors.rs +++ b/ableos/src/filesystem/errors.rs @@ -10,6 +10,7 @@ pub enum FsError { InvalidDevice, IsDirectory, NotADirectory, + NotFound, UnsupportedOperation, } @@ -31,7 +32,7 @@ impl Into for ext2::error::Error { ext2::error::Error::InodeNotFound { inode: _ } => FsError::InodeNotFound, ext2::error::Error::NotADirectory { inode: _, name: _ } => FsError::NotADirectory, ext2::error::Error::NotAbsolute { name: _ } => todo!(), - ext2::error::Error::NotFound { name: _ } => todo!(), + ext2::error::Error::NotFound { name: _ } => FsError::NotFound, } } } diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index 0d351da2..d0163a7e 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -4,12 +4,16 @@ * SPDX-License-Identifier: MPL-2.0 */ +use alloc::sync::Weak; use ext2::fs::{sync::Synced, Ext2}; use ext2::sector::SectorSize; +use ext2::sys::inode::TypePerm; use ext2::volume::Volume; +use crate::handle::Handle; + use super::errors::FsError; -use super::{DirectoryEntry, FsNode, FsResult as Result, StorageDevice}; +use super::{DirectoryEntry, FsFlags, FsNode, FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice where @@ -17,6 +21,7 @@ where V: Volume, { fs: Synced>, + device_handle: Handle, } impl Ext2StorageDevice @@ -24,9 +29,10 @@ where S: SectorSize, V: Volume, { - pub fn new(volume: V) -> Result { + pub fn new(volume: V, device_handle: Handle) -> Result { Ok(Self { fs: Synced::new(volume).map_err(|e| e.into())?, + device_handle, }) } } @@ -63,18 +69,64 @@ where .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) + let entry = dir + .nth(index) .ok_or_else(|| FsError::InodeNotFound)? .map_err(|e| e.into())?; Ok(DirectoryEntry::new(entry.file_name_string(), entry.inode)) } - fn find_dir(&self, _node: &FsNode, _name: &str) -> Result { - todo!() + fn find_dir(&self, 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); + for entry in dir { + if entry.is_err() { + continue; + } + + let entry = entry.unwrap(); + if entry.file_name_string() == name { + let inode = self + .fs + .inode_nth(entry.inode as usize) + .ok_or_else(|| FsError::InodeNotFound)?; + let flags = ext2_type_to_fs_flags(inode.type_perm()); + found_node = Ok(FsNode::new( + flags, + inode.size(), + entry.inode, + self.device_handle, + Weak::new(), + )) + } + } + + found_node } } +fn ext2_type_to_fs_flags(type_perm: TypePerm) -> FsFlags { + 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 +} + // fn load_fs() -> Arc>>> { // let mut volume = Vec::new(); // volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img")); diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index dabf90d5..6a37f5fa 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -9,7 +9,7 @@ pub mod ext2; use core::cmp; -use alloc::sync::{Weak, Arc}; +use alloc::sync::{Arc, Weak}; use bitflags::bitflags; use crate::{handle::Handle, KERNEL_STATE}; @@ -140,9 +140,9 @@ impl Drop for FsNode { 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 + /// 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; @@ -159,9 +159,6 @@ pub struct DirectoryEntry { impl DirectoryEntry { fn new(name: String, inode: usize) -> Self { - Self { - name, - inode, - } + Self { name, inode } } } diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index 4cb69363..9d1236fe 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -3,7 +3,7 @@ use hashbrown::HashMap; use spin::Lazy; use crate::{ - filesystem::{StorageDevice, FsNode}, + filesystem::{FsNode, StorageDevice}, handle::{Handle, HandleResource}, }; @@ -81,8 +81,6 @@ pub struct FileTableEntry { impl FileTableEntry { fn new(fs_node: Arc) -> Self { - Self { - fs_node - } + Self { fs_node } } } diff --git a/ext2-rs/src/fs/sync.rs b/ext2-rs/src/fs/sync.rs index d028b03e..a5fc5584 100644 --- a/ext2-rs/src/fs/sync.rs +++ b/ext2-rs/src/fs/sync.rs @@ -1,5 +1,7 @@ //! +use crate::sys::inode::TypePerm; + use { super::Ext2, alloc::{ @@ -543,6 +545,11 @@ impl> Inode { pub fn size(&self) -> usize { self.size32() as usize } + + /// ableOS: expose type_perm + pub fn type_perm(&self) -> TypePerm { + self.inner.type_perm + } } impl> File for Inode { From cc464c4ec3ad704a42a5b313412aaec68b86db70 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Thu, 4 Aug 2022 17:03:44 +0300 Subject: [PATCH 14/20] VFS+Ext2: many simplifications and filesystem initialisation --- ableos/src/filesystem/errors.rs | 1 + ableos/src/filesystem/ext2.rs | 93 ++++++++++++------- ableos/src/filesystem/mod.rs | 153 +++++--------------------------- ableos/src/handle.rs | 2 + ableos/src/kernel_state.rs | 23 +++-- ableos/src/kmain.rs | 11 ++- ableos/src/lib.rs | 4 + ext2-rs/src/fs/sync.rs | 2 - 8 files changed, 116 insertions(+), 173 deletions(-) diff --git a/ableos/src/filesystem/errors.rs b/ableos/src/filesystem/errors.rs index 49a08b05..d0ce762e 100644 --- a/ableos/src/filesystem/errors.rs +++ b/ableos/src/filesystem/errors.rs @@ -4,6 +4,7 @@ * SPDX-License-Identifier: MPL-2.0 */ +#[derive(Copy, Clone, Debug)] pub enum FsError { EndOfFile, InodeNotFound, diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index d0163a7e..7e8e44b1 100644 --- a/ableos/src/filesystem/ext2.rs +++ b/ableos/src/filesystem/ext2.rs @@ -4,16 +4,17 @@ * SPDX-License-Identifier: MPL-2.0 */ -use alloc::sync::Weak; +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; +use crate::handle::{Handle, HandleResource}; use super::errors::FsError; -use super::{DirectoryEntry, FsFlags, FsNode, FsResult as Result, StorageDevice}; +use super::vfs::{add_fs_node, find_fs_node, DirectoryEntry, FsFlags}; +use super::{FsNode, FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice where @@ -22,6 +23,7 @@ where { fs: Synced>, device_handle: Handle, + root: Arc, } impl Ext2StorageDevice @@ -29,10 +31,24 @@ where S: SectorSize, V: Volume, { - pub fn new(volume: V, device_handle: Handle) -> Result { - Ok(Self { - fs: Synced::new(volume).map_err(|e| e.into())?, + pub fn new(volume: V) -> Result { + 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(), + )); + + add_fs_node(root.clone()); + + Ok(Self { + fs, + device_handle, + root, }) } } @@ -45,7 +61,7 @@ where fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec) -> Result<()> { let inode = self .fs - .inode_nth(node.inode as usize) + .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 @@ -66,21 +82,38 @@ where fn read_dir(&self, node: &FsNode, index: usize) -> Result { let inode = self .fs - .inode_nth(node.inode as usize) + .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 entry_node_handle = + find_fs_node(entry.inode, self.device_handle).unwrap_or_else(|| { + add_fs_node(Arc::new(FsNode::new( + ext2_type_to_fs_flags(entry_inode.type_perm()), + inode.size(), + entry.inode, + self.device_handle, + Weak::new(), + ))) + }); - Ok(DirectoryEntry::new(entry.file_name_string(), entry.inode)) + Ok(DirectoryEntry::new( + entry.file_name_string(), + entry_node_handle, + )) } - fn find_dir(&self, node: &FsNode, name: &str) -> Result { + fn find_dir(&self, node: &FsNode, name: &str) -> Result { let inode = self .fs - .inode_nth(node.inode as usize) + .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); @@ -91,23 +124,30 @@ where let entry = entry.unwrap(); if entry.file_name_string() == name { - let inode = self - .fs - .inode_nth(entry.inode as usize) - .ok_or_else(|| FsError::InodeNotFound)?; - let flags = ext2_type_to_fs_flags(inode.type_perm()); - found_node = Ok(FsNode::new( - flags, - inode.size(), - entry.inode, - self.device_handle, - Weak::new(), - )) + found_node = Ok( + find_fs_node(entry.inode, self.device_handle).unwrap_or_else(|| { + add_fs_node(Arc::new(FsNode::new( + ext2_type_to_fs_flags(inode.type_perm()), + inode.size(), + entry.inode, + self.device_handle, + Weak::new(), + ))) + }), + ); } } found_node } + + fn root_node(&self) -> Arc { + self.root.clone() + } + + fn device_handle(&self) -> Handle { + self.device_handle + } } fn ext2_type_to_fs_flags(type_perm: TypePerm) -> FsFlags { @@ -127,13 +167,6 @@ fn ext2_type_to_fs_flags(type_perm: TypePerm) -> FsFlags { t | s } -// fn load_fs() -> Arc>>> { -// let mut volume = Vec::new(); -// volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img")); - -// Arc::>::new(volume).unwrap() -// } - // pub fn walk>( // fs: &Synced>, // inode: Inode, diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 6a37f5fa..284f226c 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -6,15 +6,18 @@ pub mod errors; pub mod ext2; +pub mod vfs; -use core::cmp; - -use alloc::sync::{Arc, Weak}; -use bitflags::bitflags; +use ::ext2::sector::Size1024; +use alloc::sync::Arc; use crate::{handle::Handle, KERNEL_STATE}; -use self::errors::FsError; +use self::{ + errors::FsError, + ext2::Ext2StorageDevice, + vfs::{DirectoryEntry, FsNode}, +}; use FsResult as Result; pub type FsResult = core::result::Result; @@ -27,138 +30,22 @@ 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, node: &FsNode, name: &str) -> Result; // TODO: flush to disk + + fn root_node(&self) -> Arc; + fn device_handle(&self) -> Handle; } -/// 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 +pub fn init() -> Result<()> { + let mut state = KERNEL_STATE.lock(); + state.add_storage_device(load_fs()?); + Ok(()) } -impl FsNode { - pub fn new( - flags: FsFlags, - length: usize, - inode: usize, - device_handle: Handle, - ptr: Weak, - ) -> Self { - Self { - flags, - length, - inode, - device_handle, - ptr, - } - } +fn load_fs() -> Result>> { + let mut volume = Vec::new(); + volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img")); - /// 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 - .get_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 - .get_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 - .get_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 - .get_storage_device(self.device_handle) - .ok_or_else(|| FsError::InvalidDevice)?; - - device.find_dir(self, name) - } - - pub fn is_dir(&self) -> bool { - (self.flags & FsFlags::DIRECTORY) == FsFlags::DIRECTORY - } -} - -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 DirectoryEntry { - name: String, - inode: usize, -} - -impl DirectoryEntry { - fn new(name: String, inode: usize) -> Self { - Self { name, inode } - } + Ext2StorageDevice::new(volume) } diff --git a/ableos/src/handle.rs b/ableos/src/handle.rs index fb9f31db..4c30c730 100644 --- a/ableos/src/handle.rs +++ b/ableos/src/handle.rs @@ -23,6 +23,7 @@ pub enum HandleResource { BinaryData, StorageDevice, FileDescriptor, + FsNode, } #[derive(Debug, Eq, Hash, PartialEq, Clone, Copy)] @@ -40,6 +41,7 @@ impl Display for Handle { HandleResource::Socket => write!(f, "-Socket")?, HandleResource::StorageDevice => write!(f, "-StorageDevice")?, HandleResource::FileDescriptor => write!(f, "-FileDescriptor")?, + HandleResource::FsNode => write!(f, "-FsNode")?, } Ok(()) diff --git a/ableos/src/kernel_state.rs b/ableos/src/kernel_state.rs index 9d1236fe..3a7736da 100644 --- a/ableos/src/kernel_state.rs +++ b/ableos/src/kernel_state.rs @@ -1,9 +1,15 @@ +/* + * Copyright (c) 2022, Umut İnan Erdoğan + * + * SPDX-License-Identifier: MPL-2.0 + */ + use alloc::sync::Arc; use hashbrown::HashMap; use spin::Lazy; use crate::{ - filesystem::{FsNode, StorageDevice}, + filesystem::{vfs::FsNode, StorageDevice}, handle::{Handle, HandleResource}, }; @@ -32,13 +38,12 @@ impl KernelInternalState { self.hostname = hostname; } - pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) -> Handle { - let handle = Handle::new(HandleResource::StorageDevice); - self.storage_devices.insert(handle, Box::new(device)); - handle + pub fn add_storage_device(&mut self, device: impl StorageDevice + Send + 'static) { + let device = Box::new(device); + self.storage_devices.insert(device.device_handle(), device); } - pub fn get_storage_device(&self, handle: Handle) -> Option<&dyn StorageDevice> { + pub fn storage_device(&self, handle: Handle) -> Option<&dyn StorageDevice> { self.storage_devices.get(&handle).map(|d| &**d) } @@ -49,7 +54,7 @@ impl KernelInternalState { handle } - pub fn get_file_descriptor(&self, handle: Handle) -> Option<&FileTableEntry> { + pub fn file_descriptor(&self, handle: Handle) -> Option<&FileTableEntry> { self.file_table.get(&handle) } @@ -83,4 +88,8 @@ impl FileTableEntry { fn new(fs_node: Arc) -> Self { Self { fs_node } } + + pub fn fs_node(&self) -> Arc { + self.fs_node.clone() + } } diff --git a/ableos/src/kmain.rs b/ableos/src/kmain.rs index 9a4cc1d2..0115208a 100644 --- a/ableos/src/kmain.rs +++ b/ableos/src/kmain.rs @@ -1,13 +1,19 @@ +/* + * Copyright (c) 2022, Umut İnan Erdoğan + * + * SPDX-License-Identifier: MPL-2.0 + */ + #![allow(clippy::empty_loop)] use core::sync::atomic::AtomicU64; use crate::arch::{drivers::sysinfo::master, init, sloop}; -use crate::hardware; use crate::relib::network::socket::{SimpleSock, Socket}; use crate::{ boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM, }; +use crate::{filesystem, hardware}; use kernel::KERNEL_VERSION; use spin::Lazy; @@ -36,6 +42,9 @@ pub fn kernel_main() -> ! { x86_64::instructions::interrupts::without_interrupts(|| { hardware::init_mouse(); }); + + filesystem::init().unwrap(); + /* // println!("abc"); diff --git a/ableos/src/lib.rs b/ableos/src/lib.rs index 2ab6915a..201ece87 100644 --- a/ableos/src/lib.rs +++ b/ableos/src/lib.rs @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Able + * Copyright (c) 2022, Umut İnan Erdoğan * * SPDX-License-Identifier: MPL-2.0 */ @@ -25,6 +26,9 @@ #[macro_use] pub extern crate log; +#[macro_use] +pub extern crate bitflags; + pub extern crate alloc; pub extern crate externc_libm as libm; diff --git a/ext2-rs/src/fs/sync.rs b/ext2-rs/src/fs/sync.rs index a5fc5584..25e01e15 100644 --- a/ext2-rs/src/fs/sync.rs +++ b/ext2-rs/src/fs/sync.rs @@ -393,8 +393,6 @@ impl> Inode { } /// Determine if an inode is a directory pub fn is_dir(&self) -> bool { - use sys::inode::TypePerm; - { self.inner.type_perm }.contains(TypePerm::DIRECTORY) // self.inner.type_perm.contains(TypePerm::DIRECTORY) } From 1e45f08c8f8a58e8f86d89a3341646aff9753f2d Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Fri, 5 Aug 2022 09:16:34 +0300 Subject: [PATCH 15/20] 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() { From 315df055329c142157578a0553da5058ed0b61e5 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Sat, 6 Aug 2022 21:38:30 +0300 Subject: [PATCH 16/20] VFS: move stuff into a VirtualFileSystem struct --- ableos/src/filesystem/ext2.rs | 24 ++++++++------ ableos/src/filesystem/mod.rs | 10 ++++-- ableos/src/filesystem/vfs.rs | 60 ++++++++++++++++++++++++----------- ableos/src/scratchpad.rs | 9 +++--- 4 files changed, 67 insertions(+), 36 deletions(-) diff --git a/ableos/src/filesystem/ext2.rs b/ableos/src/filesystem/ext2.rs index 5fd9928b..1140ecdb 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::{add_fs_node, find_fs_node, DirectoryEntry, FsFlags}; +use super::vfs::{DirectoryEntry, FsFlags, VFS}; use super::{FsNode, FsResult as Result, StorageDevice}; pub struct Ext2StorageDevice @@ -43,7 +43,8 @@ where Weak::new(), )); - add_fs_node(root.clone()); + let mut vfs = VFS.lock(); + vfs.add_fs_node(root.clone()); Ok(Self { fs, @@ -93,9 +94,11 @@ where .fs .inode_nth(entry.inode) .ok_or_else(|| FsError::InodeNotFound)?; - let entry_node_handle = - find_fs_node(entry.inode, self.device_handle).unwrap_or_else(|| { - add_fs_node(Arc::new(FsNode::new( + 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()), inode.size(), entry.inode, @@ -117,6 +120,7 @@ where .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; @@ -124,17 +128,17 @@ where let entry = entry.unwrap(); if entry.file_name_string() == name { - found_node = Ok( - find_fs_node(entry.inode, self.device_handle).unwrap_or_else(|| { - add_fs_node(Arc::new(FsNode::new( + 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(inode.type_perm()), inode.size(), entry.inode, self.device_handle, Weak::new(), ))) - }), - ); + })); } } diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index bc382016..8f25dd0a 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::{filesystem::vfs::fs_node, handle::Handle, KERNEL_STATE}; +use crate::{filesystem::vfs::VFS, handle::Handle, KERNEL_STATE}; use self::{ errors::FsError, @@ -39,7 +39,10 @@ where pub fn init() -> Result<()> { let mut state = KERNEL_STATE.lock(); - state.add_storage_device(load_fs()?); + let mut vfs = VFS.lock(); + let fs = load_fs()?; + vfs.init(fs.root_node()); + state.add_storage_device(fs); Ok(()) } @@ -53,8 +56,9 @@ fn load_fs() -> Result>> { pub fn walk>(dir: Arc, path: S) { let path = path.into(); if let Some(dir) = dir.directory() { + let mut vfs = VFS.lock(); for entry in dir { - let fs_node = fs_node(entry.node()).unwrap(); + let fs_node = vfs.fs_node(entry.node()).unwrap(); println!("{}/{} => {}", path, entry.name(), fs_node.inode()); trace!("{entry:#?}"); if entry.name() != "." && entry.name() != ".." { diff --git a/ableos/src/filesystem/vfs.rs b/ableos/src/filesystem/vfs.rs index 00624d5a..5d6b4aec 100644 --- a/ableos/src/filesystem/vfs.rs +++ b/ableos/src/filesystem/vfs.rs @@ -18,30 +18,52 @@ use crate::{ }; lazy_static! { - static ref FS_NODES: Mutex>> = Default::default(); + pub static ref VFS: 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 struct VirtualFileSystem { + fs_nodes: HashMap>, + root_node: Weak, } -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 +impl VirtualFileSystem { + pub fn init(&mut self, root_node: Arc) { + self.root_node = Arc::downgrade(&root_node) + } + + pub fn add_fs_node(&mut self, fs_node: Arc) -> Handle { + let handle = Handle::new(HandleResource::FsNode); + self.fs_nodes.insert(handle, fs_node); + handle + } + + pub fn find_fs_node(&mut self, inode: usize, device_handle: Handle) -> Option { + self.fs_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(&mut self, handle: Handle) -> Option> { + self.fs_nodes.get(&handle).cloned() + } + + pub fn root_node(&self) -> Arc { + // okay to unwrap since this should never be called before init + self.root_node.upgrade().unwrap() + } +} + +impl Default for VirtualFileSystem { + fn default() -> Self { + Self { + fs_nodes: HashMap::new(), + root_node: Weak::new(), } - }) -} - -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. diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index f545120b..8cbf600e 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -7,13 +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::systeminfo::{KERNEL_VERSION, RELEASE_TYPE}; use crate::time::fetch_time; use crate::KERNEL_STATE; use crate::{ arch::shutdown, rhai_shell::KEYBUFF, - vterm::Term, + vterm::VTerm, // wasm_jumploader::run_program, }; use crate::{filesystem, KERNEL_STATE}; @@ -123,13 +124,13 @@ pub fn scratchpad() { ); let root = { - let state = KERNEL_STATE.lock(); - state.storage_devices.iter().next().unwrap().1.root_node() + let vfs = VFS.lock(); + vfs.root_node() }; filesystem::walk(root, ""); - // real_shell(); + real_shell(); } pub fn acpi() { From fa45737901280e06186eded2f087fbabe34bb2e9 Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Sat, 6 Aug 2022 22:27:25 +0300 Subject: [PATCH 17/20] NOT ONE, but TWO deadlocks fixed! --- ableos/src/filesystem/mod.rs | 9 ++++++--- ableos/src/scratchpad.rs | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ableos/src/filesystem/mod.rs b/ableos/src/filesystem/mod.rs index 8f25dd0a..76a529fc 100644 --- a/ableos/src/filesystem/mod.rs +++ b/ableos/src/filesystem/mod.rs @@ -39,8 +39,8 @@ where pub fn init() -> Result<()> { let mut state = KERNEL_STATE.lock(); - let mut vfs = VFS.lock(); let fs = load_fs()?; + let mut vfs = VFS.lock(); vfs.init(fs.root_node()); state.add_storage_device(fs); Ok(()) @@ -56,9 +56,12 @@ fn load_fs() -> Result>> { pub fn walk>(dir: Arc, path: S) { let path = path.into(); if let Some(dir) = dir.directory() { - let mut vfs = VFS.lock(); for entry in dir { - let fs_node = vfs.fs_node(entry.node()).unwrap(); + let fs_node = { + let mut vfs = VFS.lock(); + vfs.fs_node(entry.node()).unwrap() + }; + println!("{}/{} => {}", path, entry.name(), fs_node.inode()); trace!("{entry:#?}"); if entry.name() != "." && entry.name() != ".." { diff --git a/ableos/src/scratchpad.rs b/ableos/src/scratchpad.rs index 8cbf600e..3d872318 100644 --- a/ableos/src/scratchpad.rs +++ b/ableos/src/scratchpad.rs @@ -10,14 +10,13 @@ use crate::devices::pci::brute_force_scan; use crate::filesystem::vfs::VFS; use crate::systeminfo::{KERNEL_VERSION, RELEASE_TYPE}; use crate::time::fetch_time; -use crate::KERNEL_STATE; +use crate::{filesystem, KERNEL_STATE}; use crate::{ arch::shutdown, rhai_shell::KEYBUFF, vterm::VTerm, // wasm_jumploader::run_program, }; -use crate::{filesystem, KERNEL_STATE}; use acpi::{AcpiTables, PlatformInfo}; use cpuio::{inb, outb}; From f67e9b23721f033fa837c07934d8d3a3b85f931f Mon Sep 17 00:00:00 2001 From: TheOddGarlic Date: Sun, 7 Aug 2022 15:35:55 +0300 Subject: [PATCH 18/20] VFS: path resolution --- ableos/src/filesystem/errors.rs | 3 + ableos/src/filesystem/ext2.rs | 20 +- ableos/src/filesystem/mod.rs | 34 ++- ableos/src/filesystem/vfs.rs | 76 ++++++- ableos/src/relib/time/kilotime.rs | 70 +++--- ableos/src/scratchpad.rs | 14 +- repbuild/src/main.rs | 358 +++++++++++++++--------------- 7 files changed, 325 insertions(+), 250 deletions(-) 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