diff --git a/fs/memfs/src/dir.rs b/fs/memfs/src/dir.rs index b87e1d6..84f1ef5 100644 --- a/fs/memfs/src/dir.rs +++ b/fs/memfs/src/dir.rs @@ -1,27 +1,30 @@ use crate::{BlockAllocator, Bvec, FileInode}; use alloc::boxed::Box; -use libsys::{error::Errno, stat::Stat}; -use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; +use libsys::{error::Errno, stat::{Stat, DirectoryEntry, OpenFlags}, ioctl::IoctlCmd}; +use vfs::{Vnode, VnodeCommon, VnodeDirectory, VnodeRef, VnodeCreateKind, VnodeData}; +use core::cell::RefCell; pub struct DirInode { alloc: A, } -#[auto_inode] -impl VnodeImpl for DirInode { +impl VnodeDirectory for DirInode { fn create( &mut self, _parent: VnodeRef, name: &str, - kind: VnodeKind, + kind: VnodeCreateKind, ) -> Result { - let vnode = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR); - match kind { - VnodeKind::Directory => vnode.set_data(Box::new(DirInode { alloc: self.alloc })), - VnodeKind::Regular => vnode.set_data(Box::new(FileInode::new(Bvec::new(self.alloc)))), - _ => todo!(), - } - Ok(vnode) + let data = match kind { + VnodeCreateKind::Directory => VnodeData::Directory(RefCell::new(Some(Box::new(DirInode { alloc: self.alloc })))), + VnodeCreateKind::File => VnodeData::File(RefCell::new(Some(Box::new(FileInode::new(Bvec::new(self.alloc)))))), + _ => todo!() + }; + Ok(Vnode::new(name, data, Vnode::SEEKABLE | Vnode::CACHE_READDIR)) + // match kind { + // _ => todo!(), + // } + // Ok(vnode) } fn lookup(&mut self, _parent: VnodeRef, _name: &str) -> Result { @@ -32,6 +35,18 @@ impl VnodeImpl for DirInode { Ok(()) } + /// Read directory entries into target buffer + fn readdir( + &mut self, + node: VnodeRef, + pos: usize, + data: &mut [DirectoryEntry], + ) -> Result { + todo!() + } +} + +impl VnodeCommon for DirInode { fn stat(&mut self, node: VnodeRef) -> Result { let props = node.props(); Ok(Stat { @@ -40,6 +55,35 @@ impl VnodeImpl for DirInode { mode: props.mode, }) } + + /// Performs filetype-specific request + fn ioctl( + &mut self, + node: VnodeRef, + cmd: IoctlCmd, + ptr: usize, + len: usize, + ) -> Result { + todo!() + } + + /// Reports the size of this filesystem object in bytes + fn size(&mut self, node: VnodeRef) -> Result { + todo!() + } + + /// Returns `true` if node is ready for an operation + fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result { + todo!() + } + + fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result { + Ok(0) + } + + fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { + Ok(()) + } } impl DirInode { diff --git a/fs/memfs/src/file.rs b/fs/memfs/src/file.rs index c3b8e8d..fc5e7d8 100644 --- a/fs/memfs/src/file.rs +++ b/fs/memfs/src/file.rs @@ -2,15 +2,16 @@ use crate::{BlockAllocator, Bvec}; use libsys::{ error::Errno, stat::{OpenFlags, Stat}, + ioctl::IoctlCmd }; -use vfs::{VnodeImpl, VnodeKind, VnodeRef}; +use vfs::{VnodeCommon, VnodeFile, VnodeRef}; pub struct FileInode<'a, A: BlockAllocator + Copy + 'static> { data: Bvec<'a, A>, } -#[auto_inode] -impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { +// #[auto_inode] +impl<'a, A: BlockAllocator + Copy + 'static> VnodeCommon for FileInode<'a, A> { fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result { Ok(0) } @@ -18,19 +19,6 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { Ok(()) } - - fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { - self.data.read(pos, data) - } - - fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result { - self.data.write(pos, data) - } - - fn truncate(&mut self, _node: VnodeRef, size: usize) -> Result<(), Errno> { - self.data.resize((size + 4095) / 4096) - } - fn size(&mut self, _node: VnodeRef) -> Result { Ok(self.data.size()) } @@ -43,6 +31,37 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { mode: props.mode }) } + + /// Performs filetype-specific request + fn ioctl( + &mut self, + node: VnodeRef, + cmd: IoctlCmd, + ptr: usize, + len: usize, + ) -> Result { + todo!() + } + + /// Returns `true` if node is ready for an operation + fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result { + todo!() + } +} + +impl<'a, A: BlockAllocator + Copy + 'static> VnodeFile for FileInode<'a, A> { + fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { + self.data.read(pos, data) + } + + fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result { + self.data.write(pos, data) + } + + fn truncate(&mut self, _node: VnodeRef, size: usize) -> Result<(), Errno> { + self.data.resize((size + 4095) / 4096) + } + } impl<'a, A: BlockAllocator + Copy + 'static> FileInode<'a, A> { diff --git a/fs/memfs/src/lib.rs b/fs/memfs/src/lib.rs index 17e6feb..6bca71f 100644 --- a/fs/memfs/src/lib.rs +++ b/fs/memfs/src/lib.rs @@ -1,8 +1,4 @@ -#![feature( - const_fn_trait_bound, - const_mut_refs, - maybe_uninit_uninit_array -)] +#![feature(const_fn_trait_bound, const_mut_refs, maybe_uninit_uninit_array)] #![no_std] extern crate alloc; @@ -21,14 +17,14 @@ use libsys::{ path::{path_component_left, path_component_right}, stat::FileMode, }; -use vfs::{BlockDevice, Filesystem, Vnode, VnodeKind, VnodeRef}; +use vfs::{BlockDevice, Filesystem, Vnode, VnodeCreateKind, VnodeData, VnodeRef}; mod block; pub use block::{BlockAllocator, BlockRef}; mod bvec; use bvec::Bvec; mod tar; -use tar::{TarIterator, Tar}; +use tar::{Tar, TarIterator}; mod file; use file::FileInode; mod dir; @@ -67,16 +63,17 @@ impl Ramfs { } fn create_node_initial(self: Rc, name: &str, tar: &Tar) -> VnodeRef { - let kind = tar.node_kind(); - let node = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR); + let kind = tar.node_create_kind(); + let data = match kind { + VnodeCreateKind::Directory => { + VnodeData::Directory(RefCell::new(Some(Box::new(DirInode::new(self.alloc))))) + } + VnodeCreateKind::File => VnodeData::File(RefCell::new(None)), + _ => todo!(), + }; + let node = Vnode::new(name, data, Vnode::SEEKABLE | Vnode::CACHE_READDIR); node.props_mut().mode = tar.mode(); node.set_fs(self.clone()); - match kind { - VnodeKind::Directory => node.set_data(Box::new(DirInode::new(self.alloc))), - VnodeKind::Regular => {} - VnodeKind::Char => todo!(), - VnodeKind::Block => todo!(), - }; node } @@ -100,7 +97,8 @@ impl Ramfs { return Err(Errno::DoesNotExist); } // TODO file modes - at.create(element, FileMode::default_dir(), VnodeKind::Directory)? + at.create(element, FileMode::default_dir(), VnodeCreateKind::Directory)? + // todo!(); } }; @@ -112,9 +110,10 @@ impl Ramfs { } unsafe fn load_tar(self: Rc, base: *const u8, size: usize) -> Result { - let root = Vnode::new("", VnodeKind::Directory, Vnode::SEEKABLE | Vnode::CACHE_READDIR); + let root_data = + VnodeData::Directory(RefCell::new(Some(Box::new(DirInode::new(self.alloc))))); + let root = Vnode::new("", root_data, Vnode::SEEKABLE | Vnode::CACHE_READDIR); root.set_fs(self.clone()); - root.set_data(Box::new(DirInode::new(self.alloc))); root.props_mut().mode = FileMode::default_dir(); // 1. Create all the paths in TAR @@ -122,10 +121,7 @@ impl Ramfs { let (dirname, basename) = path_component_right(block.path()?); let parent = self.clone().make_path(root.clone(), dirname, true)?; - let node = self - .clone() - .create_node_initial(basename, block); - assert_eq!(node.kind(), block.node_kind()); + let node = self.clone().create_node_initial(basename, block); parent.attach(node); } @@ -134,16 +130,17 @@ impl Ramfs { if block.is_file() { // Will not create any dirs let node = self.clone().make_path(root.clone(), block.path()?, false)?; - assert_eq!(node.kind(), block.node_kind()); #[cfg(feature = "cow")] { let data = block.data(); - node.set_data(Box::new(FileInode::new(Bvec::new_copy_on_write( - self.alloc, - data.as_ptr(), - data.len(), - )))); + node.as_file() + .unwrap() + .replace(Some(Box::new(FileInode::new(Bvec::new_copy_on_write( + self.alloc, + data.as_ptr(), + data.len(), + ))))); } #[cfg(not(feature = "cow"))] { @@ -166,7 +163,7 @@ impl Ramfs { mod tests { use super::*; use alloc::boxed::Box; - use libcommon::Read; + use libsys::{traits::Read, stat::{UserId, GroupId, OpenFlags}}; use vfs::Ioctx; #[test] @@ -188,15 +185,15 @@ mod tests { let fs = unsafe { Ramfs::open(data.as_ptr(), data.bytes().len(), A {}).unwrap() }; let root = fs.root().unwrap(); - let ioctx = Ioctx::new(root.clone()); + let ioctx = Ioctx::new(root.clone(), UserId::root(), GroupId::root()); assert!(Rc::ptr_eq(&ioctx.find(None, "/", true).unwrap(), &root)); let node = ioctx.find(None, "/test1.txt", true).unwrap(); - let mut file = node.open().unwrap(); + let mut file = node.open(OpenFlags::O_RDONLY).unwrap(); let mut buf = [0u8; 1024]; - assert_eq!(file.read(&mut buf).unwrap(), 20); + assert_eq!(file.borrow_mut().read(&mut buf).unwrap(), 20); let s = core::str::from_utf8(&buf[..20]).unwrap(); assert_eq!(s, "This is a test file\n"); } diff --git a/fs/memfs/src/tar.rs b/fs/memfs/src/tar.rs index 5326a2f..92399d9 100644 --- a/fs/memfs/src/tar.rs +++ b/fs/memfs/src/tar.rs @@ -1,5 +1,5 @@ use libsys::{error::Errno, stat::FileMode}; -use vfs::VnodeKind; +use vfs::{VnodeData, VnodeCreateKind}; #[repr(packed)] #[allow(dead_code)] @@ -73,18 +73,18 @@ impl Tar { core::str::from_utf8(&self.name[..zero_index]).map_err(|_| Errno::InvalidArgument) } - pub fn node_kind(&self) -> VnodeKind { + pub fn node_create_kind(&self) -> VnodeCreateKind { match self.type_ { - 0 | b'0' => VnodeKind::Regular, - b'5' => VnodeKind::Directory, + 0 | b'0' => VnodeCreateKind::File, + b'5' => VnodeCreateKind::Directory, p => panic!("Unrecognized tar entry type: '{}'", p as char), } } pub fn mode(&self) -> FileMode { - let t = match self.node_kind() { - VnodeKind::Regular => FileMode::S_IFREG, - VnodeKind::Directory => FileMode::S_IFDIR, + let t = match self.node_create_kind() { + VnodeCreateKind::File => FileMode::S_IFREG, + VnodeCreateKind::Directory => FileMode::S_IFDIR, _ => todo!() }; FileMode::from_bits(from_octal(&self.mode) as u32).unwrap() | t diff --git a/fs/vfs/src/char.rs b/fs/vfs/src/char.rs index b4d2fe4..5dedbfe 100644 --- a/fs/vfs/src/char.rs +++ b/fs/vfs/src/char.rs @@ -1,4 +1,4 @@ -use crate::{VnodeImpl, VnodeKind, VnodeRef}; +use crate::{VnodeData, VnodeRef}; use libsys::{error::Errno, ioctl::IoctlCmd, stat::OpenFlags}; /// Generic character device trait @@ -22,50 +22,50 @@ pub trait CharDevice { /// Returns `true` if the device is ready for an operation fn is_ready(&self, write: bool) -> Result; } - -/// Wrapper struct to attach [VnodeImpl] implementation -/// to [CharDevice]s -pub struct CharDeviceWrapper { - device: &'static dyn CharDevice, -} - -#[auto_inode(error)] -impl VnodeImpl for CharDeviceWrapper { - fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result { - Ok(0) - } - - fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { - Ok(()) - } - - fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result { - self.device.read(true, data) - } - - fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result { - self.device.write(true, data) - } - - fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result { - self.device.is_ready(write) - } - - fn ioctl( - &mut self, - _node: VnodeRef, - cmd: IoctlCmd, - ptr: usize, - len: usize, - ) -> Result { - self.device.ioctl(cmd, ptr, len) - } -} - -impl CharDeviceWrapper { - /// Creates a wrapper for static [CharDevice] trait object to - /// auto-implement [VnodeImpl] trait for the device - pub const fn new(device: &'static dyn CharDevice) -> Self { - Self { device } - } -} +// +// /// Wrapper struct to attach [VnodeImpl] implementation +// /// to [CharDevice]s +// pub struct CharDeviceWrapper { +// device: &'static dyn CharDevice, +// } +// +// // #[auto_inode(error)] +// impl VnodeCommon for CharDeviceWrapper { +// fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result { +// Ok(0) +// } +// +// fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { +// Ok(()) +// } +// +// fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result { +// self.device.read(true, data) +// } +// +// fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result { +// self.device.write(true, data) +// } +// +// fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result { +// self.device.is_ready(write) +// } +// +// fn ioctl( +// &mut self, +// _node: VnodeRef, +// cmd: IoctlCmd, +// ptr: usize, +// len: usize, +// ) -> Result { +// self.device.ioctl(cmd, ptr, len) +// } +// } +// +// impl CharDeviceWrapper { +// /// Creates a wrapper for static [CharDevice] trait object to +// /// auto-implement [VnodeImpl] trait for the device +// pub const fn new(device: &'static dyn CharDevice) -> Self { +// Self { device } +// } +// } diff --git a/fs/vfs/src/file.rs b/fs/vfs/src/file.rs index d4ce1df..056c5c0 100644 --- a/fs/vfs/src/file.rs +++ b/fs/vfs/src/file.rs @@ -1,4 +1,4 @@ -use crate::{VnodeKind, VnodeRef, Vnode}; +use crate::{Vnode, VnodeData, VnodeRef}; use alloc::rc::Rc; use core::cell::RefCell; use core::cmp::min; @@ -39,9 +39,10 @@ impl Read for File { match &mut self.inner { FileInner::Normal(inner) => { let count = inner.vnode.read(inner.pos, data)?; - if inner.vnode.kind() != VnodeKind::Char { - inner.pos += count; - } + // TODO + // if inner.vnode.kind() != VnodeKind::Char { + inner.pos += count; + // } Ok(count) } _ => unimplemented!(), @@ -58,9 +59,10 @@ impl Write for File { match &mut self.inner { FileInner::Normal(inner) => { let count = inner.vnode.write(inner.pos, data)?; - if inner.vnode.kind() != VnodeKind::Char { - inner.pos += count; - } + // TODO + // if inner.vnode.kind() != VnodeKind::Char { + inner.pos += count; + // } Ok(count) } _ => unimplemented!(), @@ -134,7 +136,10 @@ impl File { } } - fn cache_readdir(inner: &mut NormalFile, entries: &mut [DirectoryEntry]) -> Result { + fn cache_readdir( + inner: &mut NormalFile, + entries: &mut [DirectoryEntry], + ) -> Result { let mut count = entries.len(); let mut offset = 0usize; @@ -177,7 +182,7 @@ impl File { pub fn readdir(&mut self, entries: &mut [DirectoryEntry]) -> Result { match &mut self.inner { FileInner::Normal(inner) => { - assert_eq!(inner.vnode.kind(), VnodeKind::Directory); + // assert_eq!(inner.vnode.kind(), VnodeKind::Directory); if inner.vnode.flags() & Vnode::CACHE_READDIR != 0 { Self::cache_readdir(inner, entries) @@ -204,24 +209,38 @@ impl Drop for File { #[cfg(test)] mod tests { use super::*; - use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; - use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat}; use alloc::boxed::Box; use alloc::rc::Rc; + use crate::node::{VnodeCommon, VnodeFile}; + use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat}; struct DummyInode; - #[auto_inode] - impl VnodeImpl for DummyInode { - fn create( + impl VnodeCommon for DummyInode { + /// Performs filetype-specific request + fn ioctl( &mut self, - _at: VnodeRef, - name: &str, - kind: VnodeKind, - ) -> Result { - let node = Vnode::new(name, kind, 0); - node.set_data(Box::new(DummyInode {})); - Ok(node) + node: VnodeRef, + cmd: IoctlCmd, + ptr: usize, + len: usize, + ) -> Result { + todo!() + } + + /// Retrieves file status + fn stat(&mut self, node: VnodeRef) -> Result { + todo!() + } + + /// Reports the size of this filesystem object in bytes + fn size(&mut self, node: VnodeRef) -> Result { + todo!() + } + + /// Returns `true` if node is ready for an operation + fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result { + todo!() } fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result { @@ -232,6 +251,9 @@ mod tests { Ok(()) } + } + + impl VnodeFile for DummyInode { fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { #[cfg(test)] println!("read {} at {}", data.len(), pos); @@ -249,12 +271,19 @@ mod tests { fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { Err(Errno::NotImplemented) } + + fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno> { + todo!() + } } #[test] fn test_normal_read() { - let node = Vnode::new("", VnodeKind::Regular, 0); - node.set_data(Box::new(DummyInode {})); + let node = Vnode::new( + "", + VnodeData::File(RefCell::new(Some(Box::new(DummyInode {})))), + 0, + ); let mut file = node.open(OpenFlags::O_RDONLY).unwrap(); let mut buf = [0u8; 4096]; diff --git a/fs/vfs/src/ioctx.rs b/fs/vfs/src/ioctx.rs index f55b3c7..d89dddf 100644 --- a/fs/vfs/src/ioctx.rs +++ b/fs/vfs/src/ioctx.rs @@ -1,4 +1,4 @@ -use crate::{FileRef, VnodeKind, VnodeRef}; +use crate::{FileRef, VnodeData, VnodeRef, VnodeCreateKind}; use libsys::{ error::Errno, path::{path_component_left, path_component_right}, @@ -34,9 +34,10 @@ impl Ioctx { loop { (element, rest) = path_component_left(rest); - if !at.is_directory() { - return Err(Errno::NotADirectory); - } + at.as_directory()?; + // if !at.is_directory() { + // return Err(Errno::NotADirectory); + // } match element { ".." => { @@ -48,7 +49,7 @@ impl Ioctx { } while let Some(target) = at.target() { - assert!(at.kind() == VnodeKind::Directory); + // assert!(at.kind() == VnodeKind::Directory); at = target; } @@ -60,7 +61,7 @@ impl Ioctx { let mut node = at.lookup_or_load(element)?; while let Some(target) = node.target() { - assert!(node.kind() == VnodeKind::Directory); + // assert!(node.kind() == VnodeKind::Directory); node = target; } @@ -97,12 +98,13 @@ impl Ioctx { path: &str, mode: FileMode, ) -> Result { - let (parent, name) = path_component_right(path); - self.find(at, parent, true)?.create( - name.trim_start_matches('/'), - mode, - VnodeKind::Directory, - ) + todo!() + // let (parent, name) = path_component_right(path); + // self.find(at, parent, true)?.create( + // name.trim_start_matches('/'), + // mode, + // VnodeKind::Directory, + // ) } /// Opens (and possibly creates) a filesystem path for access @@ -117,7 +119,7 @@ impl Ioctx { Err(Errno::DoesNotExist) => { let (parent, name) = path_component_right(path); let at = self.find(at, parent, true)?; - at.create(name, mode, VnodeKind::Regular) + at.create(name, mode, VnodeCreateKind::File) } o => o, }?; @@ -127,199 +129,200 @@ impl Ioctx { /// Changes current working directory of the process pub fn chdir(&mut self, path: &str) -> Result<(), Errno> { - let node = self.find(None, path, true)?; - if !node.is_directory() { - return Err(Errno::NotADirectory); - } - self.cwd = node; - Ok(()) + todo!() + // let node = self.find(None, path, true)?; + // if !node.is_directory() { + // return Err(Errno::NotADirectory); + // } + // self.cwd = node; + // Ok(()) } } -#[cfg(test)] -mod tests { - use super::*; - use crate::{Vnode, VnodeImpl, VnodeKind}; - use alloc::{boxed::Box, rc::Rc}; - use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat}; - - pub struct DummyInode; - - #[auto_inode] - impl VnodeImpl for DummyInode { - fn create( - &mut self, - _at: VnodeRef, - name: &str, - kind: VnodeKind, - ) -> Result { - let vnode = Vnode::new(name, kind, 0); - vnode.set_data(Box::new(DummyInode {})); - Ok(vnode) - } - - fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { - Err(Errno::DoesNotExist) - } - } - - #[test] - fn test_find_existing_absolute() { - let root = Vnode::new("", VnodeKind::Directory, 0); - let d0 = Vnode::new("dir0", VnodeKind::Directory, 0); - let d1 = Vnode::new("dir1", VnodeKind::Directory, 0); - let d0d0 = Vnode::new("dir0", VnodeKind::Directory, 0); - let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0); - let d1f0 = Vnode::new("file0", VnodeKind::Regular, 0); - - root.attach(d0.clone()); - root.attach(d1.clone()); - d0.attach(d0d0.clone()); - d0.attach(d0f0.clone()); - d1.attach(d1f0.clone()); - - let ioctx = Ioctx::new(root.clone()); - - assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/", false).unwrap())); - assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.", false).unwrap())); - assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/./.", false).unwrap())); - assert!(Rc::ptr_eq( - &root, - &ioctx.find(None, "/.///.", false).unwrap() - )); - assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/..", false).unwrap())); - assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../", false).unwrap())); - assert!(Rc::ptr_eq( - &root, - &ioctx.find(None, "/../.", false).unwrap() - )); - assert!(Rc::ptr_eq( - &root, - &ioctx.find(None, "/../..", false).unwrap() - )); - - assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0", false).unwrap())); - assert!(Rc::ptr_eq(&d1, &ioctx.find(None, "/dir1", false).unwrap())); - assert!(Rc::ptr_eq( - &d0, - &ioctx.find(None, "/dir1/../dir0", false).unwrap() - )); - assert!(Rc::ptr_eq( - &d1, - &ioctx - .find(None, "/dir1/../dir0/./../../.././dir1", false) - .unwrap() - )); - - assert!(Rc::ptr_eq( - &d0d0, - &ioctx.find(None, "/dir0/dir0", false).unwrap() - )); - assert!(Rc::ptr_eq( - &d0d0, - &ioctx.find(None, "/dir0/dir0/.", false).unwrap() - )); - assert!(Rc::ptr_eq( - &d0, - &ioctx.find(None, "/dir0/dir0/..", false).unwrap() - )); - assert!(Rc::ptr_eq( - &d0, - &ioctx.find(None, "/dir0/dir0/../", false).unwrap() - )); - assert!(Rc::ptr_eq( - &d0, - &ioctx.find(None, "/dir0/dir0/../.", false).unwrap() - )); - - assert!(Rc::ptr_eq( - &d0f0, - &ioctx.find(None, "/dir0/file0", false).unwrap() - )); - assert!(Rc::ptr_eq( - &d0f0, - &ioctx.find(None, "/dir1/../dir0/./file0", false).unwrap() - )); - } - - #[test] - fn test_find_rejects_file_dots() { - let root = Vnode::new("", VnodeKind::Directory, 0); - let d0 = Vnode::new("dir0", VnodeKind::Directory, 0); - let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0); - - root.attach(d0.clone()); - d0.attach(d0f0.clone()); - - let ioctx = Ioctx::new(root.clone()); - - assert_eq!( - ioctx.find(None, "/dir0/file0/.", false).unwrap_err(), - Errno::NotADirectory - ); - assert_eq!( - ioctx.find(None, "/dir0/file0/..", false).unwrap_err(), - Errno::NotADirectory - ); - - // TODO handle this case - // assert_eq!(ioctx.find(None, "/dir0/file0/").unwrap_err(), Errno::NotADirectory); - } - - #[test] - fn test_mkdir() { - let root = Vnode::new("", VnodeKind::Directory, 0); - let ioctx = Ioctx::new(root.clone()); - - root.set_data(Box::new(DummyInode {})); - - assert!(ioctx.mkdir(None, "/dir0", FileMode::default_dir()).is_ok()); - assert_eq!( - ioctx - .mkdir(None, "/dir0", FileMode::default_dir()) - .unwrap_err(), - Errno::AlreadyExists - ); - } - - #[test] - fn test_find_mount() { - let root_outer = Vnode::new("", VnodeKind::Directory, 0); - let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0); - let root_inner = Vnode::new("", VnodeKind::Directory, 0); - let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0); - - root_outer.clone().attach(dir0.clone()); - root_inner.clone().attach(dir1.clone()); - - let ioctx = Ioctx::new(root_outer.clone()); - - assert_eq!( - ioctx.find(None, "/dir0/dir1", false).unwrap_err(), - Errno::DoesNotExist - ); - - dir0.mount(root_inner.clone()).unwrap(); - - assert!(Rc::ptr_eq( - &root_inner, - &ioctx.find(None, "/dir0", false).unwrap() - )); - assert!(Rc::ptr_eq( - &dir1, - &ioctx.find(None, "/dir0/dir1", false).unwrap() - )); - assert!(Rc::ptr_eq( - &root_inner, - &ioctx.find(None, "/dir0/dir1/..", false).unwrap() - )); - assert!(Rc::ptr_eq( - &dir0, - &ioctx.find(None, "/dir0/dir1/../..", false).unwrap() - )); - assert!(Rc::ptr_eq( - &root_outer, - &ioctx.find(None, "/dir0/dir1/../../..", false).unwrap() - )); - } -} +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::{Vnode, VnodeImpl, VnodeKind}; +// use alloc::{boxed::Box, rc::Rc}; +// use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat}; +// +// pub struct DummyInode; +// +// #[auto_inode] +// impl VnodeImpl for DummyInode { +// fn create( +// &mut self, +// _at: VnodeRef, +// name: &str, +// kind: VnodeKind, +// ) -> Result { +// let vnode = Vnode::new(name, kind, 0); +// vnode.set_data(Box::new(DummyInode {})); +// Ok(vnode) +// } +// +// fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { +// Err(Errno::DoesNotExist) +// } +// } +// +// #[test] +// fn test_find_existing_absolute() { +// let root = Vnode::new("", VnodeKind::Directory, 0); +// let d0 = Vnode::new("dir0", VnodeKind::Directory, 0); +// let d1 = Vnode::new("dir1", VnodeKind::Directory, 0); +// let d0d0 = Vnode::new("dir0", VnodeKind::Directory, 0); +// let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0); +// let d1f0 = Vnode::new("file0", VnodeKind::Regular, 0); +// +// root.attach(d0.clone()); +// root.attach(d1.clone()); +// d0.attach(d0d0.clone()); +// d0.attach(d0f0.clone()); +// d1.attach(d1f0.clone()); +// +// let ioctx = Ioctx::new(root.clone()); +// +// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/", false).unwrap())); +// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.", false).unwrap())); +// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/./.", false).unwrap())); +// assert!(Rc::ptr_eq( +// &root, +// &ioctx.find(None, "/.///.", false).unwrap() +// )); +// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/..", false).unwrap())); +// assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../", false).unwrap())); +// assert!(Rc::ptr_eq( +// &root, +// &ioctx.find(None, "/../.", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &root, +// &ioctx.find(None, "/../..", false).unwrap() +// )); +// +// assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0", false).unwrap())); +// assert!(Rc::ptr_eq(&d1, &ioctx.find(None, "/dir1", false).unwrap())); +// assert!(Rc::ptr_eq( +// &d0, +// &ioctx.find(None, "/dir1/../dir0", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &d1, +// &ioctx +// .find(None, "/dir1/../dir0/./../../.././dir1", false) +// .unwrap() +// )); +// +// assert!(Rc::ptr_eq( +// &d0d0, +// &ioctx.find(None, "/dir0/dir0", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &d0d0, +// &ioctx.find(None, "/dir0/dir0/.", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &d0, +// &ioctx.find(None, "/dir0/dir0/..", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &d0, +// &ioctx.find(None, "/dir0/dir0/../", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &d0, +// &ioctx.find(None, "/dir0/dir0/../.", false).unwrap() +// )); +// +// assert!(Rc::ptr_eq( +// &d0f0, +// &ioctx.find(None, "/dir0/file0", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &d0f0, +// &ioctx.find(None, "/dir1/../dir0/./file0", false).unwrap() +// )); +// } +// +// #[test] +// fn test_find_rejects_file_dots() { +// let root = Vnode::new("", VnodeKind::Directory, 0); +// let d0 = Vnode::new("dir0", VnodeKind::Directory, 0); +// let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0); +// +// root.attach(d0.clone()); +// d0.attach(d0f0.clone()); +// +// let ioctx = Ioctx::new(root.clone()); +// +// assert_eq!( +// ioctx.find(None, "/dir0/file0/.", false).unwrap_err(), +// Errno::NotADirectory +// ); +// assert_eq!( +// ioctx.find(None, "/dir0/file0/..", false).unwrap_err(), +// Errno::NotADirectory +// ); +// +// // TODO handle this case +// // assert_eq!(ioctx.find(None, "/dir0/file0/").unwrap_err(), Errno::NotADirectory); +// } +// +// #[test] +// fn test_mkdir() { +// let root = Vnode::new("", VnodeKind::Directory, 0); +// let ioctx = Ioctx::new(root.clone()); +// +// root.set_data(Box::new(DummyInode {})); +// +// assert!(ioctx.mkdir(None, "/dir0", FileMode::default_dir()).is_ok()); +// assert_eq!( +// ioctx +// .mkdir(None, "/dir0", FileMode::default_dir()) +// .unwrap_err(), +// Errno::AlreadyExists +// ); +// } +// +// #[test] +// fn test_find_mount() { +// let root_outer = Vnode::new("", VnodeKind::Directory, 0); +// let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0); +// let root_inner = Vnode::new("", VnodeKind::Directory, 0); +// let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0); +// +// root_outer.clone().attach(dir0.clone()); +// root_inner.clone().attach(dir1.clone()); +// +// let ioctx = Ioctx::new(root_outer.clone()); +// +// assert_eq!( +// ioctx.find(None, "/dir0/dir1", false).unwrap_err(), +// Errno::DoesNotExist +// ); +// +// dir0.mount(root_inner.clone()).unwrap(); +// +// assert!(Rc::ptr_eq( +// &root_inner, +// &ioctx.find(None, "/dir0", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &dir1, +// &ioctx.find(None, "/dir0/dir1", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &root_inner, +// &ioctx.find(None, "/dir0/dir1/..", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &dir0, +// &ioctx.find(None, "/dir0/dir1/../..", false).unwrap() +// )); +// assert!(Rc::ptr_eq( +// &root_outer, +// &ioctx.find(None, "/dir0/dir1/../../..", false).unwrap() +// )); +// } +// } diff --git a/fs/vfs/src/lib.rs b/fs/vfs/src/lib.rs index c4264be..6bbbc0d 100644 --- a/fs/vfs/src/lib.rs +++ b/fs/vfs/src/lib.rs @@ -1,6 +1,6 @@ //! Virtual filesystem API and facilities #![warn(missing_docs)] -#![feature(const_fn_trait_bound)] +#![feature(const_fn_trait_bound, const_discriminant)] #![no_std] #[cfg(test)] @@ -20,10 +20,10 @@ pub use block::BlockDevice; mod fs; pub use fs::Filesystem; mod node; -pub use node::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; +pub use node::{Vnode, VnodeData, VnodeRef, VnodeCreateKind, VnodeCommon, VnodeFile, VnodeDirectory}; mod ioctx; pub use ioctx::Ioctx; mod file; pub use file::{File, FileRef}; mod char; -pub use crate::char::{CharDevice, CharDeviceWrapper}; +pub use crate::char::CharDevice; diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index 934488e..e2c0d41 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -1,7 +1,9 @@ -use crate::{File, FileRef, Filesystem, Ioctx}; +use crate::{File, FileRef, Filesystem, Ioctx, CharDevice, BlockDevice}; use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec}; +use core::borrow::BorrowMut; use core::cell::{Ref, RefCell, RefMut}; use core::fmt; +use core::mem::Discriminant; use libsys::{ error::Errno, ioctl::IoctlCmd, @@ -10,20 +12,75 @@ use libsys::{ /// Convenience type alias for [Rc] pub type VnodeRef = Rc; +pub type VnodeKind = Discriminant; -/// List of possible vnode types -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum VnodeKind { - /// Node is a directory with create/lookup/remove operations - Directory, - /// Node is a regular file - Regular, - /// Node is a character device - Char, - /// Node is a block device - Block, +pub trait VnodeCommon { + /// Performs filetype-specific request + fn ioctl( + &mut self, + node: VnodeRef, + cmd: IoctlCmd, + ptr: usize, + len: usize, + ) -> Result; + + /// Retrieves file status + fn stat(&mut self, node: VnodeRef) -> Result; + + /// Reports the size of this filesystem object in bytes + fn size(&mut self, node: VnodeRef) -> Result; + + /// Returns `true` if node is ready for an operation + fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result; + + /// Opens a vnode for access. Returns initial file position. + fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result; + /// Closes a vnode + fn close(&mut self, node: VnodeRef) -> Result<(), Errno>; } +pub trait VnodeFile: VnodeCommon { + /// Changes file's underlying storage size + fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno>; + /// Reads `data.len()` bytes into the buffer from file offset `pos` + fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result; + /// Writes `data.len()` bytes from the buffer to file offset `pos`. + /// Resizes the file storage if necessary. + fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result; +} + +pub trait VnodeDirectory: VnodeCommon { + fn create( + &mut self, + at: VnodeRef, + name: &str, + kind: VnodeCreateKind, + ) -> Result; + fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>; + fn lookup(&mut self, at: VnodeRef, name: &str) -> Result; + + /// Read directory entries into target buffer + fn readdir( + &mut self, + node: VnodeRef, + pos: usize, + data: &mut [DirectoryEntry], + ) -> Result; +} + +// /// List of possible vnode types +// #[derive(Debug, Clone, Copy, PartialEq)] +// pub enum VnodeKind { +// /// Node is a directory with create/lookup/remove operations +// Directory, +// /// Node is a regular file +// Regular, +// /// Node is a character device +// Char, +// /// Node is a block device +// Block, +// } + pub(crate) struct TreeNode { parent: Option, children: Vec, @@ -35,6 +92,18 @@ pub struct VnodeProps { pub mode: FileMode, } +pub enum VnodeData { + Directory(RefCell>>), + File(RefCell>>), + Char(&'static dyn CharDevice) +} + +#[derive(Debug, Clone, Copy)] +pub enum VnodeCreateKind { + Directory, + File, +} + /// Virtual filesystem node struct, generalizes access to /// underlying real filesystems pub struct Vnode { @@ -42,64 +111,13 @@ pub struct Vnode { tree: RefCell, props: RefCell, - kind: VnodeKind, + // kind: VnodeKind, + data: VnodeData, flags: u32, target: RefCell>, fs: RefCell>>, - data: RefCell>>, -} - -/// Interface for "inode" of a real filesystem -pub trait VnodeImpl { - // Directory-only operations - /// Creates a new vnode, sets it up, attaches it (in real FS) to `at` with `name` and - /// returns it - fn create(&mut self, at: VnodeRef, name: &str, kind: VnodeKind) -> Result; - /// Removes the filesystem inode from its parent by erasing its directory entry - fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>; - /// Looks up a corresponding directory entry for `name`. If present, loads its inode from - /// storage medium and returns a new vnode associated with it. - fn lookup(&mut self, at: VnodeRef, name: &str) -> Result; - - /// Opens a vnode for access. Returns initial file position. - fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result; - /// Closes a vnode - fn close(&mut self, node: VnodeRef) -> Result<(), Errno>; - - /// Changes file's underlying storage size - fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno>; - /// Reads `data.len()` bytes into the buffer from file offset `pos` - fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result; - /// Writes `data.len()` bytes from the buffer to file offset `pos`. - /// Resizes the file storage if necessary. - fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result; - - /// Read directory entries into target buffer - fn readdir( - &mut self, - node: VnodeRef, - pos: usize, - data: &mut [DirectoryEntry], - ) -> Result; - - /// Retrieves file status - fn stat(&mut self, node: VnodeRef) -> Result; - - /// Reports the size of this filesystem object in bytes - fn size(&mut self, node: VnodeRef) -> Result; - - /// Returns `true` if node is ready for an operation - fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result; - - /// Performs filetype-specific request - fn ioctl( - &mut self, - node: VnodeRef, - cmd: IoctlCmd, - ptr: usize, - len: usize, - ) -> Result; + // data: RefCell>>, } impl Vnode { @@ -114,10 +132,10 @@ impl Vnode { /// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node /// then needs to have [Vnode::set_data()] called on it to be usable. - pub fn new(name: &str, kind: VnodeKind, flags: u32) -> VnodeRef { + pub fn new(name: &str, data: VnodeData, flags: u32) -> VnodeRef { Rc::new(Self { name: name.to_owned(), - kind, + data, flags, props: RefCell::new(VnodeProps { mode: FileMode::empty(), @@ -128,7 +146,6 @@ impl Vnode { }), target: RefCell::new(None), fs: RefCell::new(None), - data: RefCell::new(None), }) } @@ -147,19 +164,28 @@ impl Vnode { self.props.borrow() } - /// Sets an associated [VnodeImpl] for the [Vnode] - pub fn set_data(&self, data: Box) { - *self.data.borrow_mut() = Some(data); - } + // /// Sets an associated [VnodeImpl] for the [Vnode] + // pub fn set_data(&self, data: Box) { + // *self.data.borrow_mut() = Some(data); + // } /// Sets an associated [Filesystem] for the [Vnode] pub fn set_fs(&self, fs: Rc) { *self.fs.borrow_mut() = Some(fs); } - /// Returns a reference to the associated [VnodeImpl] - pub fn data(&self) -> RefMut>> { - self.data.borrow_mut() + pub fn as_directory(&self) -> Result<&RefCell>>, Errno> { + match &self.data { + VnodeData::Directory(data) => Ok(data), + _ => Err(Errno::NotADirectory), + } + } + + pub fn as_file(&self) -> Result<&RefCell>>, Errno> { + match &self.data { + VnodeData::File(data) => Ok(data), + _ => Err(Errno::IsADirectory), + } } /// Returns the associated [Fileystem] @@ -167,11 +193,6 @@ impl Vnode { self.fs.borrow().clone() } - /// Returns `true` if the vnode represents a directory - pub fn is_directory(&self) -> bool { - self.kind == VnodeKind::Directory - } - /// Returns `true` if the vnode allows arbitrary seeking pub fn is_seekable(&self) -> bool { self.flags & Self::SEEKABLE != 0 @@ -180,7 +201,7 @@ impl Vnode { /// Returns kind of the vnode #[inline(always)] pub const fn kind(&self) -> VnodeKind { - self.kind + core::mem::discriminant(&self.data) } /// Returns flags of the vnode @@ -220,12 +241,9 @@ impl Vnode { /// Attaches some filesystem's root directory node at another directory pub fn mount(self: &VnodeRef, root: VnodeRef) -> Result<(), Errno> { - if !self.is_directory() { - return Err(Errno::NotADirectory); - } - if !root.is_directory() { - return Err(Errno::NotADirectory); - } + let dir = self.as_directory()?; + let root_dir = root.as_directory()?; + if self.target.borrow().is_some() { return Err(Errno::Busy); } @@ -252,7 +270,7 @@ impl Vnode { /// Looks up a child `name` in in-memory tree cache pub fn lookup(self: &VnodeRef, name: &str) -> Option { - assert!(self.is_directory()); + // assert!(self.is_directory()); self.tree .borrow() .children @@ -267,7 +285,8 @@ impl Vnode { limit: usize, mut f: F, ) -> usize { - assert!(self.is_directory()); + // TODO + // assert!(self.is_directory()); let mut count = 0; for (index, item) in self .tree @@ -287,10 +306,12 @@ impl Vnode { /// Looks up a child `name` in `self`. Will first try looking up a cached /// vnode and will load it from disk if it's missing. pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result { + let dir = self.as_directory()?; + if let Some(node) = self.lookup(name) { Ok(node) - } else if let Some(ref mut data) = *self.data() { - let vnode = data.lookup(self.clone(), name)?; + } else if let Some(ref mut dir) = *dir.borrow_mut() { + let vnode = dir.lookup(self.clone(), name)?; if let Some(fs) = self.fs() { vnode.set_fs(fs); } @@ -306,11 +327,10 @@ impl Vnode { self: &VnodeRef, name: &str, mode: FileMode, - kind: VnodeKind, + kind: VnodeCreateKind, ) -> Result { - if self.kind != VnodeKind::Directory { - return Err(Errno::NotADirectory); - } + let dir = self.as_directory()?; + if name.contains('/') { return Err(Errno::InvalidArgument); } @@ -321,8 +341,8 @@ impl Vnode { e => return e, }; - if let Some(ref mut data) = *self.data() { - let vnode = data.create(self.clone(), name, kind)?; + if let Some(ref mut dir) = *dir.borrow_mut() { + let vnode = dir.create(self.clone(), name, kind)?; if let Some(fs) = self.fs() { vnode.set_fs(fs); } @@ -336,16 +356,14 @@ impl Vnode { /// Removes a directory entry `name` from `self` pub fn unlink(self: &VnodeRef, name: &str) -> Result<(), Errno> { - if self.kind != VnodeKind::Directory { - return Err(Errno::NotADirectory); - } + let dir = self.as_directory()?; if name.contains('/') { return Err(Errno::InvalidArgument); } - if let Some(ref mut data) = *self.data() { + if let Some(ref mut dir) = *dir.borrow_mut() { let vnode = self.lookup(name).ok_or(Errno::DoesNotExist)?; - data.remove(self.clone(), name)?; + dir.remove(self.clone(), name)?; vnode.detach(); Ok(()) } else { @@ -356,93 +374,152 @@ impl Vnode { /// Opens a vnode for access pub fn open(self: &VnodeRef, flags: OpenFlags) -> Result { let mut open_flags = 0; - if flags.contains(OpenFlags::O_DIRECTORY) { - if self.kind != VnodeKind::Directory { - return Err(Errno::NotADirectory); - } - if flags & OpenFlags::O_ACCESS != OpenFlags::O_RDONLY { - return Err(Errno::IsADirectory); - } - - open_flags = File::READ; - } else { - if self.kind == VnodeKind::Directory { - return Err(Errno::IsADirectory); - } - - match flags & OpenFlags::O_ACCESS { - OpenFlags::O_RDONLY => open_flags |= File::READ, - OpenFlags::O_WRONLY => open_flags |= File::WRITE, - OpenFlags::O_RDWR => open_flags |= File::READ | File::WRITE, - _ => unimplemented!(), - } - } if flags.contains(OpenFlags::O_CLOEXEC) { open_flags |= File::CLOEXEC; } - if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 { - Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags)) - } else if let Some(ref mut data) = *self.data() { - let pos = data.open(self.clone(), flags)?; - Ok(File::normal(self.clone(), pos, open_flags)) - } else { - Err(Errno::NotImplemented) + match flags & OpenFlags::O_ACCESS { + OpenFlags::O_RDONLY => open_flags |= File::READ, + OpenFlags::O_WRONLY => open_flags |= File::WRITE, + OpenFlags::O_RDWR => open_flags |= File::READ | File::WRITE, + _ => unimplemented!(), } + + match &self.data { + VnodeData::Directory(dir) => { + if !flags.contains(OpenFlags::O_DIRECTORY) { + return Err(Errno::NotADirectory); + } + + if self.flags & Vnode::CACHE_READDIR != 0 { + Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags)) + } else { + todo!() + } + }, + VnodeData::File(file) => { + if flags.contains(OpenFlags::O_DIRECTORY) { + return Err(Errno::IsADirectory); + } + + if let Some(ref mut file) = *file.borrow_mut() { + let pos = file.open(self.clone(), flags)?; + Ok(File::normal(self.clone(), pos, open_flags)) + } else { + Err(Errno::NotImplemented) + } + }, + VnodeData::Char(chr) => { + if flags.contains(OpenFlags::O_DIRECTORY) { + return Err(Errno::IsADirectory); + } + + Ok(File::normal(self.clone(), 0, open_flags)) + // if let Some(ref mut chr) = *chr.borrow_mut() { + // let pos = file.open(self.clone(), flags)?; + // Ok(File::normal(self.clone(), pos, open_flags)) + // } else { + // Err(Errno::NotImplemented) + // } + } + _ => todo!() + } + + // if flags.contains(OpenFlags::O_DIRECTORY) { + // let dir = self.as_directory()?; + + // todo!() + // // if self.kind != VnodeKind::Directory { + // // return Err(Errno::NotADirectory); + // // } + // // if flags & OpenFlags::O_ACCESS != OpenFlags::O_RDONLY { + // // return Err(Errno::IsADirectory); + // // } + + // // open_flags = File::READ; + // } else { + // let file = self.as_file()?; + + // } + + // if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 { + // Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags)) + // } else if let Some(ref mut data) = *self.data() { + // } else { + // } } /// Closes a vnode pub fn close(self: &VnodeRef) -> Result<(), Errno> { - if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 { - Ok(()) - } else if let Some(ref mut data) = *self.data() { - data.close(self.clone()) - } else { - Err(Errno::NotImplemented) + match &self.data { + VnodeData::Directory(dir) => { + if let Some(ref mut dir) = *dir.borrow_mut() { + return dir.close(self.clone()); + } + }, + VnodeData::File(file) => { + if let Some(ref mut file) = *file.borrow_mut() { + return file.close(self.clone()); + } + }, + _ => {} } + + Err(Errno::NotImplemented) } /// Reads data from offset `pos` into `buf` pub fn read(self: &VnodeRef, pos: usize, buf: &mut [u8]) -> Result { - if self.kind == VnodeKind::Directory { - Err(Errno::IsADirectory) - } else if let Some(ref mut data) = *self.data() { - data.read(self.clone(), pos, buf) - } else { - Err(Errno::NotImplemented) + match &self.data { + VnodeData::File(file) => { + file.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.read(self.clone(), pos, buf) + } + VnodeData::Char(chr) => { + chr.read(true, buf) + } + _ => todo!() } } /// Writes data from `buf` to offset `pos` pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result { - if self.kind == VnodeKind::Directory { - Err(Errno::IsADirectory) - } else if let Some(ref mut data) = *self.data() { - data.write(self.clone(), pos, buf) - } else { - Err(Errno::NotImplemented) + match &self.data { + VnodeData::File(file) => { + file.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.write(self.clone(), pos, buf) + } + VnodeData::Char(chr) => { + chr.write(true, buf) + } + _ => todo!() } } /// Resizes the vnode data pub fn truncate(self: &VnodeRef, size: usize) -> Result<(), Errno> { - if self.kind != VnodeKind::Regular { - Err(Errno::IsADirectory) - } else if let Some(ref mut data) = *self.data() { - data.truncate(self.clone(), size) - } else { - Err(Errno::NotImplemented) - } + todo!() + // if self.kind != VnodeKind::Regular { + // Err(Errno::IsADirectory) + // } else if let Some(ref mut data) = *self.data() { + // data.truncate(self.clone(), size) + // } else { + // Err(Errno::NotImplemented) + // } } /// Returns current vnode data size pub fn size(self: &VnodeRef) -> Result { - if let Some(ref mut data) = *self.data() { - data.size(self.clone()) - } else { - Err(Errno::NotImplemented) + match &self.data { + VnodeData::File(file) => { + file.borrow_mut().as_mut().ok_or(Errno::NotADirectory)?.size(self.clone()) + }, + _ => todo!() } + // if let Some(ref mut data) = *self.data() { + // data.size(self.clone()) + // } else { + // Err(Errno::NotImplemented) + // } } /// Reports file status @@ -454,29 +531,42 @@ impl Vnode { size: 0, mode: props.mode, }) - } else if let Some(ref mut data) = *self.data() { - data.stat(self.clone()) } else { - Err(Errno::NotImplemented) + match &self.data { + VnodeData::File(file) => file.borrow_mut().as_mut().ok_or(Errno::NotADirectory)?.stat(self.clone()), + VnodeData::Directory(dir) => dir.borrow_mut().as_mut().ok_or(Errno::NotImplemented)?.stat(self.clone()), + _ => todo!() + } } } /// Performs node-specific requests pub fn ioctl(self: &VnodeRef, cmd: IoctlCmd, ptr: usize, len: usize) -> Result { - if let Some(ref mut data) = *self.data() { - data.ioctl(self.clone(), cmd, ptr, len) - } else { - Err(Errno::NotImplemented) + match &self.data { + VnodeData::File(file) => { + todo!() + // file.borrow_mut().as_mut().ok_or(Errno::NotADirectory)?.size(self.clone()) + }, + VnodeData::Char(chr) => { + chr.ioctl(cmd, ptr, len) + }, + _ => todo!() } + // if let Some(ref mut data) = *self.data() { + // data.ioctl(self.clone(), cmd, ptr, len) + // } else { + // Err(Errno::NotImplemented) + // } } /// Returns `true` if the node is ready for operation pub fn is_ready(self: &VnodeRef, write: bool) -> Result { - if let Some(ref mut data) = *self.data() { - data.is_ready(self.clone(), write) - } else { - Err(Errno::NotImplemented) - } + todo!() + // if let Some(ref mut data) = *self.data() { + // data.is_ready(self.clone(), write) + // } else { + // Err(Errno::NotImplemented) + // } } /// Checks if given [Ioctx] has `access` permissions to the vnode @@ -525,32 +615,81 @@ mod tests { use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat}; pub struct DummyInode; - #[auto_inode] - impl VnodeImpl for DummyInode { - fn create( + // TODO derive macro for this + impl VnodeCommon for DummyInode { + /// Performs filetype-specific request + fn ioctl( &mut self, - _at: VnodeRef, - name: &str, - kind: VnodeKind, - ) -> Result { - let node = Vnode::new(name, kind, 0); - node.set_data(Box::new(DummyInode {})); - Ok(node) + node: VnodeRef, + cmd: IoctlCmd, + ptr: usize, + len: usize, + ) -> Result { + todo!() } - fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> { + /// Retrieves file status + fn stat(&mut self, node: VnodeRef) -> Result { + todo!() + } + + /// Reports the size of this filesystem object in bytes + fn size(&mut self, node: VnodeRef) -> Result { + todo!() + } + + /// Returns `true` if node is ready for an operation + fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result { + todo!() + } + + fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result { + Ok(0) + } + + fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { Ok(()) } - fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { + } + + impl VnodeDirectory for DummyInode { + fn create( + &mut self, + at: VnodeRef, + name: &str, + kind: VnodeCreateKind, + ) -> Result { + let data = match kind { + VnodeCreateKind::Directory => { + VnodeData::Directory(RefCell::new(Some(Box::new(DummyInode {})))) + } + _ => todo!(), + }; + Ok(Vnode::new(name, data, 0)) + } + fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno> { + Ok(()) + } + fn lookup(&mut self, at: VnodeRef, name: &str) -> Result { Err(Errno::DoesNotExist) } + + /// Read directory entries into target buffer + fn readdir( + &mut self, + node: VnodeRef, + pos: usize, + data: &mut [DirectoryEntry], + ) -> Result { + todo!() + } } #[test] fn test_parent() { - let root = Vnode::new("", VnodeKind::Directory, 0); - let node = Vnode::new("dir0", VnodeKind::Directory, 0); + let root = Vnode::new("", VnodeData::Directory(RefCell::new(None)), 0); + let node = Vnode::new("dir0", VnodeData::Directory(RefCell::new(None)), 0); root.attach(node.clone()); @@ -560,23 +699,27 @@ mod tests { #[test] fn test_mkdir_unlink() { - let root = Vnode::new("", VnodeKind::Directory, 0); - - root.set_data(Box::new(DummyInode {})); + let root = Vnode::new( + "", + VnodeData::Directory(RefCell::new(Some(Box::new(DummyInode {})))), + 0, + ); let node = root - .create("test", FileMode::default_dir(), VnodeKind::Directory) + .create("test", FileMode::default_dir(), VnodeCreateKind::Directory) .unwrap(); assert_eq!( - root.create("test", FileMode::default_dir(), VnodeKind::Directory) + root.create("test", FileMode::default_dir(), VnodeCreateKind::Directory) .unwrap_err(), Errno::AlreadyExists ); assert_eq!(node.props.borrow().mode, FileMode::default_dir()); assert!(Rc::ptr_eq(&node, &root.lookup("test").unwrap())); - assert!(node.data.borrow().is_some()); + let inner = node.as_directory().unwrap(); + assert!(matches!(*inner.borrow_mut(), Some(_))); + // assert!(node.data.borrow().is_some()); root.unlink("test").unwrap(); @@ -585,9 +728,9 @@ mod tests { #[test] fn test_lookup_attach_detach() { - let root = Vnode::new("", VnodeKind::Directory, 0); - let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0); - let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0); + let root = Vnode::new("", VnodeData::Directory(RefCell::new(None)), 0); + let dir0 = Vnode::new("dir0", VnodeData::Directory(RefCell::new(None)), 0); + let dir1 = Vnode::new("dir1", VnodeData::Directory(RefCell::new(None)), 0); root.attach(dir0.clone()); root.attach(dir1.clone()); diff --git a/kernel/src/fs/devfs.rs b/kernel/src/fs/devfs.rs index 9bc7cc1..1198a7c 100644 --- a/kernel/src/fs/devfs.rs +++ b/kernel/src/fs/devfs.rs @@ -3,7 +3,8 @@ use crate::util::InitOnce; use alloc::boxed::Box; use core::sync::atomic::{AtomicUsize, Ordering}; use libsys::{stat::FileMode, error::Errno}; -use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef}; +use vfs::{CharDevice, Vnode, VnodeData, VnodeRef}; +use core::cell::RefCell; /// Possible character device kinds #[derive(Debug)] @@ -16,7 +17,8 @@ static DEVFS_ROOT: InitOnce = InitOnce::new(); /// Initializes devfs pub fn init() { - let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT); + let node = Vnode::new("", VnodeData::Directory(RefCell::new(None)), Vnode::CACHE_READDIR | Vnode::CACHE_STAT); + // let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT); node.props_mut().mode = FileMode::default_dir(); DEVFS_ROOT.init(node); } @@ -29,9 +31,10 @@ pub fn root() -> &'static VnodeRef { pub fn add_named_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> { infoln!("Add char device: {}", name); - let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT); - node.props_mut().mode = FileMode::from_bits(0o600).unwrap() | FileMode::S_IFCHR; - node.set_data(Box::new(CharDeviceWrapper::new(dev))); + let node = Vnode::new(name, VnodeData::Char(dev), Vnode::CACHE_STAT); + // let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT); + // node.set_data(Box::new(CharDeviceWrapper::new(dev))); + node.props_mut().mode = FileMode::from_bits(0o600).unwrap() | FileMode::S_IFCHR; DEVFS_ROOT.get().attach(node); diff --git a/kernel/src/fs/sysfs.rs b/kernel/src/fs/sysfs.rs index 0d144fa..10c286f 100644 --- a/kernel/src/fs/sysfs.rs +++ b/kernel/src/fs/sysfs.rs @@ -1,27 +1,26 @@ +use crate::debug::{self, Level}; use crate::util::InitOnce; use alloc::boxed::Box; +use core::cell::RefCell; +use core::fmt::{self, Write}; +use core::str::FromStr; use core::sync::atomic::{AtomicUsize, Ordering}; use fs_macros::auto_inode; use libsys::{ error::Errno, stat::{FileMode, OpenFlags, Stat}, + ioctl::IoctlCmd, }; -use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef}; -use core::fmt::{self, Write}; -use core::str::FromStr; -use crate::debug::{self, Level}; +use vfs::{CharDevice, Vnode, VnodeCommon, VnodeData, VnodeFile, VnodeRef}; -struct NodeData< - R: Fn(&mut [u8]) -> Result, - W: Fn(&[u8]) -> Result, -> { +struct NodeData Result, W: Fn(&[u8]) -> Result> { read_func: R, write_func: W, } struct BufferWriter<'a> { dst: &'a mut [u8], - pos: usize + pos: usize, } impl<'a> fmt::Write for BufferWriter<'a> { @@ -47,11 +46,8 @@ impl<'a> BufferWriter<'a> { } } -#[auto_inode] -impl< - R: Fn(&mut [u8]) -> Result, - W: Fn(&[u8]) -> Result, - > VnodeImpl for NodeData +impl Result, W: Fn(&[u8]) -> Result> VnodeCommon + for NodeData { fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result { Ok(0) @@ -60,7 +56,35 @@ impl< fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { Ok(()) } + /// Performs filetype-specific request + fn ioctl( + &mut self, + node: VnodeRef, + cmd: IoctlCmd, + ptr: usize, + len: usize, + ) -> Result { + todo!() + } + /// Retrieves file status + fn stat(&mut self, node: VnodeRef) -> Result { + todo!() + } + + /// Reports the size of this filesystem object in bytes + fn size(&mut self, node: VnodeRef) -> Result { + todo!() + } + + /// Returns `true` if node is ready for an operation + fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result { + todo!() + } +} + +impl Result, W: Fn(&[u8]) -> Result> VnodeFile + for NodeData { fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { if pos != 0 { // TODO handle this @@ -76,12 +100,13 @@ impl< } (self.write_func)(data) } -} -impl< - R: Fn(&mut [u8]) -> Result, - W: Fn(&[u8]) -> Result, - > NodeData + fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno> { + todo!() + } + } +impl Result, W: Fn(&[u8]) -> Result> + NodeData { pub const fn new(read_func: R, write_func: W) -> Self { Self { @@ -100,9 +125,12 @@ where R: Fn(&mut [u8]) -> Result + 'static, W: Fn(&[u8]) -> Result + 'static, { - let node = Vnode::new(name, VnodeKind::Regular, Vnode::CACHE_STAT); + let node = Vnode::new( + name, + VnodeData::File(RefCell::new(Some(Box::new(NodeData::new(read, write))))), + Vnode::CACHE_STAT, + ); node.props_mut().mode = mode | FileMode::S_IFREG; - node.set_data(Box::new(NodeData::new(read, write))); if let Some(parent) = parent { parent.attach(node); @@ -116,15 +144,30 @@ where R: Fn(&mut [u8]) -> Result + 'static, W: Fn(&[u8]) -> Result + 'static, { - add_generic_node(parent, name, FileMode::from_bits(0o600).unwrap(), read, write) + add_generic_node( + parent, + name, + FileMode::from_bits(0o600).unwrap(), + read, + write, + ) } -pub fn add_read_node(parent: Option, name: &str, read: R) where R: Fn(&mut [u8]) -> Result + 'static { - add_generic_node(parent, name, FileMode::from_bits(0o400).unwrap(), read, |_| Err(Errno::ReadOnly)) +pub fn add_read_node(parent: Option, name: &str, read: R) +where + R: Fn(&mut [u8]) -> Result + 'static, +{ + add_generic_node( + parent, + name, + FileMode::from_bits(0o400).unwrap(), + read, + |_| Err(Errno::ReadOnly), + ) } pub fn add_directory(parent: Option, name: &str) -> Result { - let node = Vnode::new(name, VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT); + let node = Vnode::new(name, VnodeData::Directory(RefCell::new(None)), Vnode::CACHE_READDIR | Vnode::CACHE_STAT); node.props_mut().mode = FileMode::from_bits(0o500).unwrap() | FileMode::S_IFDIR; if let Some(parent) = parent { @@ -141,7 +184,7 @@ pub fn root() -> &'static VnodeRef { } pub fn init() { - let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT); + let node = Vnode::new("", VnodeData::Directory(RefCell::new(None)), Vnode::CACHE_READDIR | Vnode::CACHE_STAT); node.props_mut().mode = FileMode::default_dir(); SYSFS_ROOT.init(node); diff --git a/kernel/src/proc/io.rs b/kernel/src/proc/io.rs index c7ff1f9..275a130 100644 --- a/kernel/src/proc/io.rs +++ b/kernel/src/proc/io.rs @@ -1,7 +1,7 @@ //! Process file descriptors and I/O context use alloc::collections::BTreeMap; use libsys::{error::Errno, stat::{FileDescriptor, UserId, GroupId}}; -use vfs::{FileRef, Ioctx, VnodeRef, VnodeKind}; +use vfs::{FileRef, Ioctx, VnodeRef, VnodeData}; /// Process I/O context. Contains file tables, root/cwd info etc. pub struct ProcessIo { @@ -24,7 +24,7 @@ impl ProcessIo { /// Sets controlling terminal for the process pub fn set_ctty(&mut self, node: VnodeRef) { - assert_eq!(node.kind(), VnodeKind::Char); + // assert_eq!(node.kind(), VnodeKind::Char); self.ctty = Some(node); } diff --git a/kernel/src/util.rs b/kernel/src/util.rs index 18bae59..4a68a9f 100644 --- a/kernel/src/util.rs +++ b/kernel/src/util.rs @@ -29,8 +29,9 @@ impl InitOnce { /// Returns the initialized value. Will panic if the value has not /// yet been initialized. #[allow(clippy::mut_from_ref)] + #[track_caller] pub fn get(&self) -> &mut T { - assert!(self.is_initialized(), "Access to uninitialized InitOnce"); + assert!(self.is_initialized(), "Access to uninitialized InitOnce: {:?}", core::panic::Location::caller()); unsafe { (*self.inner.get()).assume_init_mut() } }