refactor: new VFS design

This commit is contained in:
2022-01-24 20:46:45 +02:00
parent a82751c146
commit 01da9e7ee5
13 changed files with 858 additions and 576 deletions
+56 -12
View File
@@ -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<A: BlockAllocator + Copy + 'static> {
alloc: A,
}
#[auto_inode]
impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
impl<A: BlockAllocator + Copy + 'static> VnodeDirectory for DirInode<A> {
fn create(
&mut self,
_parent: VnodeRef,
name: &str,
kind: VnodeKind,
kind: VnodeCreateKind,
) -> Result<VnodeRef, Errno> {
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<VnodeRef, Errno> {
@@ -32,6 +35,18 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
Ok(())
}
/// Read directory entries into target buffer
fn readdir(
&mut self,
node: VnodeRef,
pos: usize,
data: &mut [DirectoryEntry],
) -> Result<usize, Errno> {
todo!()
}
}
impl<A: BlockAllocator + Copy + 'static> VnodeCommon for DirInode<A> {
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
let props = node.props();
Ok(Stat {
@@ -40,6 +55,35 @@ impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
mode: props.mode,
})
}
/// Performs filetype-specific request
fn ioctl(
&mut self,
node: VnodeRef,
cmd: IoctlCmd,
ptr: usize,
len: usize,
) -> Result<usize, Errno> {
todo!()
}
/// Reports the size of this filesystem object in bytes
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
/// Returns `true` if node is ready for an operation
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno> {
todo!()
}
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
Ok(0)
}
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
Ok(())
}
}
impl<A: BlockAllocator + Copy + 'static> DirInode<A> {
+35 -16
View File
@@ -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<usize, Errno> {
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<usize, Errno> {
self.data.read(pos, data)
}
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
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<usize, Errno> {
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<usize, Errno> {
todo!()
}
/// Returns `true` if node is ready for an operation
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno> {
todo!()
}
}
impl<'a, A: BlockAllocator + Copy + 'static> VnodeFile for FileInode<'a, A> {
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
self.data.read(pos, data)
}
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
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> {
+29 -32
View File
@@ -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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
}
fn create_node_initial(self: Rc<Self>, 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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
}
unsafe fn load_tar(self: Rc<Self>, base: *const u8, size: usize) -> Result<VnodeRef, Errno> {
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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
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");
}
+7 -7
View File
@@ -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
+48 -48
View File
@@ -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<bool, Errno>;
}
/// 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<usize, Errno> {
Ok(0)
}
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
Ok(())
}
fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
self.device.read(true, data)
}
fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result<usize, Errno> {
self.device.write(true, data)
}
fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result<bool, Errno> {
self.device.is_ready(write)
}
fn ioctl(
&mut self,
_node: VnodeRef,
cmd: IoctlCmd,
ptr: usize,
len: usize,
) -> Result<usize, Errno> {
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<usize, Errno> {
// Ok(0)
// }
//
// fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
// Ok(())
// }
//
// fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
// self.device.read(true, data)
// }
//
// fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result<usize, Errno> {
// self.device.write(true, data)
// }
//
// fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result<bool, Errno> {
// self.device.is_ready(write)
// }
//
// fn ioctl(
// &mut self,
// _node: VnodeRef,
// cmd: IoctlCmd,
// ptr: usize,
// len: usize,
// ) -> Result<usize, Errno> {
// 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 }
// }
// }
+52 -23
View File
@@ -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<usize, Errno> {
fn cache_readdir(
inner: &mut NormalFile,
entries: &mut [DirectoryEntry],
) -> Result<usize, Errno> {
let mut count = entries.len();
let mut offset = 0usize;
@@ -177,7 +182,7 @@ impl File {
pub fn readdir(&mut self, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
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<VnodeRef, Errno> {
let node = Vnode::new(name, kind, 0);
node.set_data(Box::new(DummyInode {}));
Ok(node)
node: VnodeRef,
cmd: IoctlCmd,
ptr: usize,
len: usize,
) -> Result<usize, Errno> {
todo!()
}
/// Retrieves file status
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
todo!()
}
/// Reports the size of this filesystem object in bytes
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
/// Returns `true` if node is ready for an operation
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno> {
todo!()
}
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
@@ -232,6 +251,9 @@ mod tests {
Ok(())
}
}
impl VnodeFile for DummyInode {
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
#[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<usize, Errno> {
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];
+209 -206
View File
@@ -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<VnodeRef, Errno> {
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<VnodeRef, Errno> {
let vnode = Vnode::new(name, kind, 0);
vnode.set_data(Box::new(DummyInode {}));
Ok(vnode)
}
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
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<VnodeRef, Errno> {
// let vnode = Vnode::new(name, kind, 0);
// vnode.set_data(Box::new(DummyInode {}));
// Ok(vnode)
// }
//
// fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
// 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()
// ));
// }
// }
+3 -3
View File
@@ -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;
+338 -195
View File
@@ -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<Vnode>]
pub type VnodeRef = Rc<Vnode>;
pub type VnodeKind = Discriminant<VnodeData>;
/// 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<usize, Errno>;
/// Retrieves file status
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno>;
/// Reports the size of this filesystem object in bytes
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno>;
/// Returns `true` if node is ready for an operation
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno>;
/// Opens a vnode for access. Returns initial file position.
fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result<usize, Errno>;
/// 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<usize, Errno>;
/// 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<usize, Errno>;
}
pub trait VnodeDirectory: VnodeCommon {
fn create(
&mut self,
at: VnodeRef,
name: &str,
kind: VnodeCreateKind,
) -> Result<VnodeRef, Errno>;
fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>;
fn lookup(&mut self, at: VnodeRef, name: &str) -> Result<VnodeRef, Errno>;
/// Read directory entries into target buffer
fn readdir(
&mut self,
node: VnodeRef,
pos: usize,
data: &mut [DirectoryEntry],
) -> Result<usize, Errno>;
}
// /// 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<VnodeRef>,
children: Vec<VnodeRef>,
@@ -35,6 +92,18 @@ pub struct VnodeProps {
pub mode: FileMode,
}
pub enum VnodeData {
Directory(RefCell<Option<Box<dyn VnodeDirectory>>>),
File(RefCell<Option<Box<dyn VnodeFile>>>),
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<TreeNode>,
props: RefCell<VnodeProps>,
kind: VnodeKind,
// kind: VnodeKind,
data: VnodeData,
flags: u32,
target: RefCell<Option<VnodeRef>>,
fs: RefCell<Option<Rc<dyn Filesystem>>>,
data: RefCell<Option<Box<dyn VnodeImpl>>>,
}
/// 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<VnodeRef, Errno>;
/// 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<VnodeRef, Errno>;
/// Opens a vnode for access. Returns initial file position.
fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result<usize, Errno>;
/// 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<usize, Errno>;
/// 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<usize, Errno>;
/// Read directory entries into target buffer
fn readdir(
&mut self,
node: VnodeRef,
pos: usize,
data: &mut [DirectoryEntry],
) -> Result<usize, Errno>;
/// Retrieves file status
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno>;
/// Reports the size of this filesystem object in bytes
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno>;
/// Returns `true` if node is ready for an operation
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno>;
/// Performs filetype-specific request
fn ioctl(
&mut self,
node: VnodeRef,
cmd: IoctlCmd,
ptr: usize,
len: usize,
) -> Result<usize, Errno>;
// data: RefCell<Option<Box<dyn VnodeImpl>>>,
}
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<dyn VnodeImpl>) {
*self.data.borrow_mut() = Some(data);
}
// /// Sets an associated [VnodeImpl] for the [Vnode]
// pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
// *self.data.borrow_mut() = Some(data);
// }
/// Sets an associated [Filesystem] for the [Vnode]
pub fn set_fs(&self, fs: Rc<dyn Filesystem>) {
*self.fs.borrow_mut() = Some(fs);
}
/// Returns a reference to the associated [VnodeImpl]
pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
self.data.borrow_mut()
pub fn as_directory(&self) -> Result<&RefCell<Option<Box<dyn VnodeDirectory>>>, Errno> {
match &self.data {
VnodeData::Directory(data) => Ok(data),
_ => Err(Errno::NotADirectory),
}
}
pub fn as_file(&self) -> Result<&RefCell<Option<Box<dyn VnodeFile>>>, 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<VnodeRef> {
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<VnodeRef, Errno> {
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<VnodeRef, Errno> {
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<FileRef, Errno> {
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<usize, Errno> {
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<usize, Errno> {
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<usize, Errno> {
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<usize, Errno> {
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<bool, Errno> {
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<VnodeRef, Errno> {
let node = Vnode::new(name, kind, 0);
node.set_data(Box::new(DummyInode {}));
Ok(node)
node: VnodeRef,
cmd: IoctlCmd,
ptr: usize,
len: usize,
) -> Result<usize, Errno> {
todo!()
}
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
/// Retrieves file status
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
todo!()
}
/// Reports the size of this filesystem object in bytes
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
/// Returns `true` if node is ready for an operation
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno> {
todo!()
}
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
Ok(0)
}
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
Ok(())
}
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
}
impl VnodeDirectory for DummyInode {
fn create(
&mut self,
at: VnodeRef,
name: &str,
kind: VnodeCreateKind,
) -> Result<VnodeRef, Errno> {
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<VnodeRef, Errno> {
Err(Errno::DoesNotExist)
}
/// Read directory entries into target buffer
fn readdir(
&mut self,
node: VnodeRef,
pos: usize,
data: &mut [DirectoryEntry],
) -> Result<usize, Errno> {
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());
+8 -5
View File
@@ -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<VnodeRef> = 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);
+69 -26
View File
@@ -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<usize, Errno>,
W: Fn(&[u8]) -> Result<usize, Errno>,
> {
struct NodeData<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>> {
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<usize, Errno>,
W: Fn(&[u8]) -> Result<usize, Errno>,
> VnodeImpl for NodeData<R, W>
impl<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>> VnodeCommon
for NodeData<R, W>
{
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
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<usize, Errno> {
todo!()
}
/// Retrieves file status
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
todo!()
}
/// Reports the size of this filesystem object in bytes
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
/// Returns `true` if node is ready for an operation
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno> {
todo!()
}
}
impl<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>> VnodeFile
for NodeData<R, W> {
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
if pos != 0 {
// TODO handle this
@@ -76,12 +100,13 @@ impl<
}
(self.write_func)(data)
}
}
impl<
R: Fn(&mut [u8]) -> Result<usize, Errno>,
W: Fn(&[u8]) -> Result<usize, Errno>,
> NodeData<R, W>
fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno> {
todo!()
}
}
impl<R: Fn(&mut [u8]) -> Result<usize, Errno>, W: Fn(&[u8]) -> Result<usize, Errno>>
NodeData<R, W>
{
pub const fn new(read_func: R, write_func: W) -> Self {
Self {
@@ -100,9 +125,12 @@ where
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
W: Fn(&[u8]) -> Result<usize, Errno> + '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<usize, Errno> + 'static,
W: Fn(&[u8]) -> Result<usize, Errno> + '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<R>(parent: Option<VnodeRef>, name: &str, read: R) where R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static {
add_generic_node(parent, name, FileMode::from_bits(0o400).unwrap(), read, |_| Err(Errno::ReadOnly))
pub fn add_read_node<R>(parent: Option<VnodeRef>, name: &str, read: R)
where
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
{
add_generic_node(
parent,
name,
FileMode::from_bits(0o400).unwrap(),
read,
|_| Err(Errno::ReadOnly),
)
}
pub fn add_directory(parent: Option<VnodeRef>, name: &str) -> Result<VnodeRef, Errno> {
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);
+2 -2
View File
@@ -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);
}
+2 -1
View File
@@ -29,8 +29,9 @@ impl<T> InitOnce<T> {
/// 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<T>");
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>: {:?}", core::panic::Location::caller());
unsafe { (*self.inner.get()).assume_init_mut() }
}