Files
CappuccinOS/src/drivers/fs/vfs.rs

589 lines
14 KiB
Rust
Executable File

use core::{fmt::Debug, ptr::NonNull};
use alloc::{
boxed::Box,
collections::BTreeMap,
rc::Rc,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use crate::{log_info, log_ok};
// TODO: probably keeps excess memory but whatever
struct TreeNode {
vnode: Rc<VNode>,
parent: Option<NonNull<Self>>,
children: BTreeMap<String, Self>,
}
impl TreeNode {
fn new(vnode: VNode) -> Self {
return Self {
vnode: Rc::new(vnode),
parent: None,
children: BTreeMap::new(),
};
}
fn as_ptr(&self) -> NonNull<Self> {
return NonNull::from(self);
}
fn get_vnode(&mut self) -> &mut VNode {
Rc::get_mut(&mut self.vnode).unwrap()
}
fn lookup(&mut self, name: &str) -> Result<&mut Self, ()> {
let parent = Some(self.as_ptr());
crate::println!("looking up {name} in node_tree");
if !self.children.contains_key(name) {
crate::println!("not found in node tree");
let vnode: VNode;
if let Some(mut vfs) = self.vnode.vfs_mounted_here {
crate::println!("using VFS root");
unsafe {
vnode = vfs
.as_mut()
.root()
.lookup(name, UserCred { uid: 0, gid: 0 })?
};
} else {
vnode = Rc::get_mut(&mut self.vnode)
.unwrap()
.lookup(name, UserCred { uid: 0, gid: 0 })?;
}
let child_node = TreeNode {
vnode: Rc::new(vnode),
parent,
children: BTreeMap::new(),
};
self.children.insert(name.to_string(), child_node);
let child = self.children.get_mut(name).unwrap();
return Ok(child);
}
return Ok(self.children.get_mut(name).unwrap());
}
}
static mut NODE_TREE: Option<TreeNode> = None;
static mut ROOT_VFS: Vfs = Vfs::null();
// TODO: everything being Option to accomodate the stupid null root vfs is getting annoying
#[allow(unused)]
pub struct Vfs {
next: Option<Box<Vfs>>,
pub fs: Option<Box<dyn FsOps>>,
vnode_covered: Option<NonNull<VNode>>,
flags: u32,
block_size: u32,
pub data: *mut u8,
}
impl !Sync for Vfs {}
impl Vfs {
const fn null() -> Self {
return Vfs {
next: None,
fs: None,
vnode_covered: None,
flags: 0,
block_size: 0,
data: core::ptr::null_mut(),
};
}
fn add_vfs(&mut self, vfs: Box<Self>) {
let mut current = self;
while let Some(ref mut next_vfs) = current.next {
current = next_vfs;
}
current.next = Some(vfs);
}
fn as_ptr(&self) -> NonNull<Self> {
unsafe { NonNull::new_unchecked(core::ptr::addr_of!(*self) as *mut Self) }
}
pub fn mount(&mut self, path: &str) {
if self.fs.is_none() {
panic!("FsOps is null");
}
let vfsp = self.as_ptr();
self.fs
.as_mut()
.unwrap()
.as_mut()
.mount(path, &mut self.data, vfsp);
}
pub fn unmount(&mut self) {
if self.fs.is_none() {
panic!("FsOps is null");
}
let vfsp = self.as_ptr();
self.fs.as_mut().unwrap().as_mut().unmount(vfsp);
}
pub fn root(&mut self) -> VNode {
if self.fs.is_none() {
panic!("FsOps is null");
}
let vfsp = self.as_ptr();
self.fs.as_mut().unwrap().as_mut().root(vfsp)
}
pub fn statfs(&mut self) -> StatFs {
if self.fs.is_none() {
panic!("FsOps is null");
}
let vfsp = self.as_ptr();
self.fs.as_mut().unwrap().as_mut().statfs(vfsp)
}
pub fn sync(&mut self) {
if self.fs.is_none() {
panic!("FsOps is null");
}
let vfsp = self.as_ptr();
self.fs.as_mut().unwrap().as_mut().sync(vfsp);
}
pub fn fid(&mut self, path: &str) -> Option<FileId> {
if self.fs.is_none() {
panic!("FsOps is null");
}
let vfsp = self.as_ptr();
self.fs.as_mut().unwrap().as_mut().fid(path, vfsp)
}
pub fn vget(&mut self, fid: FileId) -> VNode {
if self.fs.is_none() {
panic!("FsOps is null");
}
let vfsp = self.as_ptr();
self.fs.as_mut().unwrap().as_mut().vget(fid, vfsp)
}
}
pub trait FsOps {
// yes, the vfsp was the best solution I could come up with
fn mount(&mut self, path: &str, data: &mut *mut u8, vfsp: NonNull<Vfs>);
fn unmount(&mut self, vfsp: NonNull<Vfs>);
fn root(&mut self, vfsp: NonNull<Vfs>) -> VNode;
fn statfs(&mut self, vfsp: NonNull<Vfs>) -> StatFs;
fn sync(&mut self, vfsp: NonNull<Vfs>);
fn fid(&mut self, path: &str, vfsp: NonNull<Vfs>) -> Option<FileId>;
// idk how the fuck you're supposed to accomplish this
// good luck I guess.
fn vget(&mut self, fid: FileId, vfsp: NonNull<Vfs>) -> VNode;
}
#[allow(unused)]
pub struct FileId {
len: u16,
data: u8,
}
#[allow(unused)]
pub struct StatFs {
typ: u32,
block_size: u32,
total_blocks: u32,
free_blocks: u32,
available_blocks: u32, // non-protected blocks
files: u32,
free_nodes: u32,
fs_id: u32,
_reserved: [u8; 7],
}
#[repr(u8)]
#[derive(PartialEq)]
pub enum VNodeType {
// Jury is out on this one
NON = 0,
Regular,
Directory,
Block,
Character,
Link,
Socket,
Bad,
}
pub struct VNode {
pub flags: u16,
inode: Box<dyn VNodeOperations>,
pub node_data: Option<NodeData>,
pub vfs_mounted_here: Option<NonNull<Vfs>>,
pub parent_vfs: NonNull<Vfs>,
pub file_typ: VNodeType,
pub data: *mut u8,
}
impl VNode {
pub fn new(
inode: Box<dyn VNodeOperations>,
file_typ: VNodeType,
parent_vfs: NonNull<Vfs>,
) -> Self {
return Self {
flags: 0,
inode,
node_data: None,
vfs_mounted_here: None,
parent_vfs,
file_typ,
data: core::ptr::null_mut(),
};
}
pub fn as_ptr(&self) -> NonNull<VNode> {
unsafe { NonNull::new_unchecked(core::ptr::addr_of!(*self) as *mut Self) }
}
// Trait functions
pub fn open(&mut self, f: u32, c: UserCred) -> Result<Arc<[u8]>, ()> {
let vp = self.as_ptr();
self.inode.as_mut().open(f, c, vp)
}
pub fn close(&mut self, f: u32, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().close(f, c, vp)
}
pub fn rdwr(
&mut self,
uiop: *const UIO,
direction: IODirection,
f: u32,
c: UserCred,
) -> Result<Arc<[u8]>, ()> {
let vp = self.as_ptr();
self.inode.as_mut().rdwr(uiop, direction, f, c, vp)
}
pub fn ioctl(&mut self, com: u32, d: *mut u8, f: u32, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().ioctl(com, d, f, c, vp)
}
pub fn select(&mut self, w: IODirection, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().select(w, c, vp)
}
pub fn getattr(&mut self, c: UserCred) -> VAttr {
let vp = self.as_ptr();
self.inode.as_mut().getattr(c, vp)
}
pub fn setattr(&mut self, va: VAttr, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().setattr(va, c, vp)
}
pub fn access(&mut self, m: u32, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().access(m, c, vp)
}
pub fn lookup(&mut self, nm: &str, c: UserCred) -> Result<VNode, ()> {
let vp = self.as_ptr();
self.inode.as_mut().lookup(nm, c, vp)
}
pub fn create(
&mut self,
nm: &str,
va: VAttr,
e: u32,
m: u32,
c: UserCred,
) -> Result<VNode, ()> {
let vp = self.as_ptr();
self.inode.as_mut().create(nm, va, e, m, c, vp)
}
pub fn link(&mut self, target_dir: *mut VNode, target_name: &str, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().link(target_dir, target_name, c, vp)
}
pub fn rename(&mut self, nm: &str, target_dir: *mut VNode, target_name: &str, c: UserCred) {
let vp = self.as_ptr();
self.inode
.as_mut()
.rename(nm, target_dir, target_name, c, vp)
}
pub fn mkdir(&mut self, nm: &str, va: VAttr, c: UserCred) -> Result<VNode, ()> {
let vp = self.as_ptr();
self.inode.as_mut().mkdir(nm, va, c, vp)
}
pub fn readdir(&mut self, uiop: *const UIO, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().readdir(uiop, c, vp)
}
pub fn symlink(&mut self, link_name: &str, va: VAttr, target_name: &str, c: UserCred) {
let vp = self.as_ptr();
self.inode
.as_mut()
.symlink(link_name, va, target_name, c, vp)
}
pub fn readlink(&mut self, uiop: *const UIO, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().readlink(uiop, c, vp)
}
pub fn fsync(&mut self, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().fsync(c, vp)
}
pub fn inactive(&mut self, c: UserCred) {
let vp = self.as_ptr();
self.inode.as_mut().inactive(c, vp)
}
pub fn bmap(&mut self, block_number: u32, bnp: ()) -> VNode {
let vp = self.as_ptr();
self.inode.as_mut().bmap(block_number, bnp, vp)
}
pub fn strategy(&mut self, bp: ()) {
let vp = self.as_ptr();
self.inode.as_mut().strategy(bp, vp)
}
pub fn bread(&mut self, block_number: u32) -> Arc<[u8]> {
let vp = self.as_ptr();
self.inode.as_mut().bread(block_number, vp)
}
}
impl Debug for VNode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("VNode"))
}
}
#[repr(C)]
pub union NodeData {
socket: (), // Socket
stream_data: (), // Stream
}
pub struct UserCred {
pub uid: u16,
pub gid: u16,
}
pub enum IODirection {
Read,
Write,
}
#[allow(unused)]
pub struct IoVec {
iov_base: *mut u8,
iov_len: usize,
}
#[allow(unused)]
pub struct UIO {
iov: *mut IoVec,
iov_count: u32,
offset: usize,
seg_flag: u32,
file_mode: u32,
max_offset: usize,
residual_count: u32,
}
pub trait VNodeOperations {
fn open(&mut self, f: u32, c: UserCred, vp: NonNull<VNode>) -> Result<Arc<[u8]>, ()>;
fn close(&mut self, f: u32, c: UserCred, vp: NonNull<VNode>);
fn rdwr(
&mut self,
uiop: *const UIO,
direction: IODirection,
f: u32,
c: UserCred,
vp: NonNull<VNode>,
) -> Result<Arc<[u8]>, ()>;
fn ioctl(&mut self, com: u32, d: *mut u8, f: u32, c: UserCred, vp: NonNull<VNode>);
fn select(&mut self, w: IODirection, c: UserCred, vp: NonNull<VNode>);
fn getattr(&mut self, c: UserCred, vp: NonNull<VNode>) -> VAttr;
fn setattr(&mut self, va: VAttr, c: UserCred, vp: NonNull<VNode>);
fn access(&mut self, m: u32, c: UserCred, vp: NonNull<VNode>);
fn lookup(&mut self, nm: &str, c: UserCred, vp: NonNull<VNode>) -> Result<VNode, ()>;
fn create(
&mut self,
nm: &str,
va: VAttr,
e: u32,
m: u32,
c: UserCred,
vp: NonNull<VNode>,
) -> Result<VNode, ()>;
fn link(&mut self, target_dir: *mut VNode, target_name: &str, c: UserCred, vp: NonNull<VNode>);
fn rename(
&mut self,
nm: &str,
target_dir: *mut VNode,
target_name: &str,
c: UserCred,
vp: NonNull<VNode>,
);
fn mkdir(&mut self, nm: &str, va: VAttr, c: UserCred, vp: NonNull<VNode>) -> Result<VNode, ()>;
fn readdir(&mut self, uiop: *const UIO, c: UserCred, vp: NonNull<VNode>);
fn symlink(
&mut self,
link_name: &str,
va: VAttr,
target_name: &str,
c: UserCred,
vp: NonNull<VNode>,
);
fn readlink(&mut self, uiop: *const UIO, c: UserCred, vp: NonNull<VNode>);
fn fsync(&mut self, c: UserCred, vp: NonNull<VNode>);
fn inactive(&mut self, c: UserCred, vp: NonNull<VNode>);
fn bmap(&mut self, block_number: u32, bnp: (), vp: NonNull<VNode>) -> VNode;
fn strategy(&mut self, bp: (), vp: NonNull<VNode>);
fn bread(&mut self, block_number: u32, vp: NonNull<VNode>) -> Arc<[u8]>;
}
#[allow(unused)]
pub struct VAttr {
typ: VNode,
mode: u16,
uid: u16,
gid: u16,
fs_id: u32,
node_id: u32,
link_count: u16,
size: u32,
block_size: u32,
last_access: u32,
last_modify: u32,
// got no clue
last_chg: u32,
// the device???
rdev: (),
used_blocks: u32,
}
pub fn add_vfs(mount_point: &str, fs_ops: Box<dyn FsOps>) -> Result<(), ()> {
// Initialize the data so we can use the nonnull helpers
let mut new_vfs = Vfs::null();
new_vfs.fs = Some(fs_ops);
let mut vfs = Box::new(new_vfs);
let vfsp = vfs.as_ptr();
log_info!("Adding vfs at {mount_point}");
if mount_point == "/" {
if unsafe { ROOT_VFS.next.is_some() } {
return Err(());
}
unsafe { NODE_TREE = Some(TreeNode::new(vfs.fs.as_mut().unwrap().as_mut().root(vfsp))) }
} else {
if unsafe { ROOT_VFS.next.is_none() } {
return Err(());
}
if vfs_open(mount_point).is_err() {
return Err(());
}
let vnode = vfs_open(mount_point)?;
vnode.vfs_mounted_here = Some(vfsp);
}
vfs.mount(mount_point);
unsafe { ROOT_VFS.add_vfs(vfs) };
log_ok!("Added vfs at {mount_point}");
return Ok(());
}
pub fn vfs_open(path: &str) -> Result<&mut VNode, ()> {
if unsafe { ROOT_VFS.next.is_none() || NODE_TREE.is_none() } {
return Err(());
}
let mut cur_node = unsafe { NODE_TREE.as_mut().unwrap() };
let parts = path.split('/').collect::<Vec<&str>>();
for part in parts {
if part.is_empty() {
continue;
}
if let Ok(new_node) = cur_node.lookup(part) {
cur_node = new_node;
} else {
return Err(());
}
}
return Ok(cur_node.get_vnode());
}