fs: remove old vfs
This commit is contained in:
parent
6eef11a4e4
commit
cc816920b0
@ -1,348 +1,348 @@
|
||||
//! 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
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// #![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
|
||||
// // ));
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
|
@ -6,7 +6,3 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||
kernel-util = { path = "../kernel-util" }
|
||||
bitflags = "2.3.3"
|
||||
log = "0.4.20"
|
||||
|
@ -1,6 +0,0 @@
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub trait BlockDevice {
|
||||
fn read(&self, pos: usize, buf: &mut [u8]) -> Result<(), Error>;
|
||||
fn write(&self, pos: usize, buf: &[u8]) -> Result<(), Error>;
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{DeviceRequest, FileAttr, FileMode, FileType, OpenOptions},
|
||||
};
|
||||
|
||||
use crate::{node::VnodeImpl, VnodeRef};
|
||||
|
||||
pub trait CharDevice {
|
||||
fn read(&'static self, blocking: bool, data: &mut [u8]) -> Result<usize, Error>;
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Error>;
|
||||
|
||||
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct CharDeviceWrapper {
|
||||
device: &'static dyn CharDevice,
|
||||
}
|
||||
|
||||
impl CharDeviceWrapper {
|
||||
pub const fn new(device: &'static dyn CharDevice) -> Self {
|
||||
Self { device }
|
||||
}
|
||||
}
|
||||
|
||||
impl VnodeImpl for CharDeviceWrapper {
|
||||
fn open(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
Ok((0, None))
|
||||
}
|
||||
|
||||
fn close(&self, _node: &VnodeRef) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_node: &VnodeRef,
|
||||
_pos: u64,
|
||||
_inner: Option<&mut Box<dyn Any>>,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
self.device.read(true, data)
|
||||
}
|
||||
|
||||
fn write(&self, _node: &VnodeRef, _pos: u64, data: &[u8]) -> Result<usize, Error> {
|
||||
self.device.write(true, data)
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: 0,
|
||||
ty: FileType::Char,
|
||||
mode: FileMode::default_file(),
|
||||
})
|
||||
}
|
||||
|
||||
fn device_request(&self, _node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
self.device.device_request(req)
|
||||
}
|
||||
}
|
@ -1,224 +0,0 @@
|
||||
use core::{any::Any, cell::RefCell, mem::MaybeUninit};
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use bitflags::bitflags;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{DirectoryEntry, FileType},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
node::{VnodeKind, VnodeRef},
|
||||
Read, ReadDirectory, Seek, SeekFrom, Write, DIR_POSITION_FROM_CACHE,
|
||||
};
|
||||
|
||||
bitflags! {
|
||||
pub struct FileFlags: u32 {
|
||||
const READ = 1 << 0;
|
||||
const WRITE = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub type FileRef = Rc<RefCell<File>>;
|
||||
|
||||
enum DirectoryPosition {
|
||||
// TODO not the best implementation, but at least somewhat safe?
|
||||
CachePosition(usize),
|
||||
Dot,
|
||||
DotDot,
|
||||
#[allow(dead_code)]
|
||||
DiskPosition(u64),
|
||||
#[allow(dead_code)]
|
||||
End,
|
||||
}
|
||||
|
||||
pub struct NormalFile {
|
||||
vnode: VnodeRef,
|
||||
data: Option<Box<dyn Any>>,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
pub struct Directory {
|
||||
vnode: VnodeRef,
|
||||
pos: DirectoryPosition,
|
||||
}
|
||||
|
||||
pub enum FileInner {
|
||||
Normal(NormalFile),
|
||||
Directory(Directory),
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
inner: FileInner,
|
||||
flags: FileFlags,
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn normal(
|
||||
vnode: VnodeRef,
|
||||
pos: u64,
|
||||
data: Option<Box<dyn Any>>,
|
||||
flags: FileFlags,
|
||||
) -> FileRef {
|
||||
Rc::new(RefCell::new(Self {
|
||||
inner: FileInner::Normal(NormalFile { vnode, pos, data }),
|
||||
flags,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn directory(vnode: VnodeRef, pos: u64) -> FileRef {
|
||||
let pos = if pos == DIR_POSITION_FROM_CACHE {
|
||||
// Read from cache
|
||||
DirectoryPosition::Dot
|
||||
} else {
|
||||
// Reading from a "physical" directory
|
||||
todo!()
|
||||
};
|
||||
Rc::new(RefCell::new(Self {
|
||||
inner: FileInner::Directory(Directory { vnode, pos }),
|
||||
flags: FileFlags::READ,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn node(&self) -> Result<VnodeRef, Error> {
|
||||
match &self.inner {
|
||||
FileInner::Normal(inner) => Ok(inner.vnode.clone()),
|
||||
FileInner::Directory(inner) => Ok(inner.vnode.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for File {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
|
||||
if !self.flags.contains(FileFlags::WRITE) {
|
||||
panic!();
|
||||
}
|
||||
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.write(inner.pos, data)?;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count as u64;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
FileInner::Directory(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for File {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
|
||||
if !self.flags.contains(FileFlags::READ) {
|
||||
panic!();
|
||||
}
|
||||
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.read(inner.pos, inner.data.as_mut(), data)?;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count as u64;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
FileInner::Directory(_) => Err(Error::IsADirectory),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for File {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error> {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
// TODO check if the file is actually seekable
|
||||
|
||||
let size = inner.vnode.size()?;
|
||||
let pos = match pos {
|
||||
SeekFrom::Start(offset) => {
|
||||
if offset > size {
|
||||
todo!();
|
||||
}
|
||||
offset
|
||||
}
|
||||
SeekFrom::End(0) => size,
|
||||
_ => todo!(),
|
||||
};
|
||||
inner.pos = pos;
|
||||
|
||||
Ok(pos)
|
||||
}
|
||||
FileInner::Directory(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadDirectory for File {
|
||||
fn read_dir_entries(
|
||||
&mut self,
|
||||
entries: &mut [MaybeUninit<DirectoryEntry>],
|
||||
) -> Result<usize, Error> {
|
||||
let FileInner::Directory(inner) = &mut self.inner else {
|
||||
return Err(Error::NotADirectory);
|
||||
};
|
||||
|
||||
let mut nread = 0;
|
||||
let mut rem = entries.len();
|
||||
|
||||
while rem != 0 {
|
||||
let mut entry = DirectoryEntry {
|
||||
name: [0; 256],
|
||||
ty: FileType::File,
|
||||
};
|
||||
|
||||
let next_position = match inner.pos {
|
||||
DirectoryPosition::End => {
|
||||
break;
|
||||
}
|
||||
DirectoryPosition::Dot => {
|
||||
entry.name[0] = b'.';
|
||||
entry.ty = FileType::Directory;
|
||||
DirectoryPosition::DotDot
|
||||
}
|
||||
DirectoryPosition::DotDot => {
|
||||
entry.name[..2].copy_from_slice(b"..");
|
||||
entry.ty = FileType::Directory;
|
||||
DirectoryPosition::CachePosition(0)
|
||||
}
|
||||
DirectoryPosition::CachePosition(index) => {
|
||||
let child = inner.vnode.child_at(index);
|
||||
|
||||
if let Some(child) = child {
|
||||
let child_name = child.name();
|
||||
entry.name[..child_name.len()].copy_from_slice(child_name.as_bytes());
|
||||
entry.ty = FileType::from(child.kind());
|
||||
DirectoryPosition::CachePosition(index + 1)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
DirectoryPosition::DiskPosition(_) => todo!(),
|
||||
};
|
||||
inner.pos = next_position;
|
||||
|
||||
entries[nread].write(entry);
|
||||
|
||||
nread += 1;
|
||||
rem -= 1;
|
||||
}
|
||||
|
||||
Ok(nread)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
fn drop(&mut self) {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
inner.vnode.close().ok();
|
||||
}
|
||||
FileInner::Directory(inner) => {
|
||||
inner.vnode.close().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
use core::{any::Any, cell::Ref};
|
||||
|
||||
use alloc::rc::Rc;
|
||||
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{block::BlockDevice, node::VnodeRef};
|
||||
|
||||
pub trait Filesystem {
|
||||
fn root(self: Rc<Self>) -> Result<VnodeRef, Error>;
|
||||
|
||||
fn dev(self: Rc<Self>) -> Option<&'static dyn BlockDevice>;
|
||||
|
||||
fn data(&self) -> Option<Ref<dyn Any>>;
|
||||
}
|
@ -1,274 +0,0 @@
|
||||
use log::trace;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{FileMode, OpenOptions},
|
||||
path,
|
||||
};
|
||||
|
||||
use crate::{file::FileRef, node::VnodeRef};
|
||||
|
||||
pub struct IoContext {
|
||||
root: VnodeRef,
|
||||
cwd: VnodeRef,
|
||||
}
|
||||
|
||||
impl IoContext {
|
||||
pub fn new(root: VnodeRef) -> Self {
|
||||
Self {
|
||||
cwd: root.clone(),
|
||||
root,
|
||||
}
|
||||
}
|
||||
|
||||
fn _find(
|
||||
mut at: VnodeRef,
|
||||
path: &str,
|
||||
follow: bool,
|
||||
follow_mount: bool,
|
||||
) -> Result<VnodeRef, Error> {
|
||||
let mut element;
|
||||
let mut rest = path;
|
||||
|
||||
loop {
|
||||
(element, rest) = path::split_left(rest);
|
||||
|
||||
if !at.is_directory() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
match element {
|
||||
path::PARENT_NAME => {
|
||||
at = at.parent();
|
||||
}
|
||||
path::SELF_NAME => {}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
if follow || follow_mount {
|
||||
while let Some(target) = at.target() {
|
||||
assert!(at.is_mountpoint());
|
||||
if at.is_mountpoint() && !follow_mount {
|
||||
break;
|
||||
}
|
||||
|
||||
trace!("resolve parent: {:?} -> {:?}", at, target);
|
||||
at = target;
|
||||
}
|
||||
}
|
||||
|
||||
if !at.is_directory() {
|
||||
return Err(Error::NotADirectory);
|
||||
}
|
||||
|
||||
if element.is_empty() && rest.is_empty() {
|
||||
return Ok(at);
|
||||
}
|
||||
|
||||
let mut node = at.lookup_or_load(element)?;
|
||||
|
||||
if follow || follow_mount {
|
||||
while let Some(target) = node.target() {
|
||||
assert!(node.is_mountpoint());
|
||||
if node.is_mountpoint() && !follow_mount {
|
||||
break;
|
||||
}
|
||||
|
||||
trace!("resolve node: {:?} -> {:?}", node, target);
|
||||
node = target;
|
||||
}
|
||||
}
|
||||
|
||||
if rest.is_empty() {
|
||||
Ok(node)
|
||||
} else {
|
||||
Self::_find(node, rest, follow, follow_mount)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find(
|
||||
&self,
|
||||
at: Option<VnodeRef>,
|
||||
mut path: &str,
|
||||
follow: bool,
|
||||
follow_mount: bool,
|
||||
) -> Result<VnodeRef, Error> {
|
||||
trace!("find {:?} in {:?}", path, at);
|
||||
|
||||
let at = if path.starts_with('/') {
|
||||
path = path.trim_start_matches('/');
|
||||
self.root.clone()
|
||||
} else if let Some(at) = at {
|
||||
at
|
||||
} else {
|
||||
self.cwd.clone()
|
||||
};
|
||||
|
||||
Self::_find(at, path, follow, follow_mount)
|
||||
}
|
||||
|
||||
pub fn open(
|
||||
&self,
|
||||
at: Option<VnodeRef>,
|
||||
path: &str,
|
||||
opts: OpenOptions,
|
||||
_mode: FileMode,
|
||||
) -> Result<FileRef, Error> {
|
||||
let node = match self.find(at.clone(), path, true, true) {
|
||||
Err(Error::DoesNotExist) => {
|
||||
// TODO check for create option
|
||||
return Err(Error::DoesNotExist);
|
||||
}
|
||||
o => o,
|
||||
}?;
|
||||
|
||||
node.open(opts)
|
||||
}
|
||||
|
||||
pub fn root(&self) -> &VnodeRef {
|
||||
&self.root
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{node::VnodeRef, IoContext};
|
||||
use std::fmt;
|
||||
|
||||
macro_rules! node {
|
||||
($name:literal) => {{
|
||||
$crate::node::Vnode::new($name, $crate::node::VnodeKind::Regular)
|
||||
}};
|
||||
|
||||
($name:literal [ $($child:expr),* ]) => {{
|
||||
let _node = $crate::node::Vnode::new($name, $crate::node::VnodeKind::Directory);
|
||||
|
||||
$(
|
||||
_node.add_child($child);
|
||||
)*
|
||||
|
||||
_node
|
||||
}};
|
||||
}
|
||||
|
||||
struct DumpNode<'a> {
|
||||
node: &'a VnodeRef,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DumpNode<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.node.dump(f, 0, true)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vnode_find() {
|
||||
let t = node! {
|
||||
"" [
|
||||
node!("file1.txt"),
|
||||
node!("file2.txt"),
|
||||
node! {
|
||||
"dir1" [
|
||||
node!("file3.txt")
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let ctx = IoContext::new(t);
|
||||
|
||||
// Absolute lookups
|
||||
assert_eq!(
|
||||
ctx.find(None, "/file1.txt", false, false).unwrap().name(),
|
||||
"file1.txt"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(None, "/file3.txt", false, false).unwrap_err(),
|
||||
Error::DoesNotExist
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(None, "/dir1/file3.txt", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"file3.txt"
|
||||
);
|
||||
|
||||
// Non-absolute lookups from root
|
||||
assert_eq!(
|
||||
ctx.find(None, "file1.txt", false, false).unwrap().name(),
|
||||
"file1.txt"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(None, "dir1/file3.txt", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"file3.txt"
|
||||
);
|
||||
|
||||
// Absolute lookups from non-root
|
||||
let cwd = ctx.find(None, "/dir1", false, false).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "/file1.txt", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"file1.txt"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "/dir1/file3.txt", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"file3.txt"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "/file3.txt", false, false)
|
||||
.unwrap_err(),
|
||||
Error::DoesNotExist
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "/dir2", false, false)
|
||||
.unwrap_err(),
|
||||
Error::DoesNotExist
|
||||
);
|
||||
|
||||
// Non-absolute lookups in non-root
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "file3.txt", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"file3.txt"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "././file3.txt", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"file3.txt"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "../dir1/file3.txt", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"file3.txt"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), ".", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
"dir1"
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "..", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
ctx.find(Some(cwd.clone()), "../..", false, false)
|
||||
.unwrap()
|
||||
.name(),
|
||||
""
|
||||
);
|
||||
}
|
||||
}
|
@ -1,69 +1 @@
|
||||
#![no_std]
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use yggdrasil_abi::error::Error;
|
||||
use yggdrasil_abi::io::{DirectoryEntry, SeekFrom};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
pub(crate) mod block;
|
||||
pub(crate) mod char;
|
||||
pub(crate) mod file;
|
||||
pub(crate) mod fs;
|
||||
pub(crate) mod ioctx;
|
||||
pub(crate) mod node;
|
||||
|
||||
pub use self::block::BlockDevice;
|
||||
pub use self::char::{CharDevice, CharDeviceWrapper};
|
||||
pub use file::{File, FileFlags, FileRef};
|
||||
pub use fs::Filesystem;
|
||||
pub use ioctx::IoContext;
|
||||
pub use node::{CreateInfo, Vnode, VnodeDump, VnodeImpl, VnodeKind, VnodeRef, VnodeWeak};
|
||||
|
||||
pub const DIR_POSITION_FROM_CACHE: u64 = u64::MAX;
|
||||
|
||||
pub trait Write {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
pub trait Read {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Error>;
|
||||
|
||||
fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Error> {
|
||||
default_read_exact(self, data)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Seek {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error>;
|
||||
}
|
||||
|
||||
pub trait ReadDirectory {
|
||||
fn read_dir_entries(
|
||||
&mut self,
|
||||
entries: &mut [MaybeUninit<DirectoryEntry>],
|
||||
) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [u8]) -> Result<(), Error> {
|
||||
while !buf.is_empty() {
|
||||
match this.read(buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
let tmp = buf;
|
||||
buf = &mut tmp[n..];
|
||||
}
|
||||
Err(e) => todo!("default_read_exact: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
if !buf.is_empty() {
|
||||
todo!("default_read_exact unexpected eof")
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,519 +0,0 @@
|
||||
use core::{
|
||||
any::Any,
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
fmt,
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
rc::{Rc, Weak},
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{DeviceRequest, FileAttr, FileMode, FileType, OpenOptions},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
file::{File, FileFlags, FileRef},
|
||||
fs::Filesystem,
|
||||
DIR_POSITION_FROM_CACHE,
|
||||
};
|
||||
|
||||
pub type VnodeRef = Rc<Vnode>;
|
||||
pub type VnodeWeak = Weak<Vnode>;
|
||||
|
||||
pub struct VnodeDump {
|
||||
node: VnodeRef,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum VnodeKind {
|
||||
Directory,
|
||||
Regular,
|
||||
Char,
|
||||
Block,
|
||||
}
|
||||
|
||||
pub(crate) struct TreeNode {
|
||||
parent: Option<VnodeWeak>,
|
||||
children: Vec<VnodeRef>,
|
||||
}
|
||||
|
||||
pub struct Vnode {
|
||||
name: String,
|
||||
tree: RefCell<TreeNode>,
|
||||
kind: VnodeKind,
|
||||
data: OneTimeInit<Box<dyn VnodeImpl>>,
|
||||
fs: RefCell<Option<Rc<dyn Filesystem>>>,
|
||||
target: RefCell<Option<VnodeRef>>,
|
||||
}
|
||||
|
||||
pub struct CreateInfo<'a> {
|
||||
pub name: &'a str,
|
||||
pub kind: VnodeKind,
|
||||
pub mode: FileMode,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait VnodeImpl {
|
||||
fn create(&self, at: &VnodeRef, info: &CreateInfo) -> Result<VnodeRef, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn remove(&self, at: &VnodeRef, name: &str) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn open(
|
||||
&self,
|
||||
node: &VnodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<(u64, Option<Box<dyn Any>>), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn close(&self, node: &VnodeRef) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
node: &VnodeRef,
|
||||
pos: u64,
|
||||
inner: Option<&mut Box<dyn Any>>,
|
||||
data: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn write(&self, node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn metadata(&self, node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn device_request(&self, node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
impl Vnode {
|
||||
pub fn new<S: Into<String>>(name: S, kind: VnodeKind) -> VnodeRef {
|
||||
Rc::new(Self {
|
||||
name: name.into(),
|
||||
tree: RefCell::new(TreeNode {
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
}),
|
||||
kind,
|
||||
data: OneTimeInit::new(),
|
||||
fs: RefCell::new(None),
|
||||
target: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(&self) -> VnodeKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn target(&self) -> Option<VnodeRef> {
|
||||
self.target.borrow().clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn data(&self) -> Option<&dyn VnodeImpl> {
|
||||
self.data.try_get().map(Box::as_ref)
|
||||
}
|
||||
// #[inline]
|
||||
// pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
|
||||
// match self.data.try_borrow_mut() {
|
||||
// Ok(r) => r,
|
||||
// Err(e) => {
|
||||
// panic!("{:?} on {:?}", e, self.name())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[inline]
|
||||
pub fn fs(&self) -> Option<Rc<dyn Filesystem>> {
|
||||
self.fs.borrow().clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_target(&self, target: Option<VnodeRef>) {
|
||||
self.target.replace(target);
|
||||
}
|
||||
|
||||
pub fn parent(self: &VnodeRef) -> VnodeRef {
|
||||
match &self.tree.borrow().parent {
|
||||
Some(parent) => parent.upgrade().unwrap(),
|
||||
None => self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_root(&self) -> bool {
|
||||
self.tree.borrow().parent.is_none()
|
||||
}
|
||||
|
||||
pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
|
||||
self.data.init(data);
|
||||
// self.data.borrow_mut().replace(data);
|
||||
}
|
||||
|
||||
pub fn set_fs(&self, data: Rc<dyn Filesystem>) {
|
||||
self.fs.replace(Some(data));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_directory(&self) -> bool {
|
||||
self.kind == VnodeKind::Directory
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_mountpoint(&self) -> bool {
|
||||
self.is_directory() && self.target.borrow().is_some()
|
||||
}
|
||||
|
||||
// Cache tree operations
|
||||
pub fn add_child(self: &VnodeRef, child: VnodeRef) {
|
||||
let parent_weak = Rc::downgrade(self);
|
||||
let mut parent_borrow = self.tree.borrow_mut();
|
||||
|
||||
assert!(child
|
||||
.tree
|
||||
.borrow_mut()
|
||||
.parent
|
||||
.replace(parent_weak)
|
||||
.is_none());
|
||||
parent_borrow.children.push(child);
|
||||
}
|
||||
|
||||
pub fn remove_child(self: &VnodeRef, name: &str) {
|
||||
self.children_mut().retain(|node| node.name() != name);
|
||||
}
|
||||
|
||||
pub fn child_at(self: &VnodeRef, index: usize) -> Option<VnodeRef> {
|
||||
let tree = self.tree.borrow();
|
||||
tree.children.get(index).cloned()
|
||||
}
|
||||
|
||||
pub fn children(&self) -> Ref<Vec<VnodeRef>> {
|
||||
let tree = self.tree.borrow();
|
||||
Ref::map(tree, |t| &t.children)
|
||||
}
|
||||
|
||||
pub fn children_mut(&self) -> RefMut<Vec<VnodeRef>> {
|
||||
let tree = self.tree.borrow_mut();
|
||||
RefMut::map(tree, |t| &mut t.children)
|
||||
}
|
||||
|
||||
pub fn dump(&self, f: &mut fmt::Formatter<'_>, depth: usize, indent: bool) -> fmt::Result {
|
||||
if indent {
|
||||
for _ in 0..depth {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, "{:?}", self.name)?;
|
||||
|
||||
if self.is_directory() {
|
||||
let tree = self.tree.borrow();
|
||||
let target = self.target();
|
||||
|
||||
if let Some(target) = target {
|
||||
f.write_str(" -> ")?;
|
||||
return target.dump(f, depth, false);
|
||||
}
|
||||
|
||||
if tree.children.is_empty() {
|
||||
f.write_str(" []")?;
|
||||
} else {
|
||||
f.write_str(" [\n")?;
|
||||
for child in tree.children.iter() {
|
||||
child.dump(f, depth + 1, true)?;
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
for _ in 0..depth {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
f.write_str("]")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn lookup(self: &VnodeRef, name: &str) -> Option<VnodeRef> {
|
||||
assert!(self.is_directory());
|
||||
self.tree
|
||||
.borrow()
|
||||
.children
|
||||
.iter()
|
||||
.find(|e| e.name == name)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
//
|
||||
pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result<VnodeRef, Error> {
|
||||
// Lookup in cache
|
||||
if let Some(node) = self.lookup(name) {
|
||||
return Ok(node);
|
||||
}
|
||||
|
||||
// TODO load from FS
|
||||
Err(Error::DoesNotExist)
|
||||
}
|
||||
|
||||
// Node operations
|
||||
pub fn open(self: &VnodeRef, flags: OpenOptions) -> Result<FileRef, Error> {
|
||||
let mut open_flags = FileFlags::empty();
|
||||
|
||||
if flags.contains(OpenOptions::READ) {
|
||||
open_flags |= FileFlags::READ;
|
||||
}
|
||||
if flags.contains(OpenOptions::WRITE) {
|
||||
open_flags |= FileFlags::WRITE;
|
||||
}
|
||||
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Error::IsADirectory);
|
||||
}
|
||||
|
||||
if let Some(data) = self.data() {
|
||||
let (pos, inner) = data.open(self, flags)?;
|
||||
Ok(File::normal(self.clone(), pos, inner, open_flags))
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_directory(self: &VnodeRef) -> Result<FileRef, Error> {
|
||||
if !self.is_directory() {
|
||||
return Err(Error::IsADirectory);
|
||||
}
|
||||
|
||||
if let Some(data) = self.data() {
|
||||
// TODO don't discard directory's Box<dyn Any>
|
||||
let (pos, _) = data.open(self, OpenOptions::READ)?;
|
||||
Ok(File::directory(self.clone(), pos))
|
||||
} else {
|
||||
// TODO: some options here?
|
||||
Ok(File::directory(self.clone(), DIR_POSITION_FROM_CACHE))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(self: &VnodeRef) -> Result<(), Error> {
|
||||
if let Some(data) = self.data() {
|
||||
data.close(self)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(self: &VnodeRef, info: &CreateInfo) -> Result<VnodeRef, Error> {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
todo!();
|
||||
}
|
||||
if info.name.contains('/') {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
match self.lookup_or_load(info.name) {
|
||||
Err(Error::DoesNotExist) => {}
|
||||
Ok(_) => return Err(Error::AlreadyExists),
|
||||
e => return e,
|
||||
};
|
||||
|
||||
if let Some(data) = self.data() {
|
||||
let vnode = data.create(self, info)?;
|
||||
if let Some(fs) = self.fs() {
|
||||
vnode.set_fs(fs);
|
||||
}
|
||||
self.add_child(vnode.clone());
|
||||
Ok(vnode)
|
||||
} else {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(self: &VnodeRef, node: VnodeRef, recurse: bool) -> Result<(), Error> {
|
||||
let name = node.name();
|
||||
|
||||
if self.kind != VnodeKind::Directory {
|
||||
todo!();
|
||||
}
|
||||
if name.contains('/') {
|
||||
todo!();
|
||||
}
|
||||
|
||||
if node.kind() == VnodeKind::Directory {
|
||||
if recurse {
|
||||
todo!();
|
||||
}
|
||||
|
||||
// Check if remove target is not empty
|
||||
if node.size()? != 0 {
|
||||
return Err(Error::DirectoryNotEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(data) = self.data() {
|
||||
data.remove(self, name)?;
|
||||
}
|
||||
|
||||
// Unlink node from cache
|
||||
self.remove_child(name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(self: &VnodeRef, pos: u64, buf: &[u8]) -> Result<usize, Error> {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
todo!();
|
||||
}
|
||||
|
||||
if let Some(data) = self.data() {
|
||||
data.write(self, pos, buf)
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
self: &VnodeRef,
|
||||
pos: u64,
|
||||
inner: Option<&mut Box<dyn Any>>,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
todo!();
|
||||
}
|
||||
|
||||
if let Some(data) = self.data() {
|
||||
data.read(self, pos, inner, buf)
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self: &VnodeRef) -> Result<u64, Error> {
|
||||
if let Some(data) = self.data() {
|
||||
data.size(self)
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mount(self: &VnodeRef, fs_root: VnodeRef) -> Result<(), Error> {
|
||||
if !self.is_directory() {
|
||||
return Err(Error::NotADirectory);
|
||||
}
|
||||
if !fs_root.is_directory() {
|
||||
todo!("Filesystem root is not a directory");
|
||||
}
|
||||
if self.target.borrow().is_some() {
|
||||
todo!("Target mountpoint is busy");
|
||||
}
|
||||
|
||||
{
|
||||
let mut child_borrow = fs_root.tree.borrow_mut();
|
||||
if child_borrow.parent.is_some() {
|
||||
todo!("Filesystem is already mounted somewhere else");
|
||||
}
|
||||
child_borrow.parent = Some(Rc::downgrade(self));
|
||||
}
|
||||
self.target.replace(Some(fs_root));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmount_target(self: &VnodeRef) -> Result<(), Error> {
|
||||
if !self.is_directory() {
|
||||
return Err(Error::NotADirectory);
|
||||
}
|
||||
let Some(fs_root) = self.target.take() else {
|
||||
todo!();
|
||||
};
|
||||
|
||||
{
|
||||
let mut target_borrow = fs_root.tree.borrow_mut();
|
||||
let Some(parent) = target_borrow.parent.take() else {
|
||||
todo!()
|
||||
};
|
||||
assert!(Rc::ptr_eq(self, &parent.upgrade().unwrap()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn metadata(self: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
if let Some(data) = self.data() {
|
||||
data.metadata(self)
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn device_request(self: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
if let Some(data) = self.data() {
|
||||
data.device_request(self, req)
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Vnode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let prefix = match self.kind {
|
||||
VnodeKind::Directory => "DIR ",
|
||||
VnodeKind::Regular => "REG ",
|
||||
VnodeKind::Char => "CHR ",
|
||||
VnodeKind::Block => "BLK ",
|
||||
};
|
||||
|
||||
write!(f, "[{} {}]", prefix, self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl VnodeDump {
|
||||
pub fn new(node: VnodeRef) -> Self {
|
||||
Self { node }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for VnodeDump {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.node.dump(f, 0, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VnodeKind> for FileType {
|
||||
fn from(value: VnodeKind) -> Self {
|
||||
match value {
|
||||
VnodeKind::Regular => Self::File,
|
||||
VnodeKind::Directory => Self::Directory,
|
||||
VnodeKind::Block => Self::Block,
|
||||
VnodeKind::Char => Self::Char,
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ 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,
|
||||
@ -103,7 +102,8 @@ unsafe extern "C" fn __aarch64_bsp_upper_entry(dtb: PhysicalAddress) -> ! {
|
||||
// // Setup initrd
|
||||
// super::setup_initrd();
|
||||
|
||||
devfs::init();
|
||||
// XXX
|
||||
// devfs::init();
|
||||
|
||||
runtime::init_task_queue();
|
||||
|
||||
|
@ -27,7 +27,6 @@ use crate::{
|
||||
devtree::{self, DevTreeIndexPropExt, DevTreeNodeInfo, DeviceTree, FdtMemoryRegionIter},
|
||||
power::arm_psci::Psci,
|
||||
},
|
||||
fs::{Initrd, INITRD_DATA},
|
||||
mem::{
|
||||
address::{FromRaw, IntoRaw},
|
||||
device::RawDeviceMemoryMapping,
|
||||
@ -315,11 +314,12 @@ impl AArch64 {
|
||||
let data = unsafe { PhysicalRef::map_slice(initrd_start, len) };
|
||||
self.initrd.init(data);
|
||||
|
||||
INITRD_DATA.init(Initrd {
|
||||
phys_page_start: aligned_start,
|
||||
phys_page_len: aligned_end - aligned_start,
|
||||
data: self.initrd.get().as_ref(),
|
||||
});
|
||||
// XXX
|
||||
// INITRD_DATA.init(Initrd {
|
||||
// phys_page_start: aligned_start,
|
||||
// phys_page_len: aligned_end - aligned_start,
|
||||
// data: self.initrd.get().as_ref(),
|
||||
// });
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -8,7 +8,6 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use vfs::CharDevice;
|
||||
|
||||
use crate::{
|
||||
arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
|
||||
@ -19,7 +18,6 @@ use crate::{
|
||||
tty::{TtyContext, TtyDevice},
|
||||
},
|
||||
device_tree_driver,
|
||||
fs::devfs::{self, CharDeviceType},
|
||||
mem::{address::FromRaw, device::DeviceMemoryIo, PhysicalAddress},
|
||||
sync::IrqSafeSpinlock,
|
||||
task::process::ProcessId,
|
||||
@ -109,37 +107,37 @@ impl TtyDevice for Pl011 {
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for Pl011 {
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Error> {
|
||||
assert!(blocking);
|
||||
self.line_write(data)
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
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, blocking: bool, data: &[u8]) -> Result<usize, Error> {
|
||||
// assert!(blocking);
|
||||
// self.line_write(data)
|
||||
// }
|
||||
//
|
||||
// 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),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 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> {
|
||||
@ -193,7 +191,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,7 +21,7 @@ pub mod combined {
|
||||
io::{DeviceRequest, TerminalSize},
|
||||
};
|
||||
use device_api::{input::KeyboardConsumer, serial::SerialDevice};
|
||||
use vfs::CharDevice;
|
||||
// use vfs::CharDevice;
|
||||
|
||||
use crate::device::{
|
||||
display::{console::DisplayConsole, fb_console::FramebufferConsole},
|
||||
@ -73,43 +73,43 @@ 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, 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)
|
||||
// }
|
||||
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Error> {
|
||||
assert!(blocking);
|
||||
self.line_write(data)
|
||||
}
|
||||
// fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Error> {
|
||||
// assert!(blocking);
|
||||
// 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")]
|
||||
|
80
src/init.rs
80
src/init.rs
@ -3,19 +3,8 @@ use abi::{
|
||||
error::Error,
|
||||
io::{OpenOptions, RawFd},
|
||||
};
|
||||
use memfs::MemoryFilesystem;
|
||||
use vfs::{Filesystem, IoContext, VnodeRef};
|
||||
|
||||
use crate::{
|
||||
fs::{devfs, FileBlockAllocator, INITRD_DATA},
|
||||
proc,
|
||||
};
|
||||
|
||||
fn setup_root() -> Result<VnodeRef, Error> {
|
||||
let initrd_data = INITRD_DATA.get();
|
||||
let fs = MemoryFilesystem::<FileBlockAllocator>::from_slice(initrd_data.data).unwrap();
|
||||
fs.root()
|
||||
}
|
||||
use crate::proc;
|
||||
|
||||
/// Kernel's "main" process function.
|
||||
///
|
||||
@ -25,46 +14,47 @@ fn setup_root() -> Result<VnodeRef, Error> {
|
||||
/// 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 ioctx = IoContext::new(root);
|
||||
// let node = ioctx.find(None, "/init", true, true)?;
|
||||
// let file = node.open(OpenOptions::READ)?;
|
||||
|
||||
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();
|
||||
// #[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();
|
||||
|
||||
{
|
||||
// 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);
|
||||
// {
|
||||
// // 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);
|
||||
|
||||
user_init.set_session_terminal(console);
|
||||
// user_init.set_session_terminal(console);
|
||||
|
||||
user_init_main.enqueue_somewhere();
|
||||
}
|
||||
// user_init_main.enqueue_somewhere();
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
// Ok(())
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ use arch::Architecture;
|
||||
|
||||
use crate::{
|
||||
arch::{ArchitectureImpl, ARCHITECTURE},
|
||||
fs::sysfs,
|
||||
mem::heap,
|
||||
sync::SpinFence,
|
||||
task::{spawn_kernel_closure, Cpu},
|
||||
@ -51,7 +50,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;
|
||||
@ -89,7 +88,7 @@ pub fn kernel_main() -> ! {
|
||||
debugln!("Heap: {:#x?}", heap::heap_range());
|
||||
|
||||
// Setup the sysfs
|
||||
sysfs::init();
|
||||
// sysfs::init();
|
||||
|
||||
unsafe {
|
||||
ARCHITECTURE.start_application_processors();
|
||||
|
@ -7,7 +7,6 @@ use abi::{
|
||||
process::ProgramArgumentInner,
|
||||
};
|
||||
use alloc::{string::String, sync::Arc};
|
||||
use vfs::FileRef;
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
@ -146,7 +145,8 @@ fn setup_binary<S: Into<String>>(
|
||||
}
|
||||
}
|
||||
|
||||
let tls_address = proc::elf::clone_tls(&space, &image)?;
|
||||
// XXX
|
||||
let tls_address = 0; // 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,79 +1,12 @@
|
||||
//! Process I/O management
|
||||
use abi::{error::Error, io::RawFd};
|
||||
use alloc::collections::{btree_map::Entry, BTreeMap};
|
||||
use vfs::{FileRef, IoContext};
|
||||
|
||||
/// I/O context of a process, contains information like root, current directory and file
|
||||
/// descriptor table
|
||||
pub struct ProcessIo {
|
||||
ioctx: Option<IoContext>,
|
||||
files: BTreeMap<RawFd, FileRef>,
|
||||
}
|
||||
pub struct ProcessIo {}
|
||||
|
||||
impl ProcessIo {
|
||||
/// Constructs an uninitialized I/O context
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ioctx: None,
|
||||
files: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a file given descriptor refers to
|
||||
pub fn file(&self, fd: RawFd) -> Result<FileRef, Error> {
|
||||
self.files.get(&fd).cloned().ok_or(Error::InvalidFile)
|
||||
}
|
||||
|
||||
/// Iterates over the file descriptors in [ProcessIo] and removes them if predicate is `false`
|
||||
pub fn retain<F: Fn(&RawFd, &mut FileRef) -> bool>(&mut self, f: F) {
|
||||
self.files.retain(f)
|
||||
}
|
||||
|
||||
/// Sets the inner I/O context
|
||||
pub fn set_ioctx(&mut self, ioctx: IoContext) {
|
||||
self.ioctx.replace(ioctx);
|
||||
}
|
||||
|
||||
/// Inserts a file into the descriptor table. Returns error if the file is already present for
|
||||
/// given descriptor.
|
||||
pub fn set_file(&mut self, fd: RawFd, file: FileRef) -> Result<(), Error> {
|
||||
if self.files.contains_key(&fd) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
self.files.insert(fd, file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allocates a slot for a file and returns it
|
||||
pub fn place_file(&mut self, file: FileRef) -> Result<RawFd, Error> {
|
||||
for idx in 0..64 {
|
||||
let fd = RawFd(idx);
|
||||
|
||||
if let Entry::Vacant(e) = self.files.entry(fd) {
|
||||
e.insert(file);
|
||||
return Ok(fd);
|
||||
}
|
||||
}
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Closes the file and removes it from the table
|
||||
pub fn close_file(&mut self, fd: RawFd) -> Result<(), Error> {
|
||||
let file = self.files.remove(&fd);
|
||||
if file.is_none() {
|
||||
todo!();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the inner I/O context reference
|
||||
pub fn ioctx(&mut self) -> &mut IoContext {
|
||||
self.ioctx.as_mut().unwrap()
|
||||
}
|
||||
|
||||
/// Cleans up I/O context after the process exits
|
||||
pub fn handle_exit(&mut self) {
|
||||
self.files.clear();
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Internal management for processes
|
||||
|
||||
pub mod elf;
|
||||
// pub mod elf;
|
||||
pub mod exec;
|
||||
pub mod io;
|
||||
|
@ -8,7 +8,7 @@ use abi::{
|
||||
syscall::SyscallFunction,
|
||||
};
|
||||
use alloc::rc::Rc;
|
||||
use vfs::{IoContext, Read, ReadDirectory, Seek, VnodeKind, VnodeRef, Write};
|
||||
// use vfs::{IoContext, Read, ReadDirectory, Seek, VnodeKind, VnodeRef, Write};
|
||||
use yggdrasil_abi::{
|
||||
error::SyscallResult,
|
||||
io::{MountOptions, UnmountOptions},
|
||||
@ -17,7 +17,6 @@ use yggdrasil_abi::{
|
||||
use crate::{
|
||||
block,
|
||||
debug::LogLevel,
|
||||
fs,
|
||||
mem::{phys, table::MapAttributes},
|
||||
proc::{self, io::ProcessIo},
|
||||
sync::IrqSafeSpinlockGuard,
|
||||
@ -31,26 +30,26 @@ 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<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 syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error> {
|
||||
let thread = Thread::current();
|
||||
@ -119,247 +118,7 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
|
||||
Ok(0)
|
||||
}
|
||||
// I/O
|
||||
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| {
|
||||
let file = io.file(fd)?;
|
||||
let mut file_borrow = file.borrow_mut();
|
||||
|
||||
file_borrow.write(data)
|
||||
})
|
||||
}
|
||||
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| {
|
||||
let file = io.file(fd)?;
|
||||
let mut file_borrow = file.borrow_mut();
|
||||
|
||||
file_borrow.read(data)
|
||||
})
|
||||
}
|
||||
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 Ok(node) = file.borrow().node() && node.kind() == VnodeKind::Char {
|
||||
debugln!("Session terminal set for #{}: {}", process.id(), path);
|
||||
process.set_session_terminal(node);
|
||||
}
|
||||
|
||||
let fd = io.place_file(file)?;
|
||||
Ok(fd.0 as usize)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Close => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
|
||||
run_with_io(&process, |mut io| {
|
||||
io.close_file(fd)?;
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
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 file = node.open_directory()?;
|
||||
let fd = io.place_file(file)?;
|
||||
|
||||
Ok(fd.0 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| {
|
||||
let file = io.file(fd)?;
|
||||
let mut file_borrow = file.borrow_mut();
|
||||
|
||||
file_borrow.read_dir_entries(buffer)
|
||||
})
|
||||
}
|
||||
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| {
|
||||
let (parent, name) = abi::path::split_right(path);
|
||||
let parent_node = io.ioctx().find(at, parent, true, true)?;
|
||||
todo!();
|
||||
// parent_node.create(name, VnodeKind::Directory)?;
|
||||
|
||||
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| {
|
||||
let node = io.ioctx().find(at, path, false, false)?;
|
||||
|
||||
if node.is_root() || Rc::ptr_eq(io.ioctx().root(), &node) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
let parent = node.parent();
|
||||
|
||||
parent.remove(node, recurse)?;
|
||||
|
||||
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()?;
|
||||
buffer.write(metadata);
|
||||
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Seek => {
|
||||
let fd = RawFd(args[0] as u32);
|
||||
let pos = SeekFrom::from(args[1]);
|
||||
|
||||
run_with_io(&process, |io| {
|
||||
let file = io.file(fd)?;
|
||||
let mut file_borrow = file.borrow_mut();
|
||||
|
||||
file_borrow.seek(pos).map(|v| v as usize)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Mount => {
|
||||
let options = arg_user_ref::<MountOptions>(args[0] as usize)?;
|
||||
|
||||
run_with_io(&process, |mut io| {
|
||||
let target_node = io.ioctx().find(None, options.target, true, false)?;
|
||||
if !target_node.is_directory() {
|
||||
return Err(Error::NotADirectory);
|
||||
}
|
||||
|
||||
let fs_root = fs::create_filesystem(options)?;
|
||||
|
||||
target_node.mount(fs_root)?;
|
||||
|
||||
debugln!("{:?}", vfs::VnodeDump::new(io.ioctx().root().clone()));
|
||||
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
SyscallFunction::Unmount => {
|
||||
let options = arg_user_ref::<UnmountOptions>(args[0] as usize)?;
|
||||
|
||||
run_with_io(&process, |mut io| {
|
||||
let mountpoint = io.ioctx().find(None, options.mountpoint, true, false)?;
|
||||
mountpoint.unmount_target()?;
|
||||
|
||||
debugln!("{:?}", vfs::VnodeDump::new(io.ioctx().root().clone()));
|
||||
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
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.borrow().node()?;
|
||||
|
||||
node.device_request(req)?;
|
||||
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
// 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 = 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 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)?;
|
||||
}
|
||||
&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.borrow().node()?;
|
||||
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)?;
|
||||
@ -421,26 +180,6 @@ 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.borrow()
|
||||
.node()
|
||||
.map(|node| !Rc::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);
|
||||
@ -458,6 +197,10 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
_ => {
|
||||
todo!("System call: {:?}", func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ use alloc::{
|
||||
};
|
||||
use futures_util::Future;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use vfs::VnodeRef;
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
@ -131,8 +130,7 @@ struct ProcessInner {
|
||||
session_id: ProcessId,
|
||||
group_id: ProcessId,
|
||||
|
||||
session_terminal: Option<VnodeRef>,
|
||||
|
||||
// session_terminal: Option<VnodeRef>,
|
||||
threads: Vec<Arc<Thread>>,
|
||||
mutexes: BTreeMap<usize, Arc<UserspaceMutex>>,
|
||||
}
|
||||
@ -173,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(),
|
||||
}),
|
||||
@ -199,7 +197,8 @@ impl Process {
|
||||
);
|
||||
|
||||
let tls_address = if let Some(image) = self.image.as_ref() {
|
||||
proc::elf::clone_tls(&self.space, image)?
|
||||
todo!()
|
||||
// proc::elf::clone_tls(&self.space, image)?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@ -247,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<VnodeRef> {
|
||||
// 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: VnodeRef) {
|
||||
// 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<VnodeRef> {
|
||||
// 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> {
|
||||
@ -319,7 +318,8 @@ impl Process {
|
||||
|
||||
inner.state = ProcessState::Terminated(code);
|
||||
|
||||
self.io.lock().handle_exit();
|
||||
// XXX
|
||||
// self.io.lock().handle_exit();
|
||||
|
||||
drop(inner);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user