/* * Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me> * * SPDX-License-Identifier: MPL-2.0 */ pub mod errors; pub mod ext2; use alloc::rc::Weak; 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: &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. 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<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, 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(&mut self) -> Result<Handle> { 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); 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<()> { let state = KERNEL_STATE.lock(); let device = state .get_storage_device(self.device_handle) .ok_or_else(|| FsError::InvalidDevice)?; device.release(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: 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) } } 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. 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: usize, }