/* * Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me> * * SPDX-License-Identifier: MPL-2.0 */ pub mod errors; pub mod ext2; 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<T> = core::result::Result<T, FsError>; pub trait StorageDevice where Self: Send, { fn open(&self, node: Arc<FsNode> /* TODO: flags */) -> Result<FileDescriptor>; fn release(&self, node: FsNode) -> Result<()>; fn read(&self, node: &FsNode, offset: usize, size: usize) -> Result<Box<[u8]>>; fn write(&self, node: &FsNode, offset: usize, buffer: Box<[u8]>) -> Result<()>; fn read_dir(&self, node: &FsNode, index: usize) -> Result<DirectoryEntry>; fn find_dir(&self, node: &FsNode, name: &str) -> Result<FsNode>; } /// 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<FsNode>, // 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<FsNode>, ) -> 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<Self>) -> Result<Handle> { 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); 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. pub fn read(&self, offset: usize, size: usize) -> Result<Box<[u8]>> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; 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: usize, buffer: Box<[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<DirectoryEntry> { 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<FsNode> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; device.find_dir(self, name) } } impl Drop for FsNode { fn drop(&mut self) { trace!("dropping: {self:#?}") } } 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; } } /// A file descriptor. #[derive(Debug)] pub struct FileDescriptor { vfs_node: Weak<FsNode>, // 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<FsNode>, 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, }