fs: integrate the new vfs with the kernel
This commit is contained in:
parent
fbe05c1d4d
commit
e431d49ffb
@ -8,7 +8,9 @@ edition = "2021"
|
||||
[dependencies]
|
||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||
vfs = { path = "../vfs" }
|
||||
|
||||
static_assertions = "1.1.0"
|
||||
log = "0.4.20"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -1,76 +1,35 @@
|
||||
use core::{any::Any, cell::RefCell, marker::PhantomData};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Node, NodeFlags, NodeRef};
|
||||
use yggdrasil_abi::{error::Error, io::FileType};
|
||||
|
||||
use vfs::{CreateInfo, Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{FileAttr, FileMode, FileType, OpenOptions},
|
||||
};
|
||||
|
||||
use crate::{block::BlockAllocator, bvec::BVec, file::FileNode};
|
||||
use crate::{block::BlockAllocator, file::FileNode};
|
||||
|
||||
pub(crate) struct DirectoryNode<A: BlockAllocator> {
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
mode: FileMode,
|
||||
_pd: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> VnodeImpl for DirectoryNode<A> {
|
||||
fn create(&self, at: &VnodeRef, info: &CreateInfo) -> Result<VnodeRef, Error> {
|
||||
let child = Vnode::new(info.name, info.kind);
|
||||
match info.kind {
|
||||
VnodeKind::Directory => child.set_data(Box::new(Self {
|
||||
uid: info.uid,
|
||||
gid: info.gid,
|
||||
mode: info.mode,
|
||||
_pd: PhantomData,
|
||||
})),
|
||||
VnodeKind::Regular => child.set_data(Box::new(FileNode {
|
||||
data: RefCell::new(BVec::<A>::new()),
|
||||
uid: info.uid,
|
||||
gid: info.gid,
|
||||
mode: info.mode,
|
||||
})),
|
||||
impl<A: BlockAllocator> DirectoryNode<A> {
|
||||
pub fn new() -> NodeRef {
|
||||
Node::directory(
|
||||
Self { _pd: PhantomData },
|
||||
NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> CommonImpl for DirectoryNode<A> {}
|
||||
|
||||
impl<A: BlockAllocator> DirectoryImpl for DirectoryNode<A> {
|
||||
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
|
||||
Ok(DirectoryOpenPosition::FromCache)
|
||||
}
|
||||
|
||||
fn create_node(&self, _parent: &NodeRef, ty: FileType) -> Result<NodeRef, Error> {
|
||||
match ty {
|
||||
FileType::File => Ok(FileNode::<A>::new()),
|
||||
FileType::Directory => Ok(DirectoryNode::<A>::new()),
|
||||
_ => todo!(),
|
||||
}
|
||||
child.set_fs(at.fs().unwrap());
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
fn open(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
Ok((DIR_POSITION_FROM_CACHE, None))
|
||||
}
|
||||
|
||||
fn remove(&self, _at: &VnodeRef, _name: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
Ok(node.children().len() as u64)
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: 0,
|
||||
mode: self.mode,
|
||||
ty: FileType::Directory,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> DirectoryNode<A> {
|
||||
pub fn new(uid: u32, gid: u32, mode: FileMode) -> Self {
|
||||
Self {
|
||||
uid,
|
||||
gid,
|
||||
mode,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,44 @@
|
||||
use core::{any::Any, cell::RefCell};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use vfs::{VnodeImpl, VnodeRef};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{FileAttr, FileMode, FileType, OpenOptions},
|
||||
};
|
||||
use vfs::{CommonImpl, Node, NodeFlags, NodeRef, RegularImpl};
|
||||
use yggdrasil_abi::{error::Error, io::OpenOptions};
|
||||
|
||||
use crate::{block::BlockAllocator, bvec::BVec};
|
||||
|
||||
pub(crate) struct FileNode<A: BlockAllocator> {
|
||||
// TODO IrqSafeSpinlock
|
||||
pub(crate) data: RefCell<BVec<'static, A>>,
|
||||
pub(crate) uid: u32,
|
||||
pub(crate) gid: u32,
|
||||
pub(crate) mode: FileMode,
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> VnodeImpl for FileNode<A> {
|
||||
impl<A: BlockAllocator> FileNode<A> {
|
||||
pub fn new() -> NodeRef {
|
||||
Node::regular(
|
||||
Self {
|
||||
data: RefCell::new(BVec::new()),
|
||||
},
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> CommonImpl for FileNode<A> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
Ok(self.data.borrow().size() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> RegularImpl for FileNode<A> {
|
||||
fn open(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_node: &NodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
// TODO provide APPEND by vfs driver instead
|
||||
if opts.contains(OpenOptions::APPEND) {
|
||||
Ok((self.data.borrow().size() as u64, None))
|
||||
} else {
|
||||
@ -29,33 +46,27 @@ impl<A: BlockAllocator> VnodeImpl for FileNode<A> {
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&self, _node: &VnodeRef) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_node: &NodeRef,
|
||||
_instance: Option<&Box<dyn Any>>,
|
||||
pos: u64,
|
||||
_inner: Option<&mut Box<dyn Any>>,
|
||||
data: &mut [u8],
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
self.data.borrow().read(pos, data)
|
||||
self.data.borrow().read(pos, buf)
|
||||
}
|
||||
|
||||
fn write(&self, _node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> {
|
||||
self.data.borrow_mut().write(pos, data)
|
||||
fn write(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
_instance: Option<&Box<dyn Any>>,
|
||||
pos: u64,
|
||||
buf: &[u8],
|
||||
) -> Result<usize, Error> {
|
||||
self.data.borrow_mut().write(pos, buf)
|
||||
}
|
||||
|
||||
fn size(&self, _node: &VnodeRef) -> Result<u64, Error> {
|
||||
Ok(self.data.borrow().size() as u64)
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: self.data.borrow().size() as u64,
|
||||
mode: self.mode,
|
||||
ty: FileType::File,
|
||||
})
|
||||
fn close(&self, _node: &NodeRef, _instance: Option<&Box<dyn Any>>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,348 +1,192 @@
|
||||
//! In-memory filesystem driver
|
||||
#![no_std]
|
||||
// #![warn(missing_docs)]
|
||||
// #![allow(clippy::new_without_default)]
|
||||
// #![feature(
|
||||
// const_mut_refs,
|
||||
// maybe_uninit_uninit_array,
|
||||
// const_maybe_uninit_uninit_array,
|
||||
// maybe_uninit_array_assume_init
|
||||
// )]
|
||||
//
|
||||
// use core::{
|
||||
// any::Any,
|
||||
// cell::{Ref, RefCell},
|
||||
// marker::PhantomData,
|
||||
// };
|
||||
//
|
||||
// use alloc::{boxed::Box, rc::Rc};
|
||||
// use block::BlockAllocator;
|
||||
// use vfs::{BlockDevice, CreateInfo, Filesystem, Vnode, VnodeKind, VnodeRef};
|
||||
// use yggdrasil_abi::{error::Error, io::FileMode, path};
|
||||
//
|
||||
// use crate::{bvec::BVec, dir::DirectoryNode, file::FileNode, tar::TarIterator};
|
||||
//
|
||||
// #[cfg(test)]
|
||||
// extern crate std;
|
||||
//
|
||||
// extern crate alloc;
|
||||
//
|
||||
// #[cfg(test)]
|
||||
// macro_rules! test_allocator_with_counter {
|
||||
// ($counter:ident, $allocator:ident) => {
|
||||
// static $counter: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
|
||||
//
|
||||
// struct $allocator;
|
||||
//
|
||||
// unsafe impl $crate::block::BlockAllocator for $allocator {
|
||||
// fn alloc() -> Result<core::ptr::NonNull<u8>, yggdrasil_abi::error::Error> {
|
||||
// let b = std::boxed::Box::into_raw(std::boxed::Box::new([0; $crate::block::SIZE]));
|
||||
// $counter.fetch_add(1, core::sync::atomic::Ordering::Release);
|
||||
// Ok(unsafe { core::ptr::NonNull::new_unchecked(b as _) })
|
||||
// }
|
||||
//
|
||||
// unsafe fn dealloc(block: core::ptr::NonNull<u8>) {
|
||||
// $counter.fetch_sub(1, core::sync::atomic::Ordering::Release);
|
||||
// drop(std::boxed::Box::from_raw(
|
||||
// block.as_ptr() as *mut [u8; $crate::block::SIZE]
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// pub mod block;
|
||||
// pub mod bvec;
|
||||
//
|
||||
// mod dir;
|
||||
// mod file;
|
||||
// mod tar;
|
||||
//
|
||||
// const DEFAULT_FILE_MODE: FileMode = FileMode::new(0o644);
|
||||
// const DEFAULT_DIR_MODE: FileMode = FileMode::new(0o755);
|
||||
//
|
||||
// /// In-memory read/write filesystem
|
||||
// pub struct MemoryFilesystem<A: BlockAllocator> {
|
||||
// root: RefCell<Option<VnodeRef>>,
|
||||
// _pd: PhantomData<A>,
|
||||
// }
|
||||
//
|
||||
// impl<A: BlockAllocator> Filesystem for MemoryFilesystem<A> {
|
||||
// fn dev(self: Rc<Self>) -> Option<&'static dyn BlockDevice> {
|
||||
// todo!()
|
||||
// }
|
||||
//
|
||||
// fn data(&self) -> Option<Ref<dyn Any>> {
|
||||
// todo!()
|
||||
// }
|
||||
//
|
||||
// fn root(self: Rc<Self>) -> Result<VnodeRef, Error> {
|
||||
// Ok(self.root.borrow().clone().unwrap())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
// fn make_path(
|
||||
// self: &Rc<Self>,
|
||||
// at: &VnodeRef,
|
||||
// path: &str,
|
||||
// kind: VnodeKind,
|
||||
// create: bool,
|
||||
// ) -> Result<VnodeRef, Error> {
|
||||
// if path.is_empty() {
|
||||
// return Ok(at.clone());
|
||||
// }
|
||||
// let (element, rest) = path::split_left(path);
|
||||
// assert!(!element.is_empty());
|
||||
//
|
||||
// let node = at.lookup(element);
|
||||
// let node = match node {
|
||||
// Some(node) => node,
|
||||
// None => {
|
||||
// if !create {
|
||||
// return Err(Error::DoesNotExist);
|
||||
// }
|
||||
//
|
||||
// let node = self.create_node_initial(element, kind);
|
||||
// // TODO require .tar's to have all the directories present to extract their metadata?
|
||||
// if kind == VnodeKind::Directory {
|
||||
// node.set_data(Box::new(DirectoryNode::<A>::new(0, 0, DEFAULT_DIR_MODE)));
|
||||
// }
|
||||
// at.add_child(node.clone());
|
||||
//
|
||||
// node
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// if rest.is_empty() {
|
||||
// Ok(node)
|
||||
// } else {
|
||||
// assert!(node.is_directory());
|
||||
// self.make_path(&node, rest, kind, create)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn create_node_initial(self: &Rc<Self>, name: &str, kind: VnodeKind) -> VnodeRef {
|
||||
// assert!(!name.is_empty());
|
||||
// assert!(!name.contains('/'));
|
||||
//
|
||||
// let node = Vnode::new(name, kind);
|
||||
// node.set_fs(self.clone());
|
||||
//
|
||||
// // match kind {
|
||||
// // VnodeKind::Directory => node.set_data(Box::new(DirectoryNode::<A>::new(
|
||||
// // info.uid, info.gid, info.mode,
|
||||
// // ))),
|
||||
// // VnodeKind::Regular => {}
|
||||
// // _ => todo!(),
|
||||
// // }
|
||||
//
|
||||
// node
|
||||
// }
|
||||
//
|
||||
// fn from_slice_internal(self: &Rc<Self>, tar_data: &'static [u8]) -> Result<VnodeRef, Error> {
|
||||
// let root = Vnode::new("", VnodeKind::Directory);
|
||||
// root.set_fs(self.clone());
|
||||
// root.set_data(Box::new(DirectoryNode::<A>::new(0, 0, DEFAULT_DIR_MODE)));
|
||||
//
|
||||
// // 1. Create paths in tar
|
||||
// for item in TarIterator::new(tar_data) {
|
||||
// let Ok((hdr, _)) = item else {
|
||||
// return Err(Error::InvalidArgument);
|
||||
// };
|
||||
//
|
||||
// let path = hdr.name.as_str()?.trim_matches('/');
|
||||
// let (dirname, filename) = path::split_right(path);
|
||||
// let parent = self.make_path(&root, dirname, VnodeKind::Directory, true)?;
|
||||
// let node = self.create_node_initial(filename, hdr.node_kind());
|
||||
//
|
||||
// parent.add_child(node);
|
||||
// }
|
||||
//
|
||||
// // 2. Associate files with their data
|
||||
// for item in TarIterator::new(tar_data) {
|
||||
// let Ok((hdr, data)) = item else {
|
||||
// panic!("Unreachable");
|
||||
// };
|
||||
//
|
||||
// let path = hdr.name.as_str()?.trim_matches('/');
|
||||
// let node = self.make_path(&root, path, VnodeKind::Directory, false)?;
|
||||
// assert_eq!(node.kind(), hdr.node_kind());
|
||||
//
|
||||
// if hdr.node_kind() == VnodeKind::Regular {
|
||||
// let uid = usize::from(&hdr.uid).try_into().unwrap();
|
||||
// let gid = usize::from(&hdr.gid).try_into().unwrap();
|
||||
// let mode = convert_mode(usize::from(&hdr.mode))?;
|
||||
//
|
||||
// let data = data.unwrap();
|
||||
// let bvec = BVec::<A>::try_from(data)?;
|
||||
// assert_eq!(bvec.size(), data.len());
|
||||
// node.set_data(Box::new(FileNode {
|
||||
// uid,
|
||||
// gid,
|
||||
// mode,
|
||||
// data: RefCell::new(bvec),
|
||||
// }));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Ok(root)
|
||||
// }
|
||||
//
|
||||
// /// Constructs a filesystem tree from a tar image in memory
|
||||
// pub fn from_slice(tar_data: &'static [u8]) -> Result<Rc<Self>, Error> {
|
||||
// let fs = Rc::new(Self {
|
||||
// root: RefCell::new(None),
|
||||
// _pd: PhantomData,
|
||||
// });
|
||||
// let root = fs.from_slice_internal(tar_data)?;
|
||||
// fs.root.replace(Some(root));
|
||||
//
|
||||
// Ok(fs)
|
||||
// }
|
||||
//
|
||||
// /// Constructs an empty memory filesystem
|
||||
// pub fn empty() -> Rc<Self> {
|
||||
// let fs = Rc::new(Self {
|
||||
// root: RefCell::new(None),
|
||||
// _pd: PhantomData,
|
||||
// });
|
||||
// let root = Vnode::new("", VnodeKind::Directory);
|
||||
// root.set_data(Box::new(DirectoryNode::<A>::new(0, 0, DEFAULT_DIR_MODE)));
|
||||
// root.set_fs(fs.clone());
|
||||
// fs.root.replace(Some(root));
|
||||
// fs
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn convert_mode(mode: usize) -> Result<FileMode, Error> {
|
||||
// Ok(FileMode::new(mode as u32 & 0o777))
|
||||
// }
|
||||
//
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use core::sync::atomic::Ordering;
|
||||
// use std::rc::Rc;
|
||||
// use vfs::{Filesystem, IoContext, Read, Seek, VnodeKind, Write};
|
||||
// use yggdrasil_abi::io::{FileMode, OpenOptions, SeekFrom};
|
||||
//
|
||||
// use crate::MemoryFilesystem;
|
||||
//
|
||||
// #[repr(C)]
|
||||
// struct AlignedTo<Align, Bytes: ?Sized> {
|
||||
// _align: [Align; 0],
|
||||
// bytes: Bytes,
|
||||
// }
|
||||
//
|
||||
// static TEST_IMAGE: &'static AlignedTo<u64, [u8]> = &AlignedTo {
|
||||
// _align: [],
|
||||
// bytes: *include_bytes!("../test/test_image.tar"),
|
||||
// };
|
||||
//
|
||||
// #[test]
|
||||
// fn test_memfs_construction() {
|
||||
// fn check_file(ioctx: &IoContext, path: &str, expected_data: &str) {
|
||||
// let node = ioctx.find(None, path, false, false).unwrap();
|
||||
//
|
||||
// assert_eq!(node.kind(), VnodeKind::Regular);
|
||||
// assert_eq!(node.size().unwrap(), expected_data.len() as u64);
|
||||
//
|
||||
// let file = node.open(OpenOptions::READ).unwrap();
|
||||
// let mut buf = [0; 512];
|
||||
//
|
||||
// assert_eq!(
|
||||
// file.borrow_mut().read(&mut buf).unwrap(),
|
||||
// expected_data.len()
|
||||
// );
|
||||
//
|
||||
// assert_eq!(&buf[..expected_data.len()], expected_data.as_bytes());
|
||||
// }
|
||||
//
|
||||
// test_allocator_with_counter!(A_COUNTER, A);
|
||||
//
|
||||
// let fs = MemoryFilesystem::<A>::from_slice(&TEST_IMAGE.bytes).unwrap();
|
||||
// let root = fs.root().unwrap();
|
||||
//
|
||||
// // Files are small, so no indirect blocks allocated
|
||||
// assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
//
|
||||
// let ioctx = IoContext::new(root.clone());
|
||||
//
|
||||
// assert!(Rc::ptr_eq(
|
||||
// &root,
|
||||
// &ioctx.find(None, "/", false, false).unwrap()
|
||||
// ));
|
||||
//
|
||||
// let old_data = include_str!("../test/test1.txt");
|
||||
// check_file(&ioctx, "/test1.txt", old_data);
|
||||
//
|
||||
// // Write to the file
|
||||
// {
|
||||
// let node = ioctx.find(None, "/test1.txt", false, false).unwrap();
|
||||
// let file = node.open(OpenOptions::WRITE).unwrap();
|
||||
//
|
||||
// assert_eq!(file.borrow_mut().write(b"Hello").unwrap(), 5);
|
||||
// }
|
||||
//
|
||||
// assert_eq!(A_COUNTER.load(Ordering::Acquire), 1);
|
||||
//
|
||||
// // Read back
|
||||
// {
|
||||
// let node = ioctx.find(None, "/test1.txt", false, false).unwrap();
|
||||
// let file = node.open(OpenOptions::READ).unwrap();
|
||||
//
|
||||
// let mut buf = [0; 512];
|
||||
// assert_eq!(file.borrow_mut().read(&mut buf).unwrap(), old_data.len());
|
||||
//
|
||||
// assert_eq!(&buf[..5], b"Hello");
|
||||
// assert_eq!(&buf[5..old_data.len()], &old_data.as_bytes()[5..]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // #[test]
|
||||
// // fn test_memfs_create_and_write() {
|
||||
// // test_allocator_with_counter!(A_COUNTER, A);
|
||||
//
|
||||
// // let fs = MemoryFilesystem::<A>::empty();
|
||||
// // let root = fs.root().unwrap();
|
||||
//
|
||||
// // let ioctx = IoContext::new(root.clone());
|
||||
//
|
||||
// // // Create, write, seek and read file
|
||||
// // {
|
||||
// // // TODO CREATE option handling
|
||||
// // root.create("test1.txt", VnodeKind::Regular).unwrap();
|
||||
//
|
||||
// // let file = ioctx
|
||||
// // .open(
|
||||
// // None,
|
||||
// // "/test1.txt",
|
||||
// // OpenOptions::WRITE | OpenOptions::READ,
|
||||
// // FileMode::empty(),
|
||||
// // )
|
||||
// // .unwrap();
|
||||
//
|
||||
// // let write_data = [1, 2, 3, 4];
|
||||
// // let mut read_data = [0; 512];
|
||||
//
|
||||
// // let mut file = file.borrow_mut();
|
||||
// // assert_eq!(file.write(&write_data).unwrap(), write_data.len());
|
||||
// // assert_eq!(file.seek(SeekFrom::Start(0)).unwrap(), 0);
|
||||
// // assert_eq!(file.read(&mut read_data).unwrap(), write_data.len());
|
||||
// // assert_eq!(&read_data[..write_data.len()], &write_data[..]);
|
||||
// // }
|
||||
//
|
||||
// // // Create a directory
|
||||
// // {
|
||||
// // // TODO read directory
|
||||
// // root.create("dir1", VnodeKind::Directory).unwrap();
|
||||
//
|
||||
// // let dir1 = ioctx.find(None, "/dir1", false, false).unwrap();
|
||||
// // let node = dir1.create("file1.txt", VnodeKind::Regular).unwrap();
|
||||
// // assert!(Rc::ptr_eq(
|
||||
// // &ioctx.find(None, "/dir1/file1.txt", false, false).unwrap(),
|
||||
// // &node
|
||||
// // ));
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
#![deny(missing_docs)]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![feature(
|
||||
const_mut_refs,
|
||||
maybe_uninit_uninit_array,
|
||||
const_maybe_uninit_uninit_array,
|
||||
maybe_uninit_array_assume_init
|
||||
)]
|
||||
|
||||
use core::{cell::RefCell, marker::PhantomData};
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use block::BlockAllocator;
|
||||
use dir::DirectoryNode;
|
||||
use file::FileNode;
|
||||
use vfs::{AccessToken, NodeRef};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{FileMode, FileType, GroupId, UserId},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::tar::TarIterator;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! test_allocator_with_counter {
|
||||
($counter:ident, $allocator:ident) => {
|
||||
static $counter: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
|
||||
|
||||
struct $allocator;
|
||||
|
||||
unsafe impl $crate::block::BlockAllocator for $allocator {
|
||||
fn alloc() -> Result<core::ptr::NonNull<u8>, yggdrasil_abi::error::Error> {
|
||||
let b = std::boxed::Box::into_raw(std::boxed::Box::new([0; $crate::block::SIZE]));
|
||||
$counter.fetch_add(1, core::sync::atomic::Ordering::Release);
|
||||
Ok(unsafe { core::ptr::NonNull::new_unchecked(b as _) })
|
||||
}
|
||||
|
||||
unsafe fn dealloc(block: core::ptr::NonNull<u8>) {
|
||||
$counter.fetch_sub(1, core::sync::atomic::Ordering::Release);
|
||||
drop(std::boxed::Box::from_raw(
|
||||
block.as_ptr() as *mut [u8; $crate::block::SIZE]
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod block;
|
||||
pub mod bvec;
|
||||
|
||||
mod dir;
|
||||
mod file;
|
||||
mod tar;
|
||||
|
||||
/// In-memory read/write filesystem
|
||||
pub struct MemoryFilesystem<A: BlockAllocator> {
|
||||
root: RefCell<Option<NodeRef>>,
|
||||
_pd: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
fn make_path(
|
||||
self: &Rc<Self>,
|
||||
at: &NodeRef,
|
||||
path: &Path,
|
||||
kind: FileType,
|
||||
create: bool,
|
||||
) -> Result<NodeRef, Error> {
|
||||
let access = unsafe { AccessToken::authorized() };
|
||||
if path.is_empty() {
|
||||
return Ok(at.clone());
|
||||
}
|
||||
let (element, rest) = path.split_left();
|
||||
// let (element, rest) = path::split_left(path);
|
||||
assert!(!element.is_empty());
|
||||
assert!(!element.contains('/'));
|
||||
|
||||
// let node = at.lookup(element);
|
||||
let node = at.lookup_or_load(element, access);
|
||||
let node = match node {
|
||||
Ok(node) => node,
|
||||
Err(Error::DoesNotExist) => {
|
||||
if !create {
|
||||
return Err(Error::DoesNotExist);
|
||||
}
|
||||
|
||||
let node = self.create_node_initial(kind);
|
||||
at.add_child(element, node.clone())?;
|
||||
|
||||
node
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("{:?}: lookup failed: {:?}", path, err);
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
if rest.is_empty() {
|
||||
Ok(node)
|
||||
} else {
|
||||
assert!(node.is_directory());
|
||||
self.make_path(&node, rest, kind, create)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_node_initial(self: &Rc<Self>, kind: FileType) -> NodeRef {
|
||||
match kind {
|
||||
FileType::File => FileNode::<A>::new(),
|
||||
FileType::Directory => DirectoryNode::<A>::new(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_slice_internal(self: &Rc<Self>, tar_data: &'static [u8]) -> Result<NodeRef, Error> {
|
||||
let root = DirectoryNode::<A>::new();
|
||||
|
||||
// 1. Create paths in tar
|
||||
for item in TarIterator::new(tar_data) {
|
||||
let Ok((hdr, _)) = item else {
|
||||
return Err(Error::InvalidArgument);
|
||||
};
|
||||
|
||||
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
|
||||
log::debug!("Make path {:?}", path);
|
||||
|
||||
let (dirname, filename) = path.split_right();
|
||||
let parent = self.make_path(&root, dirname, FileType::Directory, true)?;
|
||||
let node = self.create_node_initial(hdr.node_kind());
|
||||
|
||||
parent.add_child(filename, node)?;
|
||||
}
|
||||
|
||||
// 2. Associate files with their data
|
||||
for item in TarIterator::new(tar_data) {
|
||||
let Ok((hdr, data)) = item else {
|
||||
panic!("Unreachable");
|
||||
};
|
||||
|
||||
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
|
||||
let node = self.make_path(&root, path, FileType::Directory, false)?;
|
||||
assert_eq!(node.ty(), hdr.node_kind());
|
||||
|
||||
let uid = UserId::from(usize::from(&hdr.uid) as u32);
|
||||
let gid = GroupId::from(usize::from(&hdr.gid) as u32);
|
||||
let mode = convert_mode(usize::from(&hdr.mode))?;
|
||||
|
||||
let access = unsafe { AccessToken::authorized() };
|
||||
node.set_access(Some(uid), Some(gid), Some(mode), access)?;
|
||||
|
||||
if hdr.node_kind() == FileType::File {
|
||||
let data = data.unwrap();
|
||||
let node_data = node.data_as_ref::<FileNode<A>>();
|
||||
let mut bvec = node_data.data.borrow_mut();
|
||||
|
||||
bvec.init_with_cow(data)?;
|
||||
assert_eq!(bvec.size(), data.len());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
/// Constructs a filesystem tree from a tar image in memory
|
||||
pub fn from_slice(tar_data: &'static [u8]) -> Result<Rc<Self>, Error> {
|
||||
let fs = Rc::new(Self {
|
||||
root: RefCell::new(None),
|
||||
_pd: PhantomData,
|
||||
});
|
||||
let root = fs.from_slice_internal(tar_data)?;
|
||||
fs.root.replace(Some(root));
|
||||
|
||||
Ok(fs)
|
||||
}
|
||||
|
||||
// TODO Filesystem trait?
|
||||
/// Returns the root node of the memory filesystem
|
||||
pub fn root(&self) -> Result<NodeRef, Error> {
|
||||
Ok(self.root.borrow().clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_mode(mode: usize) -> Result<FileMode, Error> {
|
||||
Ok(FileMode::new(mode as u32 & 0o777))
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use vfs::VnodeKind;
|
||||
use yggdrasil_abi::error::Error;
|
||||
use yggdrasil_abi::{error::Error, io::FileType};
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct OctalField<const N: usize> {
|
||||
@ -126,10 +125,10 @@ impl TarEntry {
|
||||
self.name.data[0] == 0
|
||||
}
|
||||
|
||||
pub fn node_kind(&self) -> VnodeKind {
|
||||
pub fn node_kind(&self) -> FileType {
|
||||
match self.type_ {
|
||||
0 | b'0' => VnodeKind::Regular,
|
||||
b'5' => VnodeKind::Directory,
|
||||
0 | b'0' => FileType::File,
|
||||
b'5' => FileType::Directory,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,74 @@
|
||||
use yggdrasil_abi::error::Error;
|
||||
use yggdrasil_abi::{error::Error, io::DeviceRequest};
|
||||
|
||||
use crate::node::{CommonImpl, NodeRef};
|
||||
|
||||
/// Block device interface
|
||||
#[allow(unused)]
|
||||
pub trait BlockDevice {
|
||||
fn read(&self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
/// Reads data frmo the given offset of the device
|
||||
fn read(&'static self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
fn write(&self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
|
||||
/// Writes the data to the given offset of the device
|
||||
fn write(&'static self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Returns the size of the block device in bytes
|
||||
fn size(&self) -> Result<u64, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Returns `true` if the device can be read from
|
||||
fn is_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Returns `true` if the device can be written to
|
||||
fn is_writable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn size(&self) -> Result<u64, Error>;
|
||||
|
||||
/// Performs a device-specific function
|
||||
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Character device interface
|
||||
#[allow(unused)]
|
||||
pub trait CharDevice {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
/// Reads data from the device
|
||||
fn read(&'static self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
fn write(&self, buf: &[u8]) -> Result<usize, Error> {
|
||||
/// Writes the data to the device
|
||||
fn write(&'static self, buf: &[u8]) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Returns `true` if the device can be read from
|
||||
fn is_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Returns `true` if the device can be written to
|
||||
fn is_writable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Returns `true` if the given device is a terminal
|
||||
fn is_terminal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Performs a device-specific function
|
||||
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BlockDeviceWrapper(pub(crate) &'static dyn BlockDevice);
|
||||
pub(crate) struct BlockDeviceWrapper(pub(crate) &'static dyn BlockDevice);
|
||||
#[derive(Clone)]
|
||||
pub struct CharDeviceWrapper(pub(crate) &'static dyn CharDevice);
|
||||
pub(crate) struct CharDeviceWrapper(pub(crate) &'static dyn CharDevice);
|
||||
|
||||
impl BlockDeviceWrapper {
|
||||
pub fn is_readable(&self) -> bool {
|
||||
|
@ -22,14 +22,20 @@ mod device;
|
||||
mod directory;
|
||||
mod regular;
|
||||
|
||||
/// Describes the starting position of the directory
|
||||
pub enum DirectoryOpenPosition {
|
||||
/// Contents should be fetched from the directory impl with given offset
|
||||
FromPhysical(u64),
|
||||
/// Contents should be fetched from the tree cache
|
||||
FromCache,
|
||||
}
|
||||
|
||||
/// Wrapper type for a [File] shared reference
|
||||
pub type FileRef = Arc<File>;
|
||||
|
||||
// TODO some kind of a mutex instead?
|
||||
/// Describes an open file
|
||||
#[allow(missing_docs)]
|
||||
pub enum File {
|
||||
Directory(DirectoryFile),
|
||||
Regular(RegularFile),
|
||||
@ -38,12 +44,12 @@ pub enum File {
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
|
||||
pub(crate) fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
|
||||
let position = Cell::new(position.into());
|
||||
Arc::new(Self::Directory(DirectoryFile { node, position }))
|
||||
}
|
||||
|
||||
pub fn regular(
|
||||
pub(crate) fn regular(
|
||||
node: NodeRef,
|
||||
position: u64,
|
||||
instance_data: Option<Box<dyn Any>>,
|
||||
@ -61,7 +67,7 @@ impl File {
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn block(
|
||||
pub(crate) fn block(
|
||||
device: BlockDeviceWrapper,
|
||||
node: NodeRef,
|
||||
opts: OpenOptions,
|
||||
@ -85,7 +91,7 @@ impl File {
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn char(
|
||||
pub(crate) fn char(
|
||||
device: CharDeviceWrapper,
|
||||
node: NodeRef,
|
||||
opts: OpenOptions,
|
||||
@ -108,6 +114,7 @@ impl File {
|
||||
})))
|
||||
}
|
||||
|
||||
/// Reads entries from the directory
|
||||
pub fn read_dir(&self, entries: &mut [MaybeUninit<DirectoryEntry>]) -> Result<usize, Error> {
|
||||
match self {
|
||||
Self::Directory(dir) => dir.read_entries(entries),
|
||||
@ -115,6 +122,7 @@ impl File {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying [Node] the file contains
|
||||
pub fn node(&self) -> Option<&NodeRef> {
|
||||
match self {
|
||||
Self::Directory(file) => Some(&file.node),
|
||||
@ -207,7 +215,7 @@ mod tests {
|
||||
use crate::{
|
||||
device::{BlockDevice, CharDevice},
|
||||
file::DirectoryOpenPosition,
|
||||
node::{CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl},
|
||||
node::{AccessToken, CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl},
|
||||
traits::{Read, Seek, Write},
|
||||
};
|
||||
|
||||
@ -264,7 +272,7 @@ mod tests {
|
||||
NodeFlags::empty(),
|
||||
);
|
||||
|
||||
let f = d.open_directory().unwrap();
|
||||
let f = d.open_directory(AccessToken::test_authorized()).unwrap();
|
||||
|
||||
let mut entries = [MaybeUninit::uninit(); 16];
|
||||
let count = f.read_dir(&mut entries).unwrap();
|
||||
@ -310,7 +318,7 @@ mod tests {
|
||||
|
||||
d.add_child("child1", child).unwrap();
|
||||
|
||||
let f = d.open_directory().unwrap();
|
||||
let f = d.open_directory(AccessToken::test_authorized()).unwrap();
|
||||
|
||||
let mut entries = [MaybeUninit::uninit(); 16];
|
||||
let count = f.read_dir(&mut entries).unwrap();
|
||||
@ -393,7 +401,12 @@ mod tests {
|
||||
|
||||
let data = Arc::new(RefCell::new(vec![]));
|
||||
let node = Node::regular(F { data: data.clone() }, NodeFlags::empty());
|
||||
let file = node.open(OpenOptions::READ | OpenOptions::WRITE).unwrap();
|
||||
let file = node
|
||||
.open(
|
||||
OpenOptions::READ | OpenOptions::WRITE,
|
||||
AccessToken::test_authorized(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut buf = [0; 512];
|
||||
|
||||
assert_eq!(&*data.borrow(), &[]);
|
||||
@ -460,7 +473,12 @@ mod tests {
|
||||
|
||||
let node = Node::block(dev, NodeFlags::empty());
|
||||
|
||||
let file = node.open(OpenOptions::READ | OpenOptions::WRITE).unwrap();
|
||||
let file = node
|
||||
.open(
|
||||
OpenOptions::READ | OpenOptions::WRITE,
|
||||
AccessToken::test_authorized(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 1024);
|
||||
assert_eq!(file.write(b"12345").unwrap(), 0);
|
||||
@ -495,10 +513,14 @@ mod tests {
|
||||
let node = Node::char(&DEV, NodeFlags::empty());
|
||||
let mut buf = [0; 512];
|
||||
|
||||
let err = node.open(OpenOptions::WRITE).unwrap_err();
|
||||
let err = node
|
||||
.open(OpenOptions::WRITE, AccessToken::test_authorized())
|
||||
.unwrap_err();
|
||||
assert_eq!(err, Error::ReadOnly);
|
||||
|
||||
let file = node.open(OpenOptions::READ).unwrap();
|
||||
let file = node
|
||||
.open(OpenOptions::READ, AccessToken::test_authorized())
|
||||
.unwrap();
|
||||
assert_eq!(file.tell().unwrap_err(), Error::InvalidOperation);
|
||||
assert_eq!(
|
||||
file.seek(SeekFrom::Start(10)).unwrap_err(),
|
||||
|
@ -6,10 +6,22 @@ use yggdrasil_abi::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
node::{CreateInfo, SymlinkData},
|
||||
node::{AccessToken, CreateInfo, SymlinkData},
|
||||
FileRef, NodeRef,
|
||||
};
|
||||
|
||||
/// Describes a general filesystem access
|
||||
pub enum Action {
|
||||
/// Access involves reading data without modification
|
||||
Read,
|
||||
/// Access involves writing file data or modifying directory content
|
||||
Write,
|
||||
/// Access involves traversing a path or opening a file for execution
|
||||
Execute,
|
||||
}
|
||||
|
||||
/// Contains the state of program's I/O context: current working directory, UID, GID, umask, etc.
|
||||
#[derive(Clone)]
|
||||
pub struct IoContext {
|
||||
uid: UserId,
|
||||
gid: GroupId,
|
||||
@ -20,6 +32,8 @@ pub struct IoContext {
|
||||
}
|
||||
|
||||
impl IoContext {
|
||||
/// Constructs a new [IoContext] with given root node (which also becomes the cwd). By default,
|
||||
/// the root user/group is used. Default umask is 0o022.
|
||||
pub fn new(root: NodeRef) -> Self {
|
||||
Self {
|
||||
uid: UserId::root(),
|
||||
@ -31,20 +45,91 @@ impl IoContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// "Clones" an I/O context the way it is inherited by a newly spawned child process
|
||||
pub fn inherit(other: &IoContext) -> Self {
|
||||
other.clone()
|
||||
}
|
||||
|
||||
/// Returns the root node of the [IoContext]
|
||||
pub fn root(&self) -> &NodeRef {
|
||||
&self.root
|
||||
}
|
||||
|
||||
/// Returns the current working directory node of the [IoContext]
|
||||
pub fn cwd(&self) -> &NodeRef {
|
||||
&self.cwd_node
|
||||
}
|
||||
|
||||
/// Sets the current user ID of the context. Returns [Error::PermissionDenied] if current user
|
||||
/// is not root.
|
||||
pub fn set_uid(&mut self, uid: UserId) -> Result<(), Error> {
|
||||
if uid != self.uid && !self.uid.is_root() {
|
||||
Err(Error::PermissionDenied)
|
||||
} else {
|
||||
self.uid = uid;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn set_uid_unchecked(&mut self, uid: UserId) {
|
||||
self.uid = uid;
|
||||
}
|
||||
|
||||
/// Sets the current group ID of the context. Returns [Error::PermissionDenied] if current user
|
||||
/// is not root.
|
||||
pub fn set_gid(&mut self, gid: GroupId) -> Result<(), Error> {
|
||||
if gid != self.gid && !self.uid.is_root() {
|
||||
Err(Error::PermissionDenied)
|
||||
} else {
|
||||
self.gid = gid;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn set_gid_unchecked(&mut self, gid: GroupId) {
|
||||
self.gid = gid;
|
||||
}
|
||||
|
||||
/// Checks if the current user can access given [crate::Node] and, if so, returns an
|
||||
/// [AccessToken].
|
||||
pub fn check_access(&self, action: Action, node: &NodeRef) -> Result<AccessToken, Error> {
|
||||
let metadata = node.metadata()?;
|
||||
|
||||
let allow = match action {
|
||||
Action::Read => {
|
||||
self.uid.is_root()
|
||||
| metadata.user_read(self.uid)
|
||||
| metadata.group_read(self.gid)
|
||||
| metadata.other_read()
|
||||
}
|
||||
Action::Write => {
|
||||
self.uid.is_root()
|
||||
| metadata.user_write(self.uid)
|
||||
| metadata.group_write(self.gid)
|
||||
| metadata.other_write()
|
||||
}
|
||||
Action::Execute => {
|
||||
metadata.user_exec(self.uid) | metadata.group_exec(self.gid) | metadata.other_exec()
|
||||
}
|
||||
};
|
||||
|
||||
if allow {
|
||||
Ok(unsafe { AccessToken::authorized() })
|
||||
} else {
|
||||
Err(Error::PermissionDenied)
|
||||
}
|
||||
}
|
||||
|
||||
/// Changes current working directory to `path`. Will fail if access is denied or the path does
|
||||
/// not point to a directory.
|
||||
pub fn set_cwd<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
|
||||
let path = path.as_ref();
|
||||
if !path.is_absolute() {
|
||||
todo!();
|
||||
}
|
||||
let node = Self::_find(self.root.clone(), path.trim_start_separators(), true, true)?;
|
||||
let node = self._find(self.root.clone(), path.trim_start_separators(), true, true)?;
|
||||
if !node.is_directory() {
|
||||
return Err(Error::NotADirectory);
|
||||
}
|
||||
@ -53,16 +138,24 @@ impl IoContext {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the current working directory path
|
||||
pub fn cwd_path(&self) -> &Path {
|
||||
self.cwd_path.as_ref()
|
||||
}
|
||||
|
||||
/// Makes a directory at given path become a "mountpoint" for the given filesystem root.
|
||||
/// When accessed, the target directory will return contents of the filesystem root instead of
|
||||
/// its own. Both the `target` path and `fs_root` Node must be directories.
|
||||
pub fn mount<P: AsRef<Path>>(&mut self, target: P, fs_root: NodeRef) -> Result<(), Error> {
|
||||
if !self.uid.is_root() {
|
||||
return Err(Error::PermissionDenied);
|
||||
}
|
||||
|
||||
let target = target.as_ref();
|
||||
if !target.is_absolute() {
|
||||
todo!();
|
||||
}
|
||||
let target_node = Self::_find(
|
||||
let target_node = self._find(
|
||||
self.root.clone(),
|
||||
target.trim_start_separators(),
|
||||
true,
|
||||
@ -72,6 +165,9 @@ impl IoContext {
|
||||
target_node.set_mountpoint_target(fs_root)
|
||||
}
|
||||
|
||||
/// Locates a [crate::Node] at given path and opens it with requested access options. If no
|
||||
/// such node exists and `OpenOptions::CREATE` is specified, will attempt to create a regular
|
||||
/// file node at given path and then open it.
|
||||
pub fn open<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
at: Option<NodeRef>,
|
||||
@ -93,14 +189,93 @@ impl IoContext {
|
||||
gid: self.gid,
|
||||
ty: FileType::File,
|
||||
};
|
||||
parent.create(&create_info)?
|
||||
let access = self.check_access(Action::Write, &parent)?;
|
||||
parent.create(&create_info, access)?
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
node.open(opts)
|
||||
// If not read/write is requested, access is granted
|
||||
let mut access = unsafe { AccessToken::authorized() };
|
||||
|
||||
if opts.contains(OpenOptions::READ) {
|
||||
access += self.check_access(Action::Read, &node)?;
|
||||
}
|
||||
if opts.contains(OpenOptions::WRITE) {
|
||||
access += self.check_access(Action::Write, &node)?;
|
||||
}
|
||||
|
||||
node.open(opts, access)
|
||||
}
|
||||
|
||||
/// Opens a file at given path for execution
|
||||
pub fn open_exec<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
at: Option<NodeRef>,
|
||||
path: P,
|
||||
) -> Result<FileRef, Error> {
|
||||
let node = self.find(at, path, true, true)?;
|
||||
let access = self.check_access(Action::Execute, &node)?;
|
||||
node.open(OpenOptions::READ, access)
|
||||
}
|
||||
|
||||
/// Creates a directory a given path
|
||||
pub fn create_directory<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
at: Option<NodeRef>,
|
||||
path: P,
|
||||
mode: FileMode,
|
||||
) -> Result<NodeRef, Error> {
|
||||
let path = path.as_ref();
|
||||
let (parent, name) = path.split_right();
|
||||
let parent = self.find(at, parent, true, true)?;
|
||||
let access = self.check_access(Action::Write, &parent)?;
|
||||
let create_info = CreateInfo {
|
||||
name: name.into(),
|
||||
ty: FileType::Directory,
|
||||
uid: self.uid,
|
||||
gid: self.gid,
|
||||
mode: mode & !self.umask,
|
||||
};
|
||||
|
||||
parent.create(&create_info, access)
|
||||
}
|
||||
|
||||
fn remove_entry<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
at: Option<NodeRef>,
|
||||
path: P,
|
||||
directory: bool,
|
||||
) -> Result<(), Error> {
|
||||
let path = path.as_ref();
|
||||
let (parent, name) = path.trim_end_separators().split_right();
|
||||
|
||||
if name.is_empty() {
|
||||
log::warn!("Tried to remove weird path: {:?}", path);
|
||||
return Err(Error::DoesNotExist);
|
||||
}
|
||||
|
||||
let parent = self.find(at, parent, false, false)?;
|
||||
let access = self.check_access(Action::Write, &parent)?;
|
||||
|
||||
if directory {
|
||||
// parent.remove_directory(name, access)
|
||||
todo!()
|
||||
} else {
|
||||
parent.remove_file(name, access)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a device or regular file node at given path
|
||||
pub fn remove_file<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
at: Option<NodeRef>,
|
||||
path: P,
|
||||
) -> Result<(), Error> {
|
||||
self.remove_entry(at, path, false)
|
||||
}
|
||||
|
||||
/// Locates a [crate::Node] pointed to by given [Path]
|
||||
pub fn find<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
at: Option<NodeRef>,
|
||||
@ -118,10 +293,12 @@ impl IoContext {
|
||||
self.cwd_node.clone()
|
||||
};
|
||||
|
||||
Self::_find(at, path, follow_links, follow_mount)
|
||||
self._find(at, path, follow_links, follow_mount)
|
||||
}
|
||||
|
||||
fn _resolve_link(at: &NodeRef, link: &SymlinkData) -> Result<NodeRef, Error> {
|
||||
fn _resolve_link(&self, at: &NodeRef, link: &SymlinkData) -> Result<NodeRef, Error> {
|
||||
self.check_access(Action::Read, at)?;
|
||||
|
||||
// If the filesystem itself implements direct target resolution, use that
|
||||
match link.imp.target(at) {
|
||||
Err(Error::NotImplemented) => {}
|
||||
@ -138,7 +315,12 @@ impl IoContext {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn _resolve(mut at: NodeRef, follow_links: bool, follow_mount: bool) -> Result<NodeRef, Error> {
|
||||
fn _resolve(
|
||||
&self,
|
||||
mut at: NodeRef,
|
||||
follow_links: bool,
|
||||
follow_mount: bool,
|
||||
) -> Result<NodeRef, Error> {
|
||||
loop {
|
||||
if follow_mount && let Some(target) = at.mountpoint_target() {
|
||||
at = target.clone();
|
||||
@ -147,7 +329,7 @@ impl IoContext {
|
||||
|
||||
if follow_links && let Ok(link) = at.as_symlink() {
|
||||
// Resolve the link
|
||||
at = Self::_resolve_link(&at, link)?;
|
||||
at = self._resolve_link(&at, link)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -156,6 +338,7 @@ impl IoContext {
|
||||
}
|
||||
|
||||
fn _find(
|
||||
&self,
|
||||
mut at: NodeRef,
|
||||
path: &Path,
|
||||
follow_links: bool,
|
||||
@ -180,7 +363,7 @@ impl IoContext {
|
||||
}
|
||||
}
|
||||
|
||||
at = Self::_resolve(at, follow_links, follow_mount)?;
|
||||
at = self._resolve(at, follow_links, follow_mount)?;
|
||||
|
||||
if !at.is_directory() {
|
||||
return Err(Error::NotADirectory);
|
||||
@ -190,13 +373,14 @@ impl IoContext {
|
||||
return Ok(at);
|
||||
}
|
||||
|
||||
let node = at.lookup_or_load(element)?;
|
||||
let node = Self::_resolve(node, follow_links, follow_mount)?;
|
||||
let access = self.check_access(Action::Execute, &at)?;
|
||||
let node = at.lookup_or_load(element, access)?;
|
||||
let node = self._resolve(node, follow_links, follow_mount)?;
|
||||
|
||||
if rest.is_empty() {
|
||||
Ok(node)
|
||||
} else {
|
||||
Self::_find(node, rest, follow_links, follow_mount)
|
||||
self._find(node, rest, follow_links, follow_mount)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -206,17 +390,81 @@ mod tests {
|
||||
use alloc::sync::Arc;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{FileMode, OpenOptions},
|
||||
io::{FileMode, GroupId, OpenOptions, UserId},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
impls::{const_value_node, f_symlink, mdir},
|
||||
impls::{const_value_node, f_symlink, mdir, value_node},
|
||||
node::AccessToken,
|
||||
Read,
|
||||
};
|
||||
|
||||
use super::IoContext;
|
||||
|
||||
#[test]
|
||||
fn access() {
|
||||
let f1 = const_value_node("file1");
|
||||
let f2 = const_value_node("file2");
|
||||
let f3 = value_node("file3".to_owned());
|
||||
let root = mdir([("f1", f1.clone()), ("f2", f2.clone()), ("f3", f3.clone())]);
|
||||
|
||||
let mut ioctx = IoContext::new(root.clone());
|
||||
|
||||
let uid = UserId::from(1);
|
||||
let gid = GroupId::from(1);
|
||||
|
||||
// 1:1
|
||||
ioctx.set_uid_unchecked(uid);
|
||||
ioctx.set_gid_unchecked(gid);
|
||||
|
||||
// 1:0, 0444
|
||||
f1.set_access(
|
||||
Some(uid),
|
||||
None,
|
||||
Some(FileMode::new(0o444)),
|
||||
AccessToken::test_authorized(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// 0:1, 0644
|
||||
f2.set_access(None, Some(gid), None, AccessToken::test_authorized())
|
||||
.unwrap();
|
||||
|
||||
// 1:1, 0644
|
||||
f3.set_access(Some(uid), Some(gid), None, AccessToken::test_authorized())
|
||||
.unwrap();
|
||||
|
||||
// f1, read-only
|
||||
ioctx
|
||||
.open(None, "/f1", OpenOptions::READ, FileMode::empty())
|
||||
.unwrap();
|
||||
// f1, write
|
||||
let err = ioctx
|
||||
.open(None, "/f1", OpenOptions::WRITE, FileMode::empty())
|
||||
.unwrap_err();
|
||||
assert_eq!(err, Error::PermissionDenied);
|
||||
|
||||
// f2, read-only
|
||||
ioctx
|
||||
.open(None, "/f2", OpenOptions::READ, FileMode::empty())
|
||||
.unwrap();
|
||||
// f2, write
|
||||
let err = ioctx
|
||||
.open(None, "/f2", OpenOptions::WRITE, FileMode::empty())
|
||||
.unwrap_err();
|
||||
assert_eq!(err, Error::PermissionDenied);
|
||||
|
||||
// f3, read-only
|
||||
ioctx
|
||||
.open(None, "/f3", OpenOptions::READ, FileMode::empty())
|
||||
.unwrap();
|
||||
// f3, write
|
||||
ioctx
|
||||
.open(None, "/f3", OpenOptions::WRITE, FileMode::empty())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cwd() {
|
||||
let d1_f1 = const_value_node("dir1-file1");
|
||||
|
@ -1,4 +1,7 @@
|
||||
//! Virtual filesystem interfaces and driver implementation
|
||||
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![deny(missing_docs)]
|
||||
#![feature(
|
||||
trait_upcasting,
|
||||
if_let_guard,
|
||||
@ -21,5 +24,9 @@ pub(crate) mod traits;
|
||||
|
||||
pub use device::{BlockDevice, CharDevice};
|
||||
pub use file::{DirectoryOpenPosition, File, FileRef};
|
||||
pub use node::{impls, Node, NodeRef};
|
||||
pub use ioctx::{Action, IoContext};
|
||||
pub use node::{
|
||||
impls, AccessToken, CommonImpl, CreateInfo, DirectoryImpl, Metadata, Node, NodeFlags, NodeRef,
|
||||
RegularImpl, SymlinkImpl,
|
||||
};
|
||||
pub use traits::{Read, Seek, Write};
|
||||
|
78
lib/vfs/src/node/access.rs
Normal file
78
lib/vfs/src/node/access.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Add, AddAssign},
|
||||
};
|
||||
|
||||
use yggdrasil_abi::io::{FileMode, GroupId, UserId};
|
||||
|
||||
use super::Metadata;
|
||||
|
||||
/// Zero-sized token type used to ensure checked access to node functions
|
||||
pub struct AccessToken(PhantomData<()>);
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl Metadata {
|
||||
pub fn user_read(&self, uid: UserId) -> bool {
|
||||
self.uid == uid && self.mode.contains(FileMode::USER_READ)
|
||||
}
|
||||
|
||||
pub fn user_write(&self, uid: UserId) -> bool {
|
||||
self.uid == uid && self.mode.contains(FileMode::USER_WRITE)
|
||||
}
|
||||
|
||||
pub fn user_exec(&self, uid: UserId) -> bool {
|
||||
self.uid == uid && self.mode.contains(FileMode::USER_EXEC)
|
||||
}
|
||||
|
||||
pub fn group_read(&self, gid: GroupId) -> bool {
|
||||
self.gid == gid && self.mode.contains(FileMode::GROUP_READ)
|
||||
}
|
||||
|
||||
pub fn group_write(&self, gid: GroupId) -> bool {
|
||||
self.gid == gid && self.mode.contains(FileMode::GROUP_WRITE)
|
||||
}
|
||||
|
||||
pub fn group_exec(&self, gid: GroupId) -> bool {
|
||||
self.gid == gid && self.mode.contains(FileMode::GROUP_EXEC)
|
||||
}
|
||||
|
||||
pub fn other_read(&self) -> bool {
|
||||
self.mode.contains(FileMode::OTHER_READ)
|
||||
}
|
||||
|
||||
pub fn other_write(&self) -> bool {
|
||||
self.mode.contains(FileMode::OTHER_WRITE)
|
||||
}
|
||||
|
||||
pub fn other_exec(&self) -> bool {
|
||||
self.mode.contains(FileMode::OTHER_EXEC)
|
||||
}
|
||||
}
|
||||
|
||||
impl AccessToken {
|
||||
/// Creates an "authorized" [AccessToken].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: allows for unchecked authorization of any node actions.
|
||||
pub const unsafe fn authorized() -> Self {
|
||||
Self(PhantomData)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub const fn test_authorized() -> Self {
|
||||
unsafe { Self::authorized() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for AccessToken {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, _rhs: Self) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for AccessToken {
|
||||
fn add_assign(&mut self, _rhs: Self) {}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
//! Various helper node implementations for convenience
|
||||
use core::{any::Any, cell::RefCell, marker::PhantomData, str::FromStr};
|
||||
|
||||
use alloc::{
|
||||
@ -11,15 +12,13 @@ use yggdrasil_abi::{error::Error, io::OpenOptions};
|
||||
|
||||
use crate::DirectoryOpenPosition;
|
||||
|
||||
use super::{
|
||||
CommonImpl, CreateInfo, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl, SymlinkImpl,
|
||||
};
|
||||
use super::{CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl, SymlinkImpl};
|
||||
|
||||
pub trait SliceRead {
|
||||
trait SliceRead {
|
||||
fn read_slice(&self, offset: usize, buf: &mut [u8]) -> usize;
|
||||
}
|
||||
|
||||
pub trait SliceWrite {
|
||||
trait SliceWrite {
|
||||
fn write_slice(&mut self, offset: usize, buf: &[u8]) -> usize;
|
||||
}
|
||||
|
||||
@ -27,6 +26,15 @@ trait IntoInstanceData {
|
||||
fn into_instance_data(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Closure interface for reading a single value
|
||||
pub trait ValueReadFn<T> = Fn() -> Result<T, Error>;
|
||||
/// Closure interface for writing a single value
|
||||
pub trait ValueWriteFn<T> = Fn(T) -> Result<(), Error>;
|
||||
/// Closure interface for reading bytes
|
||||
pub trait ReadFn = Fn(u64, &mut [u8]) -> Result<usize, Error>;
|
||||
/// Closure interface for writing bytes
|
||||
pub trait WriteFn = Fn(u64, &[u8]) -> Result<usize, Error>;
|
||||
|
||||
impl<T: AsRef<[u8]>> SliceRead for T {
|
||||
fn read_slice(&self, pos: usize, buf: &mut [u8]) -> usize {
|
||||
let value = self.as_ref();
|
||||
@ -55,31 +63,40 @@ impl<T: ToString> IntoInstanceData for T {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReadFn<T> = Fn() -> Result<T, Error>;
|
||||
pub trait WriteFn<T> = Fn(T) -> Result<(), Error>;
|
||||
|
||||
enum FnNodeData {
|
||||
Read(Vec<u8>),
|
||||
Write(RefCell<Vec<u8>>),
|
||||
}
|
||||
|
||||
pub struct ReadOnlyFnNode<T: ToString, R: ReadFn<T>> {
|
||||
/// Allows read-only access to a value. The value is converted to a string representation when it's
|
||||
/// read.
|
||||
pub struct ReadOnlyFnValueNode<T: ToString, R: ValueReadFn<T>> {
|
||||
read: R,
|
||||
_pd: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct FnNode<T: ToString + FromStr, R: ReadFn<T>, W: WriteFn<T>> {
|
||||
/// Allows read-write access to a value (but not both at the same time). The value is converted
|
||||
/// to/from a string representation when it's read/written.
|
||||
pub struct FnValueNode<T: ToString + FromStr, R: ValueReadFn<T>, W: ValueWriteFn<T>> {
|
||||
read: R,
|
||||
write: W,
|
||||
_pd: PhantomData<T>,
|
||||
}
|
||||
|
||||
/// Allows read-only access to a "functional file"
|
||||
pub struct ReadOnlyFnNode<R: ReadFn> {
|
||||
read: R,
|
||||
}
|
||||
|
||||
/// In-memory directory using tree cache
|
||||
pub struct MemoryDirectory;
|
||||
/// In-memory symlink pointing to a fixed [Node]
|
||||
pub struct FixedSymlink {
|
||||
target: NodeRef,
|
||||
}
|
||||
|
||||
impl<T: ToString + 'static, R: ReadFn<T> + 'static> ReadOnlyFnNode<T, R> {
|
||||
impl<T: ToString + 'static, R: ValueReadFn<T> + 'static> ReadOnlyFnValueNode<T, R> {
|
||||
/// Creates a new [ReadOnlyFnValueNode] with given read function
|
||||
pub fn new(read: R) -> NodeRef {
|
||||
Node::regular(
|
||||
Self::new_impl(read),
|
||||
@ -87,7 +104,7 @@ impl<T: ToString + 'static, R: ReadFn<T> + 'static> ReadOnlyFnNode<T, R> {
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn new_impl(read: R) -> Self {
|
||||
const fn new_impl(read: R) -> Self {
|
||||
Self {
|
||||
read,
|
||||
_pd: PhantomData,
|
||||
@ -95,13 +112,13 @@ impl<T: ToString + 'static, R: ReadFn<T> + 'static> ReadOnlyFnNode<T, R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString, R: ReadFn<T>> CommonImpl for ReadOnlyFnNode<T, R> {
|
||||
impl<T: ToString, R: ValueReadFn<T>> CommonImpl for ReadOnlyFnValueNode<T, R> {
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString + 'static, R: ReadFn<T>> RegularImpl for ReadOnlyFnNode<T, R> {
|
||||
impl<T: ToString + 'static, R: ValueReadFn<T>> RegularImpl for ReadOnlyFnValueNode<T, R> {
|
||||
fn open(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
@ -162,9 +179,13 @@ impl FnNodeData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString + FromStr + 'static, R: ReadFn<T> + 'static, W: WriteFn<T> + 'static>
|
||||
FnNode<T, R, W>
|
||||
impl<T, R, W> FnValueNode<T, R, W>
|
||||
where
|
||||
T: ToString + FromStr + 'static,
|
||||
R: ValueReadFn<T> + 'static,
|
||||
W: ValueWriteFn<T> + 'static,
|
||||
{
|
||||
/// Creates a new [FnValueNode] with given read and write functions
|
||||
pub fn new(read: R, write: W) -> NodeRef {
|
||||
Node::regular(
|
||||
Self::new_impl(read, write),
|
||||
@ -172,7 +193,7 @@ impl<T: ToString + FromStr + 'static, R: ReadFn<T> + 'static, W: WriteFn<T> + 's
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn new_impl(read: R, write: W) -> Self {
|
||||
const fn new_impl(read: R, write: W) -> Self {
|
||||
Self {
|
||||
read,
|
||||
write,
|
||||
@ -181,13 +202,17 @@ impl<T: ToString + FromStr + 'static, R: ReadFn<T> + 'static, W: WriteFn<T> + 's
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString + FromStr, R: ReadFn<T>, W: WriteFn<T>> CommonImpl for FnNode<T, R, W> {
|
||||
impl<T: ToString + FromStr, R: ValueReadFn<T>, W: ValueWriteFn<T>> CommonImpl
|
||||
for FnValueNode<T, R, W>
|
||||
{
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString + FromStr, R: ReadFn<T>, W: WriteFn<T>> RegularImpl for FnNode<T, R, W> {
|
||||
impl<T: ToString + FromStr, R: ValueReadFn<T>, W: ValueWriteFn<T>> RegularImpl
|
||||
for FnValueNode<T, R, W>
|
||||
{
|
||||
fn open(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
@ -247,17 +272,70 @@ impl<T: ToString + FromStr, R: ReadFn<T>, W: WriteFn<T>> RegularImpl for FnNode<
|
||||
}
|
||||
}
|
||||
|
||||
// Byte read-only node
|
||||
|
||||
impl<R> ReadOnlyFnNode<R>
|
||||
where
|
||||
R: ReadFn + 'static,
|
||||
{
|
||||
/// Creates a new [ReadOnlyFnNode] with given read function
|
||||
pub fn new(read: R) -> NodeRef {
|
||||
Node::regular(Self { read }, NodeFlags::IN_MEMORY_PROPS)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> CommonImpl for ReadOnlyFnNode<R>
|
||||
where
|
||||
R: ReadFn,
|
||||
{
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> RegularImpl for ReadOnlyFnNode<R>
|
||||
where
|
||||
R: ReadFn,
|
||||
{
|
||||
fn open(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
if opts.contains(OpenOptions::WRITE) {
|
||||
return Err(Error::ReadOnly);
|
||||
}
|
||||
Ok((0, None))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
_instance: Option<&Box<dyn Any>>,
|
||||
pos: u64,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
(self.read)(pos, buf)
|
||||
}
|
||||
}
|
||||
|
||||
// In-memory directory
|
||||
|
||||
impl MemoryDirectory {
|
||||
/// Creates a [MemoryDirectory] with no children
|
||||
pub fn empty() -> NodeRef {
|
||||
Node::directory(
|
||||
MemoryDirectory,
|
||||
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommonImpl for MemoryDirectory {}
|
||||
impl DirectoryImpl for MemoryDirectory {
|
||||
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
|
||||
Ok(DirectoryOpenPosition::FromCache)
|
||||
}
|
||||
|
||||
fn create(&self, _parent: &NodeRef, _info: &CreateInfo) -> Result<NodeRef, Error> {
|
||||
Err(Error::ReadOnly)
|
||||
}
|
||||
}
|
||||
|
||||
// In-memory fixed symlink
|
||||
@ -269,15 +347,17 @@ impl SymlinkImpl for FixedSymlink {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a read-only value node with given `value`
|
||||
pub fn const_value_node<T: ToString + Clone + 'static>(value: T) -> NodeRef {
|
||||
ReadOnlyFnNode::new(move || Ok(value.clone()))
|
||||
ReadOnlyFnValueNode::new(move || Ok(value.clone()))
|
||||
}
|
||||
|
||||
/// Creates a read-write value node with given `value`
|
||||
pub fn value_node<T: ToString + FromStr + Clone + 'static>(value: T) -> NodeRef {
|
||||
let rd_state = Arc::new(RefCell::new(value));
|
||||
let wr_state = rd_state.clone();
|
||||
|
||||
FnNode::new(
|
||||
FnValueNode::new(
|
||||
move || Ok(rd_state.borrow().clone()),
|
||||
move |t| {
|
||||
*wr_state.borrow_mut() = t;
|
||||
@ -286,6 +366,12 @@ pub fn value_node<T: ToString + FromStr + Clone + 'static>(value: T) -> NodeRef
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a read-only node with given read function
|
||||
pub fn read_fn_node<R: ReadFn + 'static>(read: R) -> NodeRef {
|
||||
ReadOnlyFnNode::new(read)
|
||||
}
|
||||
|
||||
/// Creates an in-memory directory from the iterator
|
||||
pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> NodeRef {
|
||||
let dir = Node::directory(
|
||||
MemoryDirectory,
|
||||
@ -297,8 +383,9 @@ pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> Nod
|
||||
dir
|
||||
}
|
||||
|
||||
/// Creates a static symlink pointing to given node
|
||||
pub fn f_symlink(target: NodeRef) -> NodeRef {
|
||||
Node::symlink(FixedSymlink { target }, NodeFlags::empty())
|
||||
Node::symlink(FixedSymlink { target }, NodeFlags::IN_MEMORY_PROPS)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -306,14 +393,19 @@ mod tests {
|
||||
use yggdrasil_abi::io::OpenOptions;
|
||||
|
||||
use crate::{
|
||||
node::impls::{const_value_node, value_node},
|
||||
node::{
|
||||
impls::{const_value_node, value_node},
|
||||
AccessToken,
|
||||
},
|
||||
traits::{Read, Seek, Write},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn read_only_fn_node() {
|
||||
let node = const_value_node("abcdef");
|
||||
let file = node.open(OpenOptions::READ).unwrap();
|
||||
let file = node
|
||||
.open(OpenOptions::READ, AccessToken::test_authorized())
|
||||
.unwrap();
|
||||
let mut buf = [0; 512];
|
||||
|
||||
assert_eq!(file.tell().unwrap(), 0);
|
||||
@ -331,19 +423,25 @@ mod tests {
|
||||
let mut buf = [0; 512];
|
||||
|
||||
// Try read the value
|
||||
let file = node.open(OpenOptions::READ).unwrap();
|
||||
let file = node
|
||||
.open(OpenOptions::READ, AccessToken::test_authorized())
|
||||
.unwrap();
|
||||
assert_eq!(file.tell().unwrap(), 0);
|
||||
assert_eq!(file.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(&buf[..4], b"1234");
|
||||
|
||||
// Try write the value
|
||||
let file = node.open(OpenOptions::WRITE).unwrap();
|
||||
let file = node
|
||||
.open(OpenOptions::WRITE, AccessToken::test_authorized())
|
||||
.unwrap();
|
||||
assert_eq!(file.tell().unwrap(), 0);
|
||||
assert_eq!(file.write(b"654321").unwrap(), 6);
|
||||
drop(file);
|
||||
|
||||
// Try read the value again
|
||||
let file = node.open(OpenOptions::READ).unwrap();
|
||||
let file = node
|
||||
.open(OpenOptions::READ, AccessToken::test_authorized())
|
||||
.unwrap();
|
||||
assert_eq!(file.tell().unwrap(), 0);
|
||||
assert_eq!(file.read(&mut buf).unwrap(), 6);
|
||||
assert_eq!(&buf[..6], b"654321");
|
||||
|
@ -1,17 +1,14 @@
|
||||
use core::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
fmt,
|
||||
};
|
||||
use core::{any::Any, fmt};
|
||||
|
||||
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
|
||||
use kernel_util::sync::IrqSafeSpinlock;
|
||||
use yggdrasil_abi::{
|
||||
bitflags,
|
||||
error::Error,
|
||||
io::{FileAttr, FileMode, FileType, GroupId, UserId},
|
||||
io::{FileMode, FileType, GroupId, UserId},
|
||||
};
|
||||
|
||||
mod access;
|
||||
pub mod impls;
|
||||
mod traits;
|
||||
|
||||
@ -19,25 +16,36 @@ mod traits;
|
||||
mod ops;
|
||||
mod tree;
|
||||
|
||||
pub use access::AccessToken;
|
||||
pub use traits::{CommonImpl, DirectoryImpl, RegularImpl, SymlinkImpl};
|
||||
|
||||
use crate::device::{BlockDevice, BlockDeviceWrapper, CharDevice, CharDeviceWrapper};
|
||||
|
||||
/// Wrapper type for a [Node] shared reference
|
||||
pub type NodeRef = Arc<Node>;
|
||||
|
||||
bitflags! {
|
||||
#[doc = "Describes additional flags for the node"]
|
||||
pub struct NodeFlags: u32 {
|
||||
#[doc = "Node's metadata only exists within the VFS cache and should not be fetched"]
|
||||
const IN_MEMORY_PROPS: bit 0;
|
||||
#[doc = "Node's size only exists within the VFS cache"]
|
||||
const IN_MEMORY_SIZE: bit 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Information used when creating an entry within a directory
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CreateInfo {
|
||||
/// New entry name
|
||||
pub name: String,
|
||||
/// User ID of the entry
|
||||
pub uid: UserId,
|
||||
/// Group ID of the entry
|
||||
pub gid: GroupId,
|
||||
/// Access mode of the entry
|
||||
pub mode: FileMode,
|
||||
/// Entry type
|
||||
pub ty: FileType,
|
||||
}
|
||||
|
||||
@ -61,19 +69,23 @@ enum NodeImpl {
|
||||
Symlink(SymlinkData),
|
||||
}
|
||||
|
||||
/// Metadata of the node
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Metadata {
|
||||
/// User ID of the node
|
||||
pub uid: UserId,
|
||||
/// Group ID of the node
|
||||
pub gid: GroupId,
|
||||
/// Access mode of the node
|
||||
pub mode: FileMode,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PropertyCache {
|
||||
metadata: Metadata,
|
||||
size: Option<u64>,
|
||||
}
|
||||
|
||||
/// Decribes a single entry within a filesytem
|
||||
pub struct Node {
|
||||
data: NodeImpl,
|
||||
flags: NodeFlags,
|
||||
@ -81,47 +93,75 @@ pub struct Node {
|
||||
parent: IrqSafeSpinlock<Option<NodeRef>>,
|
||||
}
|
||||
|
||||
impl Default for Metadata {
|
||||
fn default() -> Metadata {
|
||||
impl Metadata {
|
||||
pub(crate) const fn default_dir() -> Metadata {
|
||||
Metadata {
|
||||
uid: UserId::root(),
|
||||
gid: GroupId::root(),
|
||||
mode: FileMode::new(0),
|
||||
mode: FileMode::new(0o755),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn default_file() -> Metadata {
|
||||
Metadata {
|
||||
uid: UserId::root(),
|
||||
gid: GroupId::root(),
|
||||
mode: FileMode::new(0o644),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn new(data: NodeImpl, flags: NodeFlags) -> NodeRef {
|
||||
fn new(data: NodeImpl, flags: NodeFlags, metadata: Metadata) -> NodeRef {
|
||||
Arc::new(Self {
|
||||
data,
|
||||
flags,
|
||||
props: IrqSafeSpinlock::new(PropertyCache::default()),
|
||||
props: IrqSafeSpinlock::new(PropertyCache {
|
||||
metadata,
|
||||
size: None,
|
||||
}),
|
||||
parent: IrqSafeSpinlock::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new directory node with given [DirectoryImpl]
|
||||
pub fn directory<T: DirectoryImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
|
||||
let data = NodeImpl::Directory(DirectoryData {
|
||||
imp: Box::new(data),
|
||||
mountpoint: IrqSafeSpinlock::new(None),
|
||||
children: IrqSafeSpinlock::new(Vec::new()),
|
||||
});
|
||||
Self::new(data, flags)
|
||||
Self::new(data, flags, Metadata::default_dir())
|
||||
}
|
||||
|
||||
/// Creates a new file node with given [RegularImpl]
|
||||
pub fn regular<T: RegularImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
|
||||
Self::new(NodeImpl::Regular(Box::new(data)), flags)
|
||||
Self::new(
|
||||
NodeImpl::Regular(Box::new(data)),
|
||||
flags,
|
||||
Metadata::default_file(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new block device node with given [BlockDevice]
|
||||
pub fn block(device: &'static dyn BlockDevice, flags: NodeFlags) -> NodeRef {
|
||||
Self::new(NodeImpl::Block(BlockDeviceWrapper(device)), flags)
|
||||
Self::new(
|
||||
NodeImpl::Block(BlockDeviceWrapper(device)),
|
||||
flags,
|
||||
Metadata::default_file(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new character device node with given [CharDevice]
|
||||
pub fn char(device: &'static dyn CharDevice, flags: NodeFlags) -> NodeRef {
|
||||
Self::new(NodeImpl::Char(CharDeviceWrapper(device)), flags)
|
||||
Self::new(
|
||||
NodeImpl::Char(CharDeviceWrapper(device)),
|
||||
flags,
|
||||
Metadata::default_file(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new symbolic link node with given [SymlinkImpl]
|
||||
pub fn symlink<T: SymlinkImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
|
||||
Self::new(
|
||||
NodeImpl::Symlink(SymlinkData {
|
||||
@ -129,9 +169,11 @@ impl Node {
|
||||
imp: Box::new(data),
|
||||
}),
|
||||
flags,
|
||||
Metadata::default_file(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the impl data of the node as `dyn Any`
|
||||
pub fn data_as_any(&self) -> &dyn Any {
|
||||
match &self.data {
|
||||
NodeImpl::Directory(dir) => dir.imp.as_any(),
|
||||
@ -142,6 +184,7 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the impl data of the node as `dyn CommonImpl`
|
||||
pub fn data_as_common(&self) -> &dyn CommonImpl {
|
||||
match &self.data {
|
||||
NodeImpl::Directory(dir) => dir.imp.as_ref(),
|
||||
@ -152,10 +195,12 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to cast the impl data of the node to `&T`
|
||||
pub fn data_as_ref<T: 'static>(&self) -> &T {
|
||||
self.data_as_any().downcast_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the type of the node
|
||||
pub fn ty(&self) -> FileType {
|
||||
match &self.data {
|
||||
NodeImpl::Regular(_) => FileType::File,
|
||||
@ -166,10 +211,13 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the node represents a directory
|
||||
pub fn is_directory(&self) -> bool {
|
||||
matches!(&self.data, NodeImpl::Directory(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if the node represents a character device and the character device is a
|
||||
/// terminal
|
||||
pub fn is_terminal(&self) -> bool {
|
||||
if let NodeImpl::Char(dev) = &self.data {
|
||||
dev.is_terminal()
|
||||
|
@ -2,15 +2,30 @@ use core::mem::MaybeUninit;
|
||||
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{DirectoryEntry, OpenOptions},
|
||||
io::{DeviceRequest, DirectoryEntry, FileMode, GroupId, OpenOptions, UserId},
|
||||
};
|
||||
|
||||
use crate::file::{File, FileRef};
|
||||
|
||||
use super::{CreateInfo, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
|
||||
use super::{AccessToken, CreateInfo, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
|
||||
|
||||
impl Node {
|
||||
pub fn open(self: &NodeRef, opts: OpenOptions) -> Result<FileRef, Error> {
|
||||
// Devices
|
||||
|
||||
/// Performs a device-specific function on a the device node
|
||||
pub fn device_request(self: &NodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
match &self.data {
|
||||
NodeImpl::Block(dev) => dev.0.device_request(req),
|
||||
NodeImpl::Char(dev) => dev.0.device_request(req),
|
||||
_ => Err(Error::InvalidOperation),
|
||||
}
|
||||
}
|
||||
|
||||
// Devices + files
|
||||
|
||||
/// Opens the node with given [OpenOptions]. Only works for regular files and devices. For
|
||||
/// directories, use [Node::open_directory].
|
||||
pub fn open(self: &NodeRef, opts: OpenOptions, _check: AccessToken) -> Result<FileRef, Error> {
|
||||
match &self.data {
|
||||
NodeImpl::Regular(imp) => {
|
||||
let (pos, instance) = imp.open(self, opts)?;
|
||||
@ -25,12 +40,14 @@ impl Node {
|
||||
|
||||
// Directory
|
||||
|
||||
pub fn open_directory(self: &NodeRef) -> Result<FileRef, Error> {
|
||||
/// Opens the node as a directory for reading its entries
|
||||
pub fn open_directory(self: &NodeRef, _check: AccessToken) -> Result<FileRef, Error> {
|
||||
let dir = self.as_directory()?;
|
||||
let pos = dir.imp.open(self)?;
|
||||
Ok(File::directory(self.clone(), pos))
|
||||
}
|
||||
|
||||
/// Reads entries from the directory
|
||||
pub fn read_directory(
|
||||
self: &NodeRef,
|
||||
pos: u64,
|
||||
@ -39,7 +56,14 @@ impl Node {
|
||||
self.as_directory()?.imp.read_entries(self, pos, entries)
|
||||
}
|
||||
|
||||
pub fn lookup_or_load(self: &NodeRef, name: &str) -> Result<NodeRef, Error> {
|
||||
/// Attempts to look up a child node with given name inside the directory node in the tree
|
||||
/// cache. If no such node is present there, will attempt to fetch it from the underlying
|
||||
/// filesystem.
|
||||
pub fn lookup_or_load(
|
||||
self: &NodeRef,
|
||||
name: &str,
|
||||
_check: AccessToken,
|
||||
) -> Result<NodeRef, Error> {
|
||||
let dir = self.as_directory()?;
|
||||
let children = dir.children.lock();
|
||||
|
||||
@ -52,16 +76,87 @@ impl Node {
|
||||
Err(Error::DoesNotExist)
|
||||
}
|
||||
|
||||
pub fn create(self: &NodeRef, info: &CreateInfo) -> Result<NodeRef, Error> {
|
||||
/// Creates an entry within a directory with given [CreateInfo].
|
||||
pub fn create(self: &NodeRef, info: &CreateInfo, check: AccessToken) -> Result<NodeRef, Error> {
|
||||
let directory = self.as_directory()?;
|
||||
let node = directory.imp.create(self, info)?;
|
||||
let node = directory.imp.create_node(self, info.ty)?;
|
||||
|
||||
// Fill out the node info
|
||||
node.set_access(Some(info.uid), Some(info.gid), Some(info.mode), check)?;
|
||||
|
||||
match directory.imp.attach_node(self, &node, &info.name) {
|
||||
Ok(_) | Err(Error::NotImplemented) => (),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
|
||||
// Attach the created node to the directory in memory cache
|
||||
self.add_child(&info.name, node.clone())?;
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
/// Removes a regular file, device or symlink from the directory
|
||||
pub fn remove_file(self: &NodeRef, name: &str, check: AccessToken) -> Result<(), Error> {
|
||||
let directory = self.as_directory()?;
|
||||
let child = self.lookup_or_load(name, check)?;
|
||||
|
||||
if child.is_directory() {
|
||||
return Err(Error::IsADirectory);
|
||||
}
|
||||
|
||||
// Detach the node in the real filesystem
|
||||
match directory.imp.unlink_node(self, name) {
|
||||
Ok(_) | Err(Error::NotImplemented) => (),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
|
||||
// Detach the node in the tree cache
|
||||
{
|
||||
let mut children = directory.children.lock();
|
||||
children.retain(|(name_, _)| name != name_);
|
||||
}
|
||||
|
||||
// TODO child.destroy() or something?
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Common
|
||||
|
||||
/// Changes user/group ID or access mode of the node
|
||||
pub fn set_access(
|
||||
self: &NodeRef,
|
||||
uid: Option<UserId>,
|
||||
gid: Option<GroupId>,
|
||||
mode: Option<FileMode>,
|
||||
_check: AccessToken,
|
||||
) -> Result<(), Error> {
|
||||
if uid.is_none() && gid.is_none() && mode.is_none() {
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
let mut metadata = self.metadata()?;
|
||||
|
||||
if let Some(uid) = uid {
|
||||
metadata.uid = uid;
|
||||
}
|
||||
if let Some(gid) = gid {
|
||||
metadata.gid = gid;
|
||||
}
|
||||
if let Some(mode) = mode {
|
||||
metadata.mode = mode;
|
||||
}
|
||||
|
||||
// Update cached props
|
||||
self.props.lock().metadata = metadata;
|
||||
|
||||
if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
|
||||
// Update permissions in the real node
|
||||
todo!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the "metadata" of the file: uid, gid, access mode
|
||||
pub fn metadata(self: &NodeRef) -> Result<Metadata, Error> {
|
||||
if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
|
||||
let props = self.props.lock();
|
||||
@ -71,6 +166,8 @@ impl Node {
|
||||
self.data_as_common().metadata(self)
|
||||
}
|
||||
|
||||
// TODO clarify directory size
|
||||
/// Returns the size in bytes of the node
|
||||
pub fn size(self: &NodeRef) -> Result<u64, Error> {
|
||||
// Try to fetch the size from the cache
|
||||
let mut props = self.props.lock();
|
||||
|
@ -3,30 +3,36 @@ use core::{any::Any, mem::MaybeUninit};
|
||||
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{DirectoryEntry, OpenOptions},
|
||||
io::{DirectoryEntry, FileType, OpenOptions},
|
||||
};
|
||||
|
||||
use crate::file::DirectoryOpenPosition;
|
||||
|
||||
use super::{CreateInfo, Metadata, NodeRef};
|
||||
use super::{Metadata, NodeRef};
|
||||
|
||||
/// Common interface shared by all filesystem nodes
|
||||
#[allow(unused)]
|
||||
pub trait CommonImpl {
|
||||
/// Returns `&self` as a reference to `dyn Any`
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Fetches the metadata of the file from underlying storage
|
||||
fn metadata(&self, node: &NodeRef) -> Result<Metadata, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Fetches the size of the file from underlying storage
|
||||
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Regular file interface
|
||||
#[allow(unused)]
|
||||
pub trait RegularImpl: CommonImpl {
|
||||
/// Opens the file for reading/writing and returns a `(start position, instance data)` tuple
|
||||
fn open(
|
||||
&self,
|
||||
node: &NodeRef,
|
||||
@ -35,10 +41,12 @@ pub trait RegularImpl: CommonImpl {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Closes a file
|
||||
fn close(&self, node: &NodeRef, instance: Option<&Box<dyn Any>>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads data from the file into given buffer
|
||||
fn read(
|
||||
&self,
|
||||
node: &NodeRef,
|
||||
@ -48,6 +56,7 @@ pub trait RegularImpl: CommonImpl {
|
||||
) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
/// Writes data to the file from given buffer
|
||||
fn write(
|
||||
&self,
|
||||
node: &NodeRef,
|
||||
@ -59,12 +68,16 @@ pub trait RegularImpl: CommonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/// Directory implementation
|
||||
#[allow(unused)]
|
||||
pub trait DirectoryImpl: CommonImpl {
|
||||
/// Opens a directory for reading its entries. Returns [DirectoryOpenPosition] to specify the
|
||||
/// starting position.
|
||||
fn open(&self, node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Fetches entries from a directory into given buffer
|
||||
fn read_entries(
|
||||
&self,
|
||||
node: &NodeRef,
|
||||
@ -74,25 +87,41 @@ pub trait DirectoryImpl: CommonImpl {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn create(&self, parent: &NodeRef, info: &CreateInfo) -> Result<NodeRef, Error> {
|
||||
/// Creates a child node, but does not associate it with the directory yet
|
||||
fn create_node(&self, parent: &NodeRef, ty: FileType) -> Result<NodeRef, Error> {
|
||||
Err(Error::ReadOnly)
|
||||
}
|
||||
|
||||
/// Associates the given node with the directory, creating an entry for it inside
|
||||
fn attach_node(&self, parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Removes an entry of the directory with given name
|
||||
fn unlink_node(&self, parent: &NodeRef, name: &str) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Fetches the child of the directory with given name
|
||||
fn lookup(&self, node: &NodeRef, name: &str) -> Result<NodeRef, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Returns the "length" of the directory in entries
|
||||
fn len(&self, node: &NodeRef) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Symbolic link interface
|
||||
#[allow(unused)]
|
||||
pub trait SymlinkImpl: CommonImpl {
|
||||
/// Returns the target node (if such is available directly) of the link
|
||||
fn target(&self, node: &NodeRef) -> Result<NodeRef, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Fetches the contents of the symlink into a [String]
|
||||
fn read_to_string(&self) -> Result<String, Error> {
|
||||
let mut data = Box::new([0; 512]);
|
||||
let len = self.read_link(data.as_mut())?;
|
||||
@ -103,6 +132,7 @@ pub trait SymlinkImpl: CommonImpl {
|
||||
Ok(String::from(str))
|
||||
}
|
||||
|
||||
/// Fetches the contents of the symlink into a buffer
|
||||
fn read_link(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
@ -5,15 +5,23 @@ use alloc::string::String;
|
||||
use super::{Node, NodeRef};
|
||||
|
||||
impl Node {
|
||||
/// Returns the parent node of this node, or itself if no parent is present
|
||||
pub fn parent(self: &NodeRef) -> NodeRef {
|
||||
self.parent.lock().as_ref().unwrap_or(self).clone()
|
||||
}
|
||||
|
||||
/// Returns `true` if this node is a root
|
||||
pub fn is_root(self: &NodeRef) -> bool {
|
||||
self.parent.lock().is_none()
|
||||
}
|
||||
|
||||
/// Returns the count of entries in the directory
|
||||
pub fn children_len(&self) -> Result<usize, Error> {
|
||||
let directory = self.as_directory()?;
|
||||
Ok(directory.children.lock().len())
|
||||
}
|
||||
|
||||
/// Adds an entry to the directory tree cache
|
||||
pub fn add_child<S: Into<String>>(
|
||||
self: &NodeRef,
|
||||
name: S,
|
||||
|
@ -1,16 +1,36 @@
|
||||
use yggdrasil_abi::{error::Error, io::SeekFrom};
|
||||
|
||||
/// Immutable read interface for VFS objects
|
||||
pub trait Read {
|
||||
/// Reads bytes into the given buffer
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize, Error>;
|
||||
|
||||
/// Reads exactly `buf.len()` bytes into the given buffer, fails if such amount is not
|
||||
/// available
|
||||
fn read_exact(&self, buf: &mut [u8]) -> Result<(), Error> {
|
||||
match self.read(buf) {
|
||||
Ok(count) if count == buf.len() => Ok(()),
|
||||
Ok(_) => Err(Error::InvalidFile),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutable write interface for VFS objects
|
||||
pub trait Write {
|
||||
/// Writes bytes from the given buffer
|
||||
fn write(&self, buf: &[u8]) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
/// Immutable file positioning interface for VFS objects
|
||||
pub trait Seek {
|
||||
/// Changes position inside the file to a requested one. Fails if the file does not support
|
||||
/// seeking.
|
||||
fn seek(&self, from: SeekFrom) -> Result<u64, Error>;
|
||||
|
||||
/// Returns the position within the file, determined as an offset in bytes from the beginning
|
||||
/// of the file. Fails if the file does not support seeking or the "offset" is not defined for
|
||||
/// such type of nodes.
|
||||
fn tell(&self) -> Result<u64, Error> {
|
||||
self.seek(SeekFrom::Current(0))
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
arch::{aarch64::mem::table::L3, Architecture},
|
||||
fs::devfs,
|
||||
kernel_main, kernel_secondary_main,
|
||||
mem::{address::IntoRaw, phys, table::EntryLevel, PhysicalAddress, KERNEL_VIRT_OFFSET},
|
||||
task::runtime,
|
||||
@ -99,11 +100,7 @@ unsafe extern "C" fn __aarch64_bsp_upper_entry(dtb: PhysicalAddress) -> ! {
|
||||
|
||||
exception::init_exceptions();
|
||||
|
||||
// // Setup initrd
|
||||
// super::setup_initrd();
|
||||
|
||||
// XXX
|
||||
// devfs::init();
|
||||
devfs::init();
|
||||
|
||||
runtime::init_task_queue();
|
||||
|
||||
|
@ -27,6 +27,7 @@ use crate::{
|
||||
devtree::{self, DevTreeIndexPropExt, DevTreeNodeInfo, DeviceTree, FdtMemoryRegionIter},
|
||||
power::arm_psci::Psci,
|
||||
},
|
||||
fs::{Initrd, INITRD_DATA},
|
||||
mem::{
|
||||
address::{FromRaw, IntoRaw},
|
||||
device::RawDeviceMemoryMapping,
|
||||
@ -314,12 +315,11 @@ impl AArch64 {
|
||||
let data = unsafe { PhysicalRef::map_slice(initrd_start, len) };
|
||||
self.initrd.init(data);
|
||||
|
||||
// XXX
|
||||
// INITRD_DATA.init(Initrd {
|
||||
// phys_page_start: aligned_start,
|
||||
// phys_page_len: aligned_end - aligned_start,
|
||||
// data: self.initrd.get().as_ref(),
|
||||
// });
|
||||
INITRD_DATA.init(Initrd {
|
||||
phys_page_start: aligned_start,
|
||||
phys_page_len: aligned_end - aligned_start,
|
||||
data: self.initrd.get().as_ref(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -15,7 +15,7 @@ use device_api::{
|
||||
interrupt::{InterruptHandler, IpiDeliveryTarget},
|
||||
Device,
|
||||
};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use kernel_util::{sync::IrqSafeSpinlock, util::OneTimeInit};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
@ -27,7 +27,6 @@ use crate::{
|
||||
address::FromRaw, heap::GLOBAL_HEAP, pointer::PhysicalRef, read_memory, write_memory,
|
||||
PhysicalAddress,
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
|
||||
use super::intrinsics;
|
||||
|
@ -8,6 +8,7 @@ use device_api::{
|
||||
},
|
||||
Device,
|
||||
};
|
||||
use kernel_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
@ -17,7 +18,6 @@ use tock_registers::{
|
||||
use crate::{
|
||||
arch::x86_64::{acpi::AcpiAllocator, apic::local::BSP_APIC_ID, IrqNumber},
|
||||
mem::{address::FromRaw, device::DeviceMemoryIo, PhysicalAddress},
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
|
||||
use super::{APIC_EXTERNAL_OFFSET, MAX_EXTERNAL_VECTORS};
|
||||
|
@ -2,7 +2,7 @@
|
||||
use core::{ptr::null_mut, sync::atomic::Ordering};
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use kernel_util::{sync::IrqSafeSpinlock, util::OneTimeInit};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
|
||||
use crate::{
|
||||
@ -10,7 +10,6 @@ use crate::{
|
||||
x86_64::{cpuid, gdt, registers::MSR_IA32_KERNEL_GS_BASE, syscall},
|
||||
CpuMessage,
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
task::sched::CpuQueue,
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,7 @@ use device_api::{
|
||||
timer::MonotonicTimestampProviderDevice, Device,
|
||||
};
|
||||
use git_version::git_version;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use kernel_util::{sync::SpinFence, util::OneTimeInit};
|
||||
use yboot_proto::{v1::AvailableMemoryRegion, LoadProtocolV1};
|
||||
|
||||
mod acpi;
|
||||
@ -55,7 +55,6 @@ use crate::{
|
||||
table::EntryLevel,
|
||||
PhysicalAddress,
|
||||
},
|
||||
sync::SpinFence,
|
||||
};
|
||||
|
||||
use self::{
|
||||
|
@ -2,6 +2,7 @@ use core::time::Duration;
|
||||
|
||||
use abi::error::Error;
|
||||
use device_api::{interrupt::InterruptHandler, timer::MonotonicTimestampProviderDevice, Device};
|
||||
use kernel_util::sync::IrqSafeSpinlock;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
@ -11,7 +12,6 @@ use crate::{
|
||||
},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
task::runtime,
|
||||
};
|
||||
|
||||
|
@ -5,16 +5,14 @@ use device_api::{
|
||||
interrupt::InterruptHandler,
|
||||
Device,
|
||||
};
|
||||
use kernel_util::sync::IrqSafeSpinlock;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
x86_64::{
|
||||
intrinsics::{IoPort, IoPortAccess},
|
||||
IrqNumber,
|
||||
},
|
||||
Architecture, ARCHITECTURE,
|
||||
use crate::arch::{
|
||||
x86_64::{
|
||||
intrinsics::{IoPort, IoPortAccess},
|
||||
IrqNumber,
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
Architecture, ARCHITECTURE,
|
||||
};
|
||||
|
||||
use self::codeset::{CODE_SET_1_00, CODE_SET_1_00_SHIFT};
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Driver for x86 COM ports
|
||||
use abi::error::Error;
|
||||
use device_api::{serial::SerialDevice, Device};
|
||||
use kernel_util::sync::IrqSafeSpinlock;
|
||||
|
||||
use crate::{
|
||||
arch::x86_64::{
|
||||
@ -8,7 +9,6 @@ use crate::{
|
||||
IrqNumber,
|
||||
},
|
||||
debug::DebugSink,
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
|
||||
// Single port
|
||||
|
@ -5,6 +5,7 @@ use acpi_lib::mcfg::McfgEntry;
|
||||
use alloc::{rc::Rc, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
use device_api::Device;
|
||||
use kernel_util::sync::IrqSafeSpinlock;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
mod space;
|
||||
@ -13,10 +14,7 @@ pub use space::{
|
||||
ecam::PciEcam, PciConfigSpace, PciConfigurationSpace, PciLegacyConfigurationSpace,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
mem::{address::FromRaw, PhysicalAddress},
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
use crate::mem::{address::FromRaw, PhysicalAddress};
|
||||
|
||||
bitflags! {
|
||||
/// Command register of the PCI configuration space
|
||||
|
@ -8,6 +8,7 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use vfs::CharDevice;
|
||||
|
||||
use crate::{
|
||||
arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
|
||||
@ -18,6 +19,7 @@ use crate::{
|
||||
tty::{TtyContext, TtyDevice},
|
||||
},
|
||||
device_tree_driver,
|
||||
fs::devfs::{self, CharDeviceType},
|
||||
mem::{address::FromRaw, device::DeviceMemoryIo, PhysicalAddress},
|
||||
task::process::ProcessId,
|
||||
};
|
||||
@ -122,22 +124,38 @@ impl TtyDevice for Pl011 {
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
// match req {
|
||||
// &mut DeviceRequest::SetTerminalGroup(id) => {
|
||||
// self.set_signal_group(ProcessId::from(id));
|
||||
// Ok(())
|
||||
// }
|
||||
// DeviceRequest::SetTerminalOptions(config) => self.context.set_config(config),
|
||||
// DeviceRequest::GetTerminalOptions(config) => {
|
||||
// config.write(self.context.config());
|
||||
// Ok(())
|
||||
// }
|
||||
// _ => Err(Error::InvalidArgument),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl CharDevice for Pl011 {
|
||||
fn write(&self, data: &[u8]) -> Result<usize, Error> {
|
||||
self.line_write(data)
|
||||
}
|
||||
|
||||
fn read(&self, data: &mut [u8]) -> Result<usize, Error> {
|
||||
match block! {
|
||||
self.line_read(data).await
|
||||
} {
|
||||
Ok(res) => res,
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
match req {
|
||||
&mut DeviceRequest::SetTerminalGroup(id) => {
|
||||
self.set_signal_group(ProcessId::from(id));
|
||||
Ok(())
|
||||
}
|
||||
DeviceRequest::SetTerminalOptions(config) => self.context.set_config(config),
|
||||
DeviceRequest::GetTerminalOptions(config) => {
|
||||
config.write(self.context.config());
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Error::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for Pl011 {
|
||||
fn send(&self, byte: u8) -> Result<(), Error> {
|
||||
self.inner.get().lock().send_byte(byte)
|
||||
@ -190,7 +208,7 @@ impl Device for Pl011 {
|
||||
self.inner.init(IrqSafeSpinlock::new(inner));
|
||||
|
||||
debug::add_sink(self, LogLevel::Debug);
|
||||
// devfs::add_char_device(self, CharDeviceType::TtySerial)?;
|
||||
devfs::add_char_device(self, CharDeviceType::TtySerial)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ pub mod combined {
|
||||
io::{DeviceRequest, TerminalSize},
|
||||
};
|
||||
use device_api::{input::KeyboardConsumer, serial::SerialDevice};
|
||||
use vfs::CharDevice;
|
||||
// use vfs::CharDevice;
|
||||
|
||||
use crate::device::{
|
||||
@ -73,43 +74,40 @@ pub mod combined {
|
||||
}
|
||||
}
|
||||
|
||||
// impl CharDevice for CombinedTerminal {
|
||||
// fn read(&'static self, blocking: bool, data: &mut [u8]) -> Result<usize, Error> {
|
||||
// assert!(blocking);
|
||||
// match block! {
|
||||
// self.line_read(data).await
|
||||
// } {
|
||||
// Ok(res) => res,
|
||||
// Err(err) => Err(err),
|
||||
// }
|
||||
// // self.line_read(data)
|
||||
// }
|
||||
impl CharDevice for CombinedTerminal {
|
||||
fn read(&'static self, data: &mut [u8]) -> Result<usize, Error> {
|
||||
match block! {
|
||||
self.line_read(data).await
|
||||
} {
|
||||
Ok(res) => res,
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
// fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Error> {
|
||||
// assert!(blocking);
|
||||
// self.line_write(data)
|
||||
// }
|
||||
fn write(&self, data: &[u8]) -> Result<usize, Error> {
|
||||
self.line_write(data)
|
||||
}
|
||||
|
||||
// fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
// match req {
|
||||
// &mut DeviceRequest::SetTerminalGroup(id) => {
|
||||
// self.set_signal_group(ProcessId::from(id));
|
||||
// Ok(())
|
||||
// }
|
||||
// DeviceRequest::SetTerminalOptions(config) => self.context.set_config(config),
|
||||
// DeviceRequest::GetTerminalOptions(config) => {
|
||||
// config.write(self.context.config());
|
||||
// Ok(())
|
||||
// }
|
||||
// DeviceRequest::GetTerminalSize(out) => {
|
||||
// let (rows, columns) = self.output.text_dimensions();
|
||||
// out.write(TerminalSize { rows, columns });
|
||||
// Ok(())
|
||||
// }
|
||||
// _ => Err(Error::InvalidArgument),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
match req {
|
||||
&mut DeviceRequest::SetTerminalGroup(id) => {
|
||||
self.set_signal_group(ProcessId::from(id));
|
||||
Ok(())
|
||||
}
|
||||
DeviceRequest::SetTerminalOptions(config) => self.context.set_config(config),
|
||||
DeviceRequest::GetTerminalOptions(config) => {
|
||||
config.write(self.context.config());
|
||||
Ok(())
|
||||
}
|
||||
DeviceRequest::GetTerminalSize(out) => {
|
||||
let (rows, columns) = self.output.text_dimensions();
|
||||
out.write(TerminalSize { rows, columns });
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(Error::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fb_console")]
|
||||
|
120
src/fs/devfs.rs
120
src/fs/devfs.rs
@ -1,18 +1,15 @@
|
||||
//! Device virtual file system
|
||||
use core::{
|
||||
any::Any,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
use core::sync::atomic::{AtomicU32, AtomicU8, AtomicUsize, Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::{format, string::String, sync::Arc};
|
||||
use kernel_util::{sync::IrqSafeSpinlock, util::OneTimeInit};
|
||||
use vfs::{
|
||||
impls::{mdir, read_fn_node, MemoryDirectory},
|
||||
CharDevice, Node, NodeFlags, NodeRef,
|
||||
};
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{FileAttr, FileMode, FileType, OpenOptions},
|
||||
};
|
||||
use alloc::{boxed::Box, format, string::String};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use vfs::{
|
||||
CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE,
|
||||
};
|
||||
use crate::arch::{Architecture, ARCHITECTURE};
|
||||
|
||||
/// Describes the kind of a character device
|
||||
#[derive(Debug)]
|
||||
@ -23,34 +20,63 @@ pub enum CharDeviceType {
|
||||
TtySerial,
|
||||
}
|
||||
|
||||
struct DevfsDirectory;
|
||||
static DEVFS_ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
|
||||
|
||||
impl VnodeImpl for DevfsDirectory {
|
||||
fn open(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
Ok((DIR_POSITION_FROM_CACHE, None))
|
||||
struct RandomState {
|
||||
data: [u8; 1024],
|
||||
pos: usize,
|
||||
last_state: u32,
|
||||
}
|
||||
|
||||
impl RandomState {
|
||||
fn new(seed: u32) -> Self {
|
||||
Self {
|
||||
data: [0; 1024],
|
||||
pos: 1024,
|
||||
last_state: seed,
|
||||
}
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: 0,
|
||||
// TODO mutable directory mode
|
||||
mode: FileMode::from(0o755),
|
||||
ty: FileType::Directory,
|
||||
})
|
||||
fn next(&mut self) -> u32 {
|
||||
let mut x = self.last_state;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
self.last_state = x;
|
||||
x
|
||||
}
|
||||
|
||||
fn fill_buf(&mut self) {
|
||||
self.pos = 0;
|
||||
for i in (0..self.data.len()).step_by(4) {
|
||||
let v = self.next();
|
||||
self.data[i..i + 4].copy_from_slice(&v.to_ne_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, buf: &mut [u8]) {
|
||||
let mut rem = buf.len();
|
||||
let mut pos = 0;
|
||||
|
||||
while rem != 0 {
|
||||
if self.pos == 1024 {
|
||||
self.fill_buf();
|
||||
}
|
||||
let count = core::cmp::min(rem, 1024 - self.pos);
|
||||
|
||||
buf[pos..pos + count].copy_from_slice(&self.data[self.pos..self.pos + count]);
|
||||
|
||||
self.pos += count;
|
||||
rem -= count;
|
||||
pos += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DEVFS_ROOT: OneTimeInit<VnodeRef> = OneTimeInit::new();
|
||||
|
||||
/// Sets up the device filesystem
|
||||
pub fn init() {
|
||||
let node = Vnode::new("", VnodeKind::Directory);
|
||||
node.set_data(Box::new(DevfsDirectory));
|
||||
DEVFS_ROOT.init(node);
|
||||
let root = MemoryDirectory::empty();
|
||||
DEVFS_ROOT.init(root);
|
||||
}
|
||||
|
||||
/// Returns the root of the devfs.
|
||||
@ -58,19 +84,16 @@ pub fn init() {
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if the devfs hasn't yet been initialized.
|
||||
pub fn root() -> &'static VnodeRef {
|
||||
pub fn root() -> &'static NodeRef {
|
||||
DEVFS_ROOT.get()
|
||||
}
|
||||
|
||||
fn _add_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Error> {
|
||||
infoln!("Add char device: {}", name);
|
||||
|
||||
let node = Vnode::new(name, VnodeKind::Char);
|
||||
node.set_data(Box::new(CharDeviceWrapper::new(dev)));
|
||||
let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS);
|
||||
|
||||
DEVFS_ROOT.get().add_child(node);
|
||||
|
||||
Ok(())
|
||||
DEVFS_ROOT.get().add_child(name, node)
|
||||
}
|
||||
|
||||
/// Adds a character device to the devfs
|
||||
@ -88,3 +111,24 @@ pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Re
|
||||
|
||||
_add_char_device(dev, name)
|
||||
}
|
||||
|
||||
pub fn add_pseudo_devices() -> Result<(), Error> {
|
||||
let now = ARCHITECTURE
|
||||
.monotonic_timer()
|
||||
.monotonic_timestamp()
|
||||
.unwrap();
|
||||
let random_seed = now.subsec_millis();
|
||||
let mut random_state = RandomState::new(random_seed);
|
||||
random_state.fill_buf();
|
||||
let random_state = IrqSafeSpinlock::new(random_state);
|
||||
|
||||
let random = read_fn_node(move |_, buf| {
|
||||
random_state.lock().read_buf(buf);
|
||||
Ok(buf.len())
|
||||
});
|
||||
|
||||
let root = root();
|
||||
root.add_child("random", random)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use core::ptr::NonNull;
|
||||
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use memfs::block::{self, BlockAllocator};
|
||||
use vfs::VnodeRef;
|
||||
use vfs::NodeRef;
|
||||
use yggdrasil_abi::{error::Error, io::MountOptions};
|
||||
|
||||
use crate::mem::{phys, PhysicalAddress};
|
||||
@ -44,7 +44,7 @@ unsafe impl BlockAllocator for FileBlockAllocator {
|
||||
}
|
||||
|
||||
/// Constructs an instance of a filesystem for given set of [MountOptions]
|
||||
pub fn create_filesystem(options: &MountOptions) -> Result<VnodeRef, Error> {
|
||||
pub fn create_filesystem(options: &MountOptions) -> Result<NodeRef, Error> {
|
||||
let fs_name = options.filesystem.unwrap();
|
||||
|
||||
match fs_name {
|
||||
|
175
src/fs/sysfs.rs
175
src/fs/sysfs.rs
@ -10,161 +10,16 @@ use alloc::{
|
||||
};
|
||||
use git_version::git_version;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE};
|
||||
use vfs::{
|
||||
impls::{const_value_node, mdir, read_fn_node},
|
||||
NodeRef,
|
||||
};
|
||||
|
||||
use crate::{debug, util};
|
||||
|
||||
trait GetterFn<T: ToString> = Fn() -> Result<T, Error>;
|
||||
trait ReaderFn = Fn(u64, &mut [u8]) -> Result<usize, Error>;
|
||||
static ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
|
||||
|
||||
struct SysfsDirectory;
|
||||
|
||||
struct SysfsGetterNode<T: ToString, R: GetterFn<T>> {
|
||||
getter: R,
|
||||
_pd: PhantomData<T>,
|
||||
}
|
||||
|
||||
struct SysfsReaderNode<R: ReaderFn> {
|
||||
reader: R,
|
||||
}
|
||||
|
||||
impl<T: ToString, R: GetterFn<T>> SysfsGetterNode<T, R> {
|
||||
pub const fn new(getter: R) -> Self {
|
||||
Self {
|
||||
getter,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ReaderFn> SysfsReaderNode<R> {
|
||||
pub const fn new(reader: R) -> Self {
|
||||
Self { reader }
|
||||
}
|
||||
}
|
||||
|
||||
impl VnodeImpl for SysfsDirectory {
|
||||
fn open(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
Ok((DIR_POSITION_FROM_CACHE, None))
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: 0,
|
||||
// TODO mutable directory mode
|
||||
mode: FileMode::from(0o755),
|
||||
ty: FileType::Directory,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString, R: GetterFn<T>> VnodeImpl for SysfsGetterNode<T, R> {
|
||||
fn open(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
let value = (self.getter)()?;
|
||||
let inner = Box::new(value.to_string());
|
||||
Ok((0, Some(inner)))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
node: &VnodeRef,
|
||||
pos: u64,
|
||||
inner: Option<&mut Box<dyn Any>>,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
let string = inner.unwrap().downcast_ref::<String>().unwrap();
|
||||
let pos = pos as usize;
|
||||
let bytes = string.as_bytes();
|
||||
|
||||
if pos >= bytes.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let len = core::cmp::min(bytes.len() - pos, data.len());
|
||||
|
||||
data[..len].copy_from_slice(&bytes[pos..pos + len]);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn metadata(&self, node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: 0,
|
||||
mode: FileMode::from(0o444),
|
||||
ty: FileType::File,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ReaderFn> VnodeImpl for SysfsReaderNode<R> {
|
||||
fn open(
|
||||
&self,
|
||||
node: &VnodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
Ok((0, None))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
node: &VnodeRef,
|
||||
pos: u64,
|
||||
_inner: Option<&mut Box<dyn Any>>,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
(self.reader)(pos, data)
|
||||
}
|
||||
|
||||
fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn metadata(&self, node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
static ROOT: OneTimeInit<VnodeRef> = OneTimeInit::new();
|
||||
|
||||
fn getter<S: Into<String>, T: ToString + 'static, R: GetterFn<T> + 'static>(
|
||||
name: S,
|
||||
get: R,
|
||||
) -> VnodeRef {
|
||||
let data = Box::new(SysfsGetterNode::new(get));
|
||||
let node = Vnode::new(name, VnodeKind::Regular);
|
||||
node.set_data(data);
|
||||
node
|
||||
}
|
||||
|
||||
fn reader<S: Into<String>, R: ReaderFn + 'static>(name: S, read: R) -> VnodeRef {
|
||||
let data = Box::new(SysfsReaderNode::new(read));
|
||||
let node = Vnode::new(name, VnodeKind::Regular);
|
||||
node.set_data(data);
|
||||
node
|
||||
}
|
||||
|
||||
fn dir<S: Into<String>, I: IntoIterator<Item = VnodeRef>>(name: S, items: I) -> VnodeRef {
|
||||
let node = Vnode::new(name, VnodeKind::Directory);
|
||||
node.set_data(Box::new(SysfsDirectory));
|
||||
for item in items {
|
||||
node.add_child(item);
|
||||
}
|
||||
node
|
||||
}
|
||||
|
||||
pub fn root() -> &'static VnodeRef {
|
||||
pub fn root() -> &'static NodeRef {
|
||||
ROOT.get()
|
||||
}
|
||||
|
||||
@ -173,15 +28,15 @@ fn read_kernel_log(pos: u64, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
let kernel = dir(
|
||||
"kernel",
|
||||
[
|
||||
getter("version", || Ok(env!("CARGO_PKG_VERSION"))),
|
||||
getter("rev", || Ok(git_version!())),
|
||||
reader("log", read_kernel_log),
|
||||
],
|
||||
);
|
||||
let root = dir("", [kernel, getter("arch", || Ok(util::arch_str()))]);
|
||||
let d_kernel = mdir([
|
||||
("version", const_value_node(env!("CARGO_PKG_VERSION"))),
|
||||
("rev", const_value_node(git_version!())),
|
||||
("log", read_fn_node(read_kernel_log)),
|
||||
]);
|
||||
let root = mdir([
|
||||
("kernel", d_kernel),
|
||||
("arch", const_value_node(util::arch_str())),
|
||||
]);
|
||||
|
||||
ROOT.init(root);
|
||||
}
|
||||
|
84
src/init.rs
84
src/init.rs
@ -3,8 +3,19 @@ use abi::{
|
||||
error::Error,
|
||||
io::{OpenOptions, RawFd},
|
||||
};
|
||||
use memfs::MemoryFilesystem;
|
||||
use vfs::{Action, IoContext, NodeRef};
|
||||
|
||||
use crate::proc;
|
||||
use crate::{
|
||||
fs::{devfs, FileBlockAllocator, INITRD_DATA},
|
||||
proc,
|
||||
};
|
||||
|
||||
fn setup_root() -> Result<NodeRef, Error> {
|
||||
let initrd_data = INITRD_DATA.get();
|
||||
let fs = MemoryFilesystem::<FileBlockAllocator>::from_slice(initrd_data.data).unwrap();
|
||||
fs.root()
|
||||
}
|
||||
|
||||
/// Kernel's "main" process function.
|
||||
///
|
||||
@ -14,47 +25,50 @@ use crate::proc;
|
||||
/// initialization has finished.
|
||||
pub fn kinit() -> Result<(), Error> {
|
||||
infoln!("In main");
|
||||
loop {}
|
||||
|
||||
// #[cfg(feature = "fb_console")]
|
||||
// {
|
||||
// use crate::{device::display::console::update_consoles_task, task::runtime};
|
||||
#[cfg(feature = "fb_console")]
|
||||
{
|
||||
use crate::{device::display::console::update_consoles_task, task::runtime};
|
||||
|
||||
// runtime::spawn(async move {
|
||||
// update_consoles_task().await;
|
||||
// })?;
|
||||
// }
|
||||
runtime::spawn(async move {
|
||||
update_consoles_task().await;
|
||||
})?;
|
||||
}
|
||||
|
||||
// let root = setup_root()?;
|
||||
let root = setup_root()?;
|
||||
|
||||
// let ioctx = IoContext::new(root);
|
||||
// let node = ioctx.find(None, "/init", true, true)?;
|
||||
// let file = node.open(OpenOptions::READ)?;
|
||||
let mut ioctx = IoContext::new(root);
|
||||
let file = ioctx.open_exec(None, "/init")?;
|
||||
|
||||
// let devfs = devfs::root();
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// let console = ioctx.find(Some(devfs.clone()), "tty0", true, true)?;
|
||||
// #[cfg(target_arch = "aarch64")]
|
||||
// let console = ioctx.find(Some(devfs.clone()), "ttyS0", true, true)?;
|
||||
// let stdin = console.open(OpenOptions::READ)?;
|
||||
// let stdout = console.open(OpenOptions::WRITE)?;
|
||||
// let stderr = stdout.clone();
|
||||
let devfs = devfs::root();
|
||||
|
||||
// {
|
||||
// // XXX
|
||||
// let (user_init, user_init_main) =
|
||||
// proc::exec::load_elf("init", file, &["/init", "xxx"], &[])?;
|
||||
// let mut io = user_init.io.lock();
|
||||
// io.set_ioctx(ioctx);
|
||||
// io.set_file(RawFd::STDIN, stdin)?;
|
||||
// io.set_file(RawFd::STDOUT, stdout)?;
|
||||
// io.set_file(RawFd::STDERR, stderr)?;
|
||||
// drop(io);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let console = ioctx.find(Some(devfs.clone()), "tty0", true, true)?;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let console = ioctx.find(Some(devfs.clone()), "ttyS0", true, true)?;
|
||||
|
||||
// user_init.set_session_terminal(console);
|
||||
let access = ioctx.check_access(Action::Read, &console)?;
|
||||
let stdin = console.open(OpenOptions::READ, access)?;
|
||||
let access = ioctx.check_access(Action::Write, &console)?;
|
||||
let stdout = console.open(OpenOptions::WRITE, access)?;
|
||||
let stderr = stdout.clone();
|
||||
|
||||
// user_init_main.enqueue_somewhere();
|
||||
// }
|
||||
{
|
||||
// XXX
|
||||
let (user_init, user_init_main) =
|
||||
proc::exec::load_elf("init", file, &["/init", "xxx"], &[])?;
|
||||
|
||||
// Ok(())
|
||||
let mut io = user_init.io.lock();
|
||||
io.set_ioctx(ioctx);
|
||||
io.set_file(RawFd::STDIN, stdin)?;
|
||||
io.set_file(RawFd::STDOUT, stdout)?;
|
||||
io.set_file(RawFd::STDERR, stderr)?;
|
||||
drop(io);
|
||||
|
||||
user_init.set_session_terminal(console);
|
||||
|
||||
user_init_main.enqueue_somewhere();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ use kernel_util::sync::SpinFence;
|
||||
|
||||
use crate::{
|
||||
arch::{ArchitectureImpl, ARCHITECTURE},
|
||||
fs::{devfs, sysfs},
|
||||
mem::heap,
|
||||
task::{spawn_kernel_closure, Cpu},
|
||||
};
|
||||
@ -50,7 +51,7 @@ pub mod debug;
|
||||
pub mod arch;
|
||||
|
||||
pub mod device;
|
||||
// pub mod fs;
|
||||
pub mod fs;
|
||||
pub mod init;
|
||||
pub mod mem;
|
||||
pub mod panic;
|
||||
@ -87,7 +88,8 @@ pub fn kernel_main() -> ! {
|
||||
debugln!("Heap: {:#x?}", heap::heap_range());
|
||||
|
||||
// Setup the sysfs
|
||||
// sysfs::init();
|
||||
sysfs::init();
|
||||
devfs::add_pseudo_devices().unwrap();
|
||||
|
||||
unsafe {
|
||||
ARCHITECTURE.start_application_processors();
|
||||
|
@ -27,15 +27,11 @@ struct FileReader<'a> {
|
||||
|
||||
impl elf::io_traits::InputStream for FileReader<'_> {
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), elf::io_traits::StreamError> {
|
||||
self.file
|
||||
.borrow_mut()
|
||||
.read_exact(buf)
|
||||
.map_err(conv_stream_error)
|
||||
self.file.read_exact(buf).map_err(conv_stream_error)
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: elf::io_traits::SeekFrom) -> Result<u64, elf::io_traits::StreamError> {
|
||||
self.file
|
||||
.borrow_mut()
|
||||
.seek(conv_seek_from(pos))
|
||||
.map_err(conv_stream_error)
|
||||
}
|
||||
@ -179,9 +175,9 @@ fn load_segment(
|
||||
space,
|
||||
phdr.p_vaddr as usize,
|
||||
|off, mut dst| {
|
||||
let mut source = file.file.borrow_mut();
|
||||
source.seek(SeekFrom::Start(phdr.p_offset + off as u64))?;
|
||||
source.read_exact(dst.deref_mut())
|
||||
file.file
|
||||
.seek(SeekFrom::Start(phdr.p_offset + off as u64))?;
|
||||
file.file.read_exact(dst.deref_mut())
|
||||
},
|
||||
phdr.p_filesz as usize,
|
||||
)?;
|
||||
@ -247,9 +243,9 @@ fn tls_segment(
|
||||
space,
|
||||
base_address + data_offset,
|
||||
|off, mut dst| {
|
||||
let mut source = file.file.borrow_mut();
|
||||
source.seek(SeekFrom::Start(phdr.p_offset + off as u64))?;
|
||||
source.read_exact(dst.deref_mut())
|
||||
file.file
|
||||
.seek(SeekFrom::Start(phdr.p_offset + off as u64))?;
|
||||
file.file.read_exact(dst.deref_mut())
|
||||
},
|
||||
data_size,
|
||||
)?;
|
||||
|
@ -7,6 +7,7 @@ use abi::{
|
||||
process::ProgramArgumentInner,
|
||||
};
|
||||
use alloc::{string::String, sync::Arc};
|
||||
use vfs::FileRef;
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
@ -145,8 +146,7 @@ fn setup_binary<S: Into<String>>(
|
||||
}
|
||||
}
|
||||
|
||||
// XXX
|
||||
let tls_address = 0; // proc::elf::clone_tls(&space, &image)?;
|
||||
let tls_address = proc::elf::clone_tls(&space, &image)?;
|
||||
|
||||
let context = TaskContext::user(
|
||||
image.entry,
|
||||
@ -165,15 +165,15 @@ fn setup_binary<S: Into<String>>(
|
||||
// Ok(Process::new_with_context(name, Some(space), context))
|
||||
}
|
||||
|
||||
// /// Loads an ELF bianary from `file` and sets up all the necessary data/argument memory
|
||||
// pub fn load_elf<S: Into<String>>(
|
||||
// name: S,
|
||||
// file: FileRef,
|
||||
// args: &[&str],
|
||||
// envs: &[&str],
|
||||
// ) -> Result<(Arc<Process>, Arc<Thread>), Error> {
|
||||
// let space = ProcessAddressSpace::new()?;
|
||||
// let image = proc::elf::load_elf_from_file(&space, file)?;
|
||||
//
|
||||
// setup_binary(name, space, image, args, envs)
|
||||
// }
|
||||
/// Loads an ELF bianary from `file` and sets up all the necessary data/argument memory
|
||||
pub fn load_elf<S: Into<String>>(
|
||||
name: S,
|
||||
file: FileRef,
|
||||
args: &[&str],
|
||||
envs: &[&str],
|
||||
) -> Result<(Arc<Process>, Arc<Thread>), Error> {
|
||||
let space = ProcessAddressSpace::new()?;
|
||||
let image = proc::elf::load_elf_from_file(&space, file)?;
|
||||
|
||||
setup_binary(name, space, image, args, envs)
|
||||
}
|
||||
|
@ -1,12 +1,67 @@
|
||||
//! Process I/O management
|
||||
|
||||
use abi::{error::Error, io::RawFd};
|
||||
use alloc::collections::{btree_map::Entry, BTreeMap};
|
||||
use kernel_util::sync::IrqSafeSpinlock;
|
||||
use vfs::{FileRef, IoContext};
|
||||
|
||||
/// I/O context of a process, contains information like root, current directory and file
|
||||
/// descriptor table
|
||||
pub struct ProcessIo {}
|
||||
pub struct ProcessIo {
|
||||
ioctx: Option<IoContext>,
|
||||
files: BTreeMap<RawFd, FileRef>,
|
||||
}
|
||||
|
||||
impl ProcessIo {
|
||||
/// Constructs an uninitialized I/O context
|
||||
pub fn new() -> Self {
|
||||
todo!()
|
||||
Self {
|
||||
ioctx: None,
|
||||
files: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(&self, fd: RawFd) -> Result<&FileRef, Error> {
|
||||
self.files.get(&fd).ok_or(Error::InvalidFile)
|
||||
}
|
||||
|
||||
pub fn ioctx(&mut self) -> &mut IoContext {
|
||||
self.ioctx.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn set_ioctx(&mut self, ioctx: IoContext) {
|
||||
self.ioctx = Some(ioctx);
|
||||
}
|
||||
|
||||
pub fn set_file(&mut self, fd: RawFd, file: FileRef) -> Result<(), Error> {
|
||||
if self.files.contains_key(&fd) {
|
||||
return Err(Error::AlreadyExists);
|
||||
}
|
||||
self.files.insert(fd, file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn place_file(&mut self, file: FileRef) -> Result<RawFd, Error> {
|
||||
for idx in 0..64 {
|
||||
let fd = RawFd::from(idx);
|
||||
|
||||
if let Entry::Vacant(e) = self.files.entry(fd) {
|
||||
e.insert(file);
|
||||
return Ok(fd);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO OutOfFiles
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
pub fn close_file(&mut self, fd: RawFd) -> Result<(), Error> {
|
||||
// Do nothing, file will be dropped and closed
|
||||
self.files.remove(&fd).ok_or(Error::InvalidFile)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn retain<F: Fn(&RawFd, &mut FileRef) -> bool>(&mut self, predicate: F) {
|
||||
self.files.retain(predicate);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Internal management for processes
|
||||
|
||||
// pub mod elf;
|
||||
pub mod elf;
|
||||
pub mod exec;
|
||||
pub mod io;
|
||||
|
@ -4,11 +4,13 @@ use core::{mem::MaybeUninit, time::Duration};
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{DeviceRequest, DirectoryEntry, FileAttr, FileMode, OpenOptions, RawFd, SeekFrom},
|
||||
path::Path,
|
||||
process::{ExitCode, MutexOperation, Signal, SpawnOption, SpawnOptions, ThreadSpawnOptions},
|
||||
syscall::SyscallFunction,
|
||||
};
|
||||
use alloc::rc::Rc;
|
||||
use alloc::{rc::Rc, sync::Arc};
|
||||
use kernel_util::sync::IrqSafeSpinlockGuard;
|
||||
use vfs::{CreateInfo, IoContext, NodeRef, Read, Seek, Write};
|
||||
// use vfs::{IoContext, Read, ReadDirectory, Seek, VnodeKind, VnodeRef, Write};
|
||||
use yggdrasil_abi::{
|
||||
error::SyscallResult,
|
||||
@ -18,6 +20,7 @@ use yggdrasil_abi::{
|
||||
use crate::{
|
||||
block,
|
||||
debug::LogLevel,
|
||||
fs,
|
||||
mem::{phys, table::MapAttributes},
|
||||
proc::{self, io::ProcessIo},
|
||||
task::{
|
||||
@ -30,26 +33,29 @@ use crate::{
|
||||
mod arg;
|
||||
use arg::*;
|
||||
|
||||
// fn run_with_io<T, F: FnOnce(IrqSafeSpinlockGuard<ProcessIo>) -> T>(proc: &Process, f: F) -> T {
|
||||
// let io = proc.io.lock();
|
||||
// f(io)
|
||||
// }
|
||||
//
|
||||
// fn run_with_io_at<
|
||||
// T,
|
||||
// F: FnOnce(Option<VnodeRef>, IrqSafeSpinlockGuard<ProcessIo>) -> Result<T, Error>,
|
||||
// >(
|
||||
// proc: &Process,
|
||||
// at: Option<RawFd>,
|
||||
// f: F,
|
||||
// ) -> Result<T, Error> {
|
||||
// let io = proc.io.lock();
|
||||
// let at = at
|
||||
// .map(|fd| io.file(fd).and_then(|f| f.borrow().node()))
|
||||
// .transpose()?;
|
||||
//
|
||||
// f(at, io)
|
||||
// }
|
||||
fn run_with_io<T, F: FnOnce(IrqSafeSpinlockGuard<ProcessIo>) -> T>(proc: &Process, f: F) -> T {
|
||||
let io = proc.io.lock();
|
||||
f(io)
|
||||
}
|
||||
|
||||
fn run_with_io_at<
|
||||
T,
|
||||
F: FnOnce(Option<NodeRef>, IrqSafeSpinlockGuard<ProcessIo>) -> Result<T, Error>,
|
||||
>(
|
||||
proc: &Process,
|
||||
at: Option<RawFd>,
|
||||
f: F,
|
||||
) -> Result<T, Error> {
|
||||
let io = proc.io.lock();
|
||||
let at = at
|
||||
.map(|fd| {
|
||||
io.file(fd)
|
||||
.and_then(|f| f.node().ok_or(Error::InvalidFile).cloned())
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
f(at, io)
|
||||
}
|
||||
|
||||
fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error> {
|
||||
let thread = Thread::current();
|
||||
@ -118,7 +124,206 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
|
||||
Ok(0)
|
||||
}
|
||||
// I/O
|
||||
SyscallFunction::Open => {
|
||||
let at = arg_option_fd(args[0] as u32);
|
||||
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
|
||||
let opts = OpenOptions::from(args[3] as u32);
|
||||
let mode = FileMode::from(args[4] as u32);
|
||||
|
||||
run_with_io_at(&process, at, |at, mut io| {
|
||||
let file = io.ioctx().open(at, path, opts, mode)?;
|
||||
|
||||
// TODO NO_CTTY?
|
||||
if process.session_terminal().is_none() &&
|
||||
let Some(node) = file.node() && node.is_terminal() {
|
||||
debugln!("Session terminal set for #{}: {}", process.id(), path);
|
||||
process.set_session_terminal(node.clone());
|
||||
}
|
||||
|
||||
let fd = io.place_file(file)?;
|
||||
Ok(fd.0 as usize)
|
||||
})
|
||||
}
|
||||
SyscallFunction::OpenDirectory => {
|
||||
let at = arg_option_fd(args[0] as u32);
|
||||
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
|
||||
|
||||
run_with_io_at(&process, at, |at, mut io| {
|
||||
let node = io.ioctx().find(at, path, true, true)?;
|
||||
let access = io.ioctx().check_access(vfs::Action::Read, &node)?;
|
||||
let file = node.open_directory(access)?;
|
||||
let fd = io.place_file(file)?;
|
||||
|
||||
Ok(fd.0 as usize)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Read => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
let data = arg_buffer_mut(args[1] as _, args[2] as _)?;
|
||||
|
||||
run_with_io(&process, |io| io.file(fd)?.read(data))
|
||||
}
|
||||
SyscallFunction::Write => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
let data = arg_buffer_ref(args[1] as _, args[2] as _)?;
|
||||
|
||||
run_with_io(&process, |io| io.file(fd)?.write(data))
|
||||
}
|
||||
SyscallFunction::Seek => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
let pos = SeekFrom::from(args[1]);
|
||||
|
||||
run_with_io(&process, |io| io.file(fd)?.seek(pos).map(|v| v as usize))
|
||||
}
|
||||
SyscallFunction::ReadDirectory => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
let buffer = arg_user_slice_mut::<MaybeUninit<DirectoryEntry>>(
|
||||
args[1] as usize,
|
||||
args[2] as usize,
|
||||
)?;
|
||||
|
||||
run_with_io(&process, |io| io.file(fd)?.read_dir(buffer))
|
||||
}
|
||||
SyscallFunction::Close => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
|
||||
run_with_io(&process, |mut io| {
|
||||
io.close_file(fd)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Mount => {
|
||||
let options = arg_user_ref::<MountOptions>(args[0] as usize)?;
|
||||
|
||||
run_with_io(&process, |mut io| {
|
||||
let fs_root = fs::create_filesystem(options)?;
|
||||
io.ioctx().mount(options.target, fs_root)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Unmount => {
|
||||
todo!();
|
||||
}
|
||||
SyscallFunction::DeviceRequest => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
let req = arg_user_mut::<DeviceRequest>(args[1] as usize)?;
|
||||
|
||||
run_with_io(&process, |io| {
|
||||
let file = io.file(fd)?;
|
||||
let node = file.node().ok_or(Error::InvalidFile)?;
|
||||
node.device_request(req)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::GetMetadata => {
|
||||
let at = arg_option_fd(args[0] as u32);
|
||||
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
|
||||
let buffer = arg_user_mut::<MaybeUninit<FileAttr>>(args[3] as usize)?;
|
||||
let follow = args[4] != 0;
|
||||
|
||||
run_with_io_at(&process, at, |at, mut io| {
|
||||
let node = if path.is_empty() {
|
||||
at.ok_or(Error::InvalidArgument)?
|
||||
} else {
|
||||
io.ioctx().find(None, path, follow, true)?
|
||||
};
|
||||
|
||||
let metadata = node.metadata()?;
|
||||
let size = node.size()?;
|
||||
|
||||
buffer.write(FileAttr {
|
||||
size,
|
||||
ty: node.ty(),
|
||||
mode: metadata.mode,
|
||||
});
|
||||
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::CreateDirectory => {
|
||||
let at = arg_option_fd(args[0] as u32);
|
||||
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
|
||||
let mode = FileMode::from(args[3] as u32);
|
||||
|
||||
run_with_io_at(&process, at, |at, mut io| {
|
||||
io.ioctx().create_directory(at, path, mode)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Remove => {
|
||||
let at = arg_option_fd(args[0] as u32);
|
||||
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
|
||||
let recurse = args[3] != 0;
|
||||
|
||||
run_with_io_at(&process, at, |at, mut io| {
|
||||
io.ioctx().remove_file(at, path)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::RemoveDirectory => {
|
||||
todo!()
|
||||
}
|
||||
// Process management
|
||||
SyscallFunction::SpawnProcess => {
|
||||
let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?;
|
||||
|
||||
run_with_io(&process, |mut io| {
|
||||
// let node = io.ioctx().find(None, options.program, true, true)?;
|
||||
|
||||
// Setup a new process from the file
|
||||
let file = io.ioctx().open_exec(None, options.program)?;
|
||||
// let file = node.open(OpenOptions::READ)?;
|
||||
let (child_process, child_main) = proc::exec::load_elf(
|
||||
options.program,
|
||||
file,
|
||||
options.arguments,
|
||||
options.environment,
|
||||
)?;
|
||||
let pid: u32 = child_process.id().into();
|
||||
|
||||
// Inherit group and session from the creator
|
||||
child_process.inherit(&process)?;
|
||||
|
||||
// Inherit root from the creator
|
||||
// let child_ioctx = IoContext::new(io.ioctx().root().clone());
|
||||
let child_ioctx = IoContext::inherit(io.ioctx());
|
||||
let mut child_io = child_process.io.lock();
|
||||
child_io.set_ioctx(child_ioctx);
|
||||
|
||||
for opt in options.optional {
|
||||
match opt {
|
||||
&SpawnOption::InheritFile { source, child } => {
|
||||
let src_file = io.file(source)?;
|
||||
child_io.set_file(child, src_file.clone())?;
|
||||
}
|
||||
&SpawnOption::SetProcessGroup(pgroup) => {
|
||||
child_process.set_group_id(pgroup.into());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(fd) = options.optional.iter().find_map(|item| {
|
||||
if let &SpawnOption::GainTerminal(fd) = item {
|
||||
Some(fd)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
debugln!("{} requested terminal {:?}", pid, fd);
|
||||
let file = child_io.file(fd)?;
|
||||
let node = file.node().ok_or(Error::InvalidFile)?;
|
||||
let mut req = DeviceRequest::SetTerminalGroup(child_process.group_id().into());
|
||||
|
||||
node.device_request(&mut req)?;
|
||||
}
|
||||
|
||||
drop(child_io);
|
||||
child_main.enqueue_somewhere();
|
||||
|
||||
Ok(pid as _)
|
||||
})
|
||||
}
|
||||
SyscallFunction::SpawnThread => {
|
||||
let options = arg_user_ref::<ThreadSpawnOptions>(args[0] as usize)?;
|
||||
let id = process.spawn_thread(options)?;
|
||||
@ -180,6 +385,25 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
|
||||
process.set_group_id(group_id);
|
||||
Ok(0)
|
||||
}
|
||||
SyscallFunction::StartSession => {
|
||||
let session_terminal = process.clear_session_terminal();
|
||||
|
||||
if let Some(ctty) = session_terminal {
|
||||
// Drop all FDs referring to the old session terminal
|
||||
run_with_io(&process, |mut io| {
|
||||
io.retain(|_, f| {
|
||||
f.node()
|
||||
.map(|node| !Arc::ptr_eq(&node, &ctty))
|
||||
.unwrap_or(true)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
process.set_session_id(process.id());
|
||||
process.set_group_id(process.id());
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
// Waiting and polling
|
||||
SyscallFunction::WaitProcess => {
|
||||
let pid = ProcessId::from(args[0] as u32);
|
||||
@ -197,10 +421,6 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
_ => {
|
||||
todo!("System call: {:?}", func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ use alloc::{
|
||||
};
|
||||
use futures_util::Future;
|
||||
use kernel_util::{sync::IrqSafeSpinlock, util::OneTimeInit};
|
||||
use vfs::NodeRef;
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
@ -129,7 +130,7 @@ struct ProcessInner {
|
||||
session_id: ProcessId,
|
||||
group_id: ProcessId,
|
||||
|
||||
// session_terminal: Option<VnodeRef>,
|
||||
session_terminal: Option<NodeRef>,
|
||||
threads: Vec<Arc<Thread>>,
|
||||
mutexes: BTreeMap<usize, Arc<UserspaceMutex>>,
|
||||
}
|
||||
@ -170,7 +171,7 @@ impl Process {
|
||||
state: ProcessState::Running,
|
||||
session_id: id,
|
||||
group_id: id,
|
||||
// session_terminal: None,
|
||||
session_terminal: None,
|
||||
threads: Vec::new(),
|
||||
mutexes: BTreeMap::new(),
|
||||
}),
|
||||
@ -245,28 +246,28 @@ impl Process {
|
||||
}
|
||||
|
||||
// Resources
|
||||
// pub fn session_terminal(&self) -> Option<VnodeRef> {
|
||||
// self.inner.lock().session_terminal.clone()
|
||||
// }
|
||||
pub fn session_terminal(&self) -> Option<NodeRef> {
|
||||
self.inner.lock().session_terminal.clone()
|
||||
}
|
||||
|
||||
// pub fn set_session_terminal(&self, node: VnodeRef) {
|
||||
// self.inner.lock().session_terminal.replace(node);
|
||||
// }
|
||||
pub fn set_session_terminal(&self, node: NodeRef) {
|
||||
self.inner.lock().session_terminal.replace(node);
|
||||
}
|
||||
|
||||
// pub fn clear_session_terminal(&self) -> Option<VnodeRef> {
|
||||
// self.inner.lock().session_terminal.take()
|
||||
// }
|
||||
pub fn clear_session_terminal(&self) -> Option<NodeRef> {
|
||||
self.inner.lock().session_terminal.take()
|
||||
}
|
||||
|
||||
// pub fn inherit(&self, parent: &Process) -> Result<(), Error> {
|
||||
// let mut our_inner = self.inner.lock();
|
||||
// let their_inner = parent.inner.lock();
|
||||
pub fn inherit(&self, parent: &Process) -> Result<(), Error> {
|
||||
let mut our_inner = self.inner.lock();
|
||||
let their_inner = parent.inner.lock();
|
||||
|
||||
// our_inner.session_id = their_inner.session_id;
|
||||
// our_inner.group_id = their_inner.group_id;
|
||||
// our_inner.session_terminal = their_inner.session_terminal.clone();
|
||||
our_inner.session_id = their_inner.session_id;
|
||||
our_inner.group_id = their_inner.group_id;
|
||||
our_inner.session_terminal = their_inner.session_terminal.clone();
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// State
|
||||
pub fn get_exit_status(&self) -> Option<ExitCode> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user