/* * Copyright (c) 2022, Umut İnan Erdoğan * * SPDX-License-Identifier: MPL-2.0 */ pub mod errors; pub mod ext2; use core::cmp; use alloc::sync::{Weak, Arc}; use bitflags::bitflags; use crate::{handle::Handle, KERNEL_STATE}; use self::errors::FsError; 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 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. /// /// 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 .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, }