fs: path abi, ioctx impl

This commit is contained in:
Mark Poliakov 2023-12-03 14:52:42 +02:00
parent aadd97fc8e
commit fbe05c1d4d
11 changed files with 729 additions and 74 deletions

View File

@ -6,7 +6,7 @@ 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" }
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git", features = ["alloc"] }
kernel-util = { path = "../kernel-util" }
log = "0.4.20"

View File

@ -16,7 +16,7 @@ pub trait BlockDevice {
fn is_writable(&self) -> bool {
true
}
fn size(&self) -> Result<usize, Error>;
fn size(&self) -> Result<u64, Error>;
}
pub trait CharDevice {
@ -54,7 +54,7 @@ impl BlockDeviceWrapper {
}
impl CommonImpl for BlockDeviceWrapper {
fn size(&self, _node: &NodeRef) -> Result<usize, Error> {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
self.0.size()
}
}
@ -74,7 +74,7 @@ impl CharDeviceWrapper {
}
impl CommonImpl for CharDeviceWrapper {
fn size(&self, _node: &NodeRef) -> Result<usize, Error> {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
}

View File

@ -114,6 +114,15 @@ impl File {
_ => Err(Error::NotADirectory),
}
}
pub fn node(&self) -> Option<&NodeRef> {
match self {
Self::Directory(file) => Some(&file.node),
Self::Regular(file) => Some(&file.node),
Self::Block(file) => Some(&file.node),
Self::Char(file) => Some(&file.node),
}
}
}
impl Read for File {
@ -198,7 +207,7 @@ mod tests {
use crate::{
device::{BlockDevice, CharDevice},
file::DirectoryOpenPosition,
node::{CommonImpl, DirectoryImpl, Node, NodeRef, RegularImpl},
node::{CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl},
traits::{Read, Seek, Write},
};
@ -244,13 +253,16 @@ mod tests {
impl CommonImpl for F {}
impl RegularImpl for F {}
let d = Node::directory(D {
entries: Vec::from_iter([
("f1".to_owned(), Node::regular(F)),
("f2".to_owned(), Node::regular(F)),
("f3".to_owned(), Node::regular(F)),
]),
});
let d = Node::directory(
D {
entries: Vec::from_iter([
("f1".to_owned(), Node::regular(F, NodeFlags::empty())),
("f2".to_owned(), Node::regular(F, NodeFlags::empty())),
("f3".to_owned(), Node::regular(F, NodeFlags::empty())),
]),
},
NodeFlags::empty(),
);
let f = d.open_directory().unwrap();
@ -293,8 +305,8 @@ mod tests {
}
}
let d = Node::directory(D);
let child = Node::directory(D);
let d = Node::directory(D, NodeFlags::empty());
let child = Node::directory(D, NodeFlags::empty());
d.add_child("child1", child).unwrap();
@ -334,8 +346,8 @@ mod tests {
}
impl CommonImpl for F {
fn size(&self, _node: &NodeRef) -> Result<usize, Error> {
Ok(self.data.borrow().len())
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(self.data.borrow().len() as _)
}
}
impl RegularImpl for F {
@ -380,7 +392,7 @@ mod tests {
}
let data = Arc::new(RefCell::new(vec![]));
let node = Node::regular(F { data: data.clone() });
let node = Node::regular(F { data: data.clone() }, NodeFlags::empty());
let file = node.open(OpenOptions::READ | OpenOptions::WRITE).unwrap();
let mut buf = [0; 512];
@ -435,8 +447,8 @@ mod tests {
Ok(count)
}
fn size(&self) -> Result<usize, Error> {
Ok(self.data.lock().len())
fn size(&self) -> Result<u64, Error> {
Ok(self.data.lock().len() as _)
}
}
@ -446,7 +458,7 @@ mod tests {
let dev = Box::leak(Box::new(B { data }));
let mut buf = [0; 512];
let node = Node::block(dev);
let node = Node::block(dev, NodeFlags::empty());
let file = node.open(OpenOptions::READ | OpenOptions::WRITE).unwrap();
@ -480,7 +492,7 @@ mod tests {
static DEV: C = C;
let node = Node::char(&DEV);
let node = Node::char(&DEV, NodeFlags::empty());
let mut buf = [0; 512];
let err = node.open(OpenOptions::WRITE).unwrap_err();

390
lib/vfs/src/ioctx.rs Normal file
View File

@ -0,0 +1,390 @@
use alloc::borrow::ToOwned;
use yggdrasil_abi::{
error::Error,
io::{FileMode, FileType, GroupId, OpenOptions, UserId},
path::{Path, PathBuf},
};
use crate::{
node::{CreateInfo, SymlinkData},
FileRef, NodeRef,
};
pub struct IoContext {
uid: UserId,
gid: GroupId,
umask: FileMode,
cwd_node: NodeRef,
cwd_path: PathBuf,
root: NodeRef,
}
impl IoContext {
pub fn new(root: NodeRef) -> Self {
Self {
uid: UserId::root(),
gid: GroupId::root(),
umask: FileMode::new(0o022),
cwd_node: root.clone(),
cwd_path: PathBuf::new(),
root,
}
}
pub fn root(&self) -> &NodeRef {
&self.root
}
pub fn cwd(&self) -> &NodeRef {
&self.cwd_node
}
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)?;
if !node.is_directory() {
return Err(Error::NotADirectory);
}
self.cwd_node = node;
self.cwd_path = path.to_owned();
Ok(())
}
pub fn cwd_path(&self) -> &Path {
self.cwd_path.as_ref()
}
pub fn mount<P: AsRef<Path>>(&mut self, target: P, fs_root: NodeRef) -> Result<(), Error> {
let target = target.as_ref();
if !target.is_absolute() {
todo!();
}
let target_node = Self::_find(
self.root.clone(),
target.trim_start_separators(),
true,
false,
)?;
target_node.set_mountpoint_target(fs_root)
}
pub fn open<P: AsRef<Path>>(
&mut self,
at: Option<NodeRef>,
path: P,
opts: OpenOptions,
mode: FileMode,
) -> Result<FileRef, Error> {
let path = path.as_ref();
let node = match self.find(at.clone(), path, true, true) {
Ok(node) => node,
Err(Error::DoesNotExist) if opts.contains(OpenOptions::CREATE) => {
// let create_mode = mode & !self.umask;
let (parent, name) = path.split_right();
let parent = self.find(at, parent, true, true)?;
let create_info = CreateInfo {
name: name.into(),
mode: mode & !self.umask,
uid: self.uid,
gid: self.gid,
ty: FileType::File,
};
parent.create(&create_info)?
}
Err(err) => return Err(err),
};
node.open(opts)
}
pub fn find<P: AsRef<Path>>(
&mut self,
at: Option<NodeRef>,
path: P,
follow_links: bool,
follow_mount: bool,
) -> Result<NodeRef, Error> {
let mut path = path.as_ref();
let at = if path.is_absolute() {
path = path.trim_start_separators();
self.root.clone()
} else if let Some(at) = at {
at
} else {
self.cwd_node.clone()
};
Self::_find(at, path, follow_links, follow_mount)
}
fn _resolve_link(at: &NodeRef, link: &SymlinkData) -> Result<NodeRef, Error> {
// If the filesystem itself implements direct target resolution, use that
match link.imp.target(at) {
Err(Error::NotImplemented) => {}
other => return other,
}
// If that call wasn't implemented, check if the target was cached
if let Some((_, target)) = link.target.lock().as_ref() {
return Ok(target.clone());
}
// If no cache is present, read the link's data and resolve the path
let _path = link.imp.read_to_string()?;
todo!()
}
fn _resolve(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();
continue;
}
if follow_links && let Ok(link) = at.as_symlink() {
// Resolve the link
at = Self::_resolve_link(&at, link)?;
continue;
}
break Ok(at);
}
}
fn _find(
mut at: NodeRef,
path: &Path,
follow_links: bool,
follow_mount: bool,
) -> Result<NodeRef, Error> {
let mut element;
let mut rest = path;
loop {
(element, rest) = rest.split_left();
if !at.is_directory() {
return Err(Error::NotADirectory);
}
match element {
Path::PARENT_NAME => {
at = at.parent();
}
Path::SELF_NAME => {}
_ => break,
}
}
at = Self::_resolve(at, follow_links, follow_mount)?;
if !at.is_directory() {
return Err(Error::NotADirectory);
}
if element.is_empty() && rest.is_empty() {
return Ok(at);
}
let node = at.lookup_or_load(element)?;
let node = Self::_resolve(node, follow_links, follow_mount)?;
if rest.is_empty() {
Ok(node)
} else {
Self::_find(node, rest, follow_links, follow_mount)
}
}
}
#[cfg(test)]
mod tests {
use alloc::sync::Arc;
use yggdrasil_abi::{
error::Error,
io::{FileMode, OpenOptions},
path::Path,
};
use crate::{
impls::{const_value_node, f_symlink, mdir},
Read,
};
use super::IoContext;
#[test]
fn cwd() {
let d1_f1 = const_value_node("dir1-file1");
let d1 = mdir([("f1", d1_f1.clone())]);
let f1 = const_value_node("file1");
let root = mdir([("f1", f1.clone()), ("d1", d1.clone())]);
let mut ioctx = IoContext::new(root.clone());
assert_eq!(ioctx.cwd_path(), Path::empty());
let node = ioctx.find(None, "f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &f1));
let node = ioctx.find(None, "d1/f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &d1_f1));
let node = ioctx.find(None, "d1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &d1));
ioctx.set_cwd("/d1").unwrap();
assert_eq!(ioctx.cwd_path(), Path::from_str("/d1"));
let err = ioctx.find(None, "d1", true, true).unwrap_err();
assert_eq!(err, Error::DoesNotExist);
let node = ioctx.find(None, "f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &d1_f1));
let node = ioctx.find(None, "../d1/f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &d1_f1));
let node = ioctx.find(None, "/f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &f1));
let node = ioctx.find(None, "../f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &f1));
let node = ioctx.find(None, "..", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &root));
}
#[test]
fn mount_resolution() {
let root2_f1 = const_value_node("root2-file1");
let root2 = mdir([("f1", root2_f1.clone())]);
let root1_f1 = const_value_node("root1-file1");
let root1 = mdir([("f1", root1_f1.clone())]);
let mut ioctx = IoContext::new(root1.clone());
let node = ioctx.find(None, "/f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &root1_f1));
ioctx.mount("/", root2.clone()).unwrap();
let node = ioctx.find(None, "/f1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &root2_f1));
let node = ioctx.find(None, "/f1", true, false).unwrap();
assert!(Arc::ptr_eq(&node, &root1_f1));
let node = ioctx.find(None, "/", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &root2));
let node = ioctx.find(None, "/", true, false).unwrap();
assert!(Arc::ptr_eq(&node, &root1));
}
#[test]
fn symlink_resolution() {
let f1 = const_value_node("file1");
let l1 = f_symlink(f1.clone());
let root = mdir([("l1", l1.clone())]);
let mut ioctx = IoContext::new(root.clone());
// No follow
let node = ioctx.find(None, "/l1", false, true).unwrap();
assert!(Arc::ptr_eq(&node, &l1));
// Follow
let node = ioctx.find(None, "/l1", true, true).unwrap();
assert!(Arc::ptr_eq(&node, &f1));
}
#[test]
fn open_or_create() {
let root_d1_f1 = const_value_node("dir1/file1");
let root_f1 = const_value_node("file1");
let root_d1 = mdir([("f1", root_d1_f1.clone())]);
let root = mdir([("f1", root_f1.clone()), ("d1", root_d1.clone())]);
let mut ioctx = IoContext::new(root.clone());
let mut buf = [0; 512];
let file = ioctx
.open(None, "/d1/f1", OpenOptions::READ, FileMode::empty())
.unwrap();
assert!(Arc::ptr_eq(&file.node().unwrap(), &root_d1_f1));
assert_eq!(file.read(&mut buf).unwrap(), 10);
assert_eq!(&buf[..10], b"dir1/file1");
drop(file);
let file = ioctx
.open(
None,
"/d1/f1",
OpenOptions::READ | OpenOptions::CREATE,
FileMode::empty(),
)
.unwrap();
assert!(Arc::ptr_eq(&file.node().unwrap(), &root_d1_f1));
assert_eq!(file.read(&mut buf).unwrap(), 10);
assert_eq!(&buf[..10], b"dir1/file1");
drop(file);
let err = ioctx
.open(
None,
"/d1/f2",
OpenOptions::WRITE | OpenOptions::CREATE,
FileMode::empty(),
)
.unwrap_err();
assert_eq!(err, Error::ReadOnly);
}
#[test]
fn find_tests() {
let root_d1_f1 = const_value_node("dir1/file1");
let root_f1 = const_value_node("file1");
let root_d1 = mdir([("f1", root_d1_f1.clone())]);
let root = mdir([("f1", root_f1.clone()), ("d1", root_d1.clone())]);
let mut ioctx = IoContext::new(root.clone());
// Ok paths
let node = ioctx.find(None, Path::empty(), false, false).unwrap();
assert!(Arc::ptr_eq(&node, &root));
let node = ioctx.find(None, Path::from_str("/"), false, false).unwrap();
assert!(Arc::ptr_eq(&node, &root));
// Weird paths
let node = ioctx
.find(None, Path::from_str("////.////./"), false, false)
.unwrap();
assert!(Arc::ptr_eq(&node, &root));
let node = ioctx
.find(None, Path::from_str("/../.././//."), false, false)
.unwrap();
assert!(Arc::ptr_eq(&node, &root));
let node = ioctx
.find(None, Path::from_str("/f1"), false, false)
.unwrap();
assert!(Arc::ptr_eq(&node, &root_f1));
let node = ioctx
.find(None, Path::from_str("/d1/f1"), false, false)
.unwrap();
assert!(Arc::ptr_eq(&node, &root_d1_f1));
let node = ioctx
.find(None, Path::from_str("/d1/../d1/./f1"), false, false)
.unwrap();
assert!(Arc::ptr_eq(&node, &root_d1_f1));
let node = ioctx
.find(None, Path::from_str("/d1/.."), false, false)
.unwrap();
assert!(Arc::ptr_eq(&node, &root));
}
}

View File

@ -1,5 +1,11 @@
#![cfg_attr(not(test), no_std)]
#![feature(trait_upcasting, if_let_guard, maybe_uninit_slice, trait_alias)]
#![feature(
trait_upcasting,
if_let_guard,
maybe_uninit_slice,
trait_alias,
let_chains
)]
#[cfg(test)]
extern crate hosted_tests;
@ -8,7 +14,9 @@ extern crate alloc;
pub(crate) mod device;
pub(crate) mod file;
pub(crate) mod ioctx;
pub(crate) mod node;
pub(crate) mod path;
pub(crate) mod traits;
pub use device::{BlockDevice, CharDevice};

View File

@ -1,10 +1,19 @@
use core::{any::Any, cell::RefCell, marker::PhantomData, str::FromStr};
use alloc::{boxed::Box, string::ToString, sync::Arc, vec::Vec};
use alloc::{
boxed::Box,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use yggdrasil_abi::{error::Error, io::OpenOptions};
use super::{CommonImpl, Node, NodeRef, RegularImpl};
use crate::DirectoryOpenPosition;
use super::{
CommonImpl, CreateInfo, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl, SymlinkImpl,
};
pub trait SliceRead {
fn read_slice(&self, offset: usize, buf: &mut [u8]) -> usize;
@ -65,9 +74,17 @@ pub struct FnNode<T: ToString + FromStr, R: ReadFn<T>, W: WriteFn<T>> {
_pd: PhantomData<T>,
}
pub struct MemoryDirectory;
pub struct FixedSymlink {
target: NodeRef,
}
impl<T: ToString + 'static, R: ReadFn<T> + 'static> ReadOnlyFnNode<T, R> {
pub fn new(read: R) -> NodeRef {
Node::regular(Self::new_impl(read))
Node::regular(
Self::new_impl(read),
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
)
}
pub const fn new_impl(read: R) -> Self {
@ -79,7 +96,7 @@ impl<T: ToString + 'static, R: ReadFn<T> + 'static> ReadOnlyFnNode<T, R> {
}
impl<T: ToString, R: ReadFn<T>> CommonImpl for ReadOnlyFnNode<T, R> {
fn size(&self, _node: &NodeRef) -> Result<usize, Error> {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
}
@ -149,7 +166,10 @@ impl<T: ToString + FromStr + 'static, R: ReadFn<T> + 'static, W: WriteFn<T> + 's
FnNode<T, R, W>
{
pub fn new(read: R, write: W) -> NodeRef {
Node::regular(Self::new_impl(read, write))
Node::regular(
Self::new_impl(read, write),
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
)
}
pub const fn new_impl(read: R, write: W) -> Self {
@ -162,7 +182,7 @@ 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> {
fn size(&self, _node: &NodeRef) -> Result<usize, Error> {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
}
@ -227,6 +247,28 @@ impl<T: ToString + FromStr, R: ReadFn<T>, W: WriteFn<T>> RegularImpl for FnNode<
}
}
// In-memory directory
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
impl CommonImpl for FixedSymlink {}
impl SymlinkImpl for FixedSymlink {
fn target(&self, _node: &NodeRef) -> Result<NodeRef, Error> {
Ok(self.target.clone())
}
}
pub fn const_value_node<T: ToString + Clone + 'static>(value: T) -> NodeRef {
ReadOnlyFnNode::new(move || Ok(value.clone()))
}
@ -244,6 +286,21 @@ pub fn value_node<T: ToString + FromStr + Clone + 'static>(value: T) -> NodeRef
)
}
pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> NodeRef {
let dir = Node::directory(
MemoryDirectory,
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
);
for (name, node) in it {
dir.add_child(name, node).unwrap();
}
dir
}
pub fn f_symlink(target: NodeRef) -> NodeRef {
Node::symlink(FixedSymlink { target }, NodeFlags::empty())
}
#[cfg(test)]
mod tests {
use yggdrasil_abi::io::OpenOptions;

View File

@ -1,8 +1,16 @@
use core::any::Any;
use core::{
any::Any,
cell::{Cell, RefCell},
fmt,
};
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use kernel_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::{error::Error, io::FileType};
use yggdrasil_abi::{
bitflags,
error::Error,
io::{FileAttr, FileMode, FileType, GroupId, UserId},
};
pub mod impls;
mod traits;
@ -11,60 +19,117 @@ mod traits;
mod ops;
mod tree;
pub use traits::{CommonImpl, DirectoryImpl, RegularImpl};
pub use traits::{CommonImpl, DirectoryImpl, RegularImpl, SymlinkImpl};
use crate::device::{BlockDevice, BlockDeviceWrapper, CharDevice, CharDeviceWrapper};
pub type NodeRef = Arc<Node>;
bitflags! {
pub struct NodeFlags: u32 {
const IN_MEMORY_PROPS: bit 0;
const IN_MEMORY_SIZE: bit 1;
}
}
#[derive(Debug, Clone)]
pub struct CreateInfo {
pub name: String,
pub uid: UserId,
pub gid: GroupId,
pub mode: FileMode,
pub ty: FileType,
}
pub(crate) struct DirectoryData {
pub(crate) imp: Box<dyn DirectoryImpl>,
pub(crate) mountpoint: IrqSafeSpinlock<Option<NodeRef>>,
pub(crate) children: IrqSafeSpinlock<Vec<(String, NodeRef)>>,
}
pub(crate) struct SymlinkData {
// Cached symlink target with the literal path
pub(crate) target: IrqSafeSpinlock<Option<(String, NodeRef)>>,
pub(crate) imp: Box<dyn SymlinkImpl>,
}
enum NodeImpl {
Regular(Box<dyn RegularImpl>),
Directory(DirectoryData),
Block(BlockDeviceWrapper),
Char(CharDeviceWrapper),
Symlink(SymlinkData),
}
#[derive(Clone, Copy)]
pub struct Metadata {
pub uid: UserId,
pub gid: GroupId,
pub mode: FileMode,
}
#[derive(Default)]
struct PropertyCache {
metadata: Metadata,
size: Option<u64>,
}
pub struct Node {
data: NodeImpl,
flags: NodeFlags,
props: IrqSafeSpinlock<PropertyCache>,
parent: IrqSafeSpinlock<Option<NodeRef>>,
}
impl Default for Metadata {
fn default() -> Metadata {
Metadata {
uid: UserId::root(),
gid: GroupId::root(),
mode: FileMode::new(0),
}
}
}
impl Node {
pub fn directory<T: DirectoryImpl + 'static>(data: T) -> NodeRef {
let data = NodeImpl::Directory(DirectoryData {
imp: Box::new(data),
children: IrqSafeSpinlock::new(Vec::new()),
});
fn new(data: NodeImpl, flags: NodeFlags) -> NodeRef {
Arc::new(Self {
data,
flags,
props: IrqSafeSpinlock::new(PropertyCache::default()),
parent: IrqSafeSpinlock::new(None),
})
}
pub fn regular<T: RegularImpl + 'static>(data: T) -> NodeRef {
Arc::new(Self {
data: NodeImpl::Regular(Box::new(data)),
parent: IrqSafeSpinlock::new(None),
})
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)
}
pub fn block(device: &'static dyn BlockDevice) -> NodeRef {
Arc::new(Self {
data: NodeImpl::Block(BlockDeviceWrapper(device)),
parent: IrqSafeSpinlock::new(None),
})
pub fn regular<T: RegularImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
Self::new(NodeImpl::Regular(Box::new(data)), flags)
}
pub fn char(device: &'static dyn CharDevice) -> NodeRef {
Arc::new(Self {
data: NodeImpl::Char(CharDeviceWrapper(device)),
parent: IrqSafeSpinlock::new(None),
})
pub fn block(device: &'static dyn BlockDevice, flags: NodeFlags) -> NodeRef {
Self::new(NodeImpl::Block(BlockDeviceWrapper(device)), flags)
}
pub fn char(device: &'static dyn CharDevice, flags: NodeFlags) -> NodeRef {
Self::new(NodeImpl::Char(CharDeviceWrapper(device)), flags)
}
pub fn symlink<T: SymlinkImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
Self::new(
NodeImpl::Symlink(SymlinkData {
target: IrqSafeSpinlock::new(None),
imp: Box::new(data),
}),
flags,
)
}
pub fn data_as_any(&self) -> &dyn Any {
@ -73,6 +138,7 @@ impl Node {
NodeImpl::Regular(imp) => imp.as_any(),
NodeImpl::Block(w) => w.as_any(),
NodeImpl::Char(w) => w.as_any(),
NodeImpl::Symlink(w) => w.imp.as_any(),
}
}
@ -82,6 +148,7 @@ impl Node {
NodeImpl::Regular(imp) => imp.as_ref(),
NodeImpl::Block(w) => w,
NodeImpl::Char(w) => w,
NodeImpl::Symlink(w) => w.imp.as_ref(),
}
}
@ -95,6 +162,7 @@ impl Node {
NodeImpl::Directory(_) => FileType::Directory,
NodeImpl::Block(_) => FileType::Block,
NodeImpl::Char(_) => FileType::Char,
NodeImpl::Symlink(_) => FileType::Symlink,
}
}
@ -110,6 +178,36 @@ impl Node {
}
}
pub(crate) fn mountpoint_target(&self) -> Option<NodeRef> {
match &self.data {
NodeImpl::Directory(dir) => dir.mountpoint.lock().clone(),
_ => None,
}
}
pub(crate) fn set_mountpoint_target(self: &NodeRef, target: NodeRef) -> Result<(), Error> {
let directory = self.as_directory()?;
let mut mountpoint = directory.mountpoint.lock();
let mut target_parent_lock = target.parent.lock();
if mountpoint.is_some() {
// TODO Busy
todo!();
}
if target_parent_lock.is_some() {
// TODO mount a filesystem more than once?
return Err(Error::AlreadyExists);
}
if !target.is_directory() {
return Err(Error::NotADirectory);
}
mountpoint.replace(target.clone());
target_parent_lock.replace(self.clone());
Ok(())
}
pub(crate) fn as_directory(&self) -> Result<&DirectoryData, Error> {
match &self.data {
NodeImpl::Directory(dir) => Ok(dir),
@ -123,6 +221,25 @@ impl Node {
_ => Err(Error::InvalidFile),
}
}
pub(crate) fn as_symlink(&self) -> Result<&SymlinkData, Error> {
match &self.data {
NodeImpl::Symlink(imp) => Ok(imp),
_ => Err(Error::InvalidFile),
}
}
}
impl fmt::Debug for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.data {
NodeImpl::Directory(_) => f.debug_struct("DirectoryNode").finish_non_exhaustive(),
NodeImpl::Regular(_) => f.debug_struct("RegularNode").finish_non_exhaustive(),
NodeImpl::Char(_) => f.debug_struct("CharNode").finish_non_exhaustive(),
NodeImpl::Block(_) => f.debug_struct("BlockNode").finish_non_exhaustive(),
NodeImpl::Symlink(_) => f.debug_struct("SymlinkNode").finish_non_exhaustive(),
}
}
}
#[cfg(test)]
@ -130,6 +247,8 @@ mod tests {
use core::any::Any;
use std::sync::Arc;
use crate::node::NodeFlags;
use super::{CommonImpl, DirectoryImpl, Node, RegularImpl};
struct DummyDirectory;
@ -143,9 +262,9 @@ mod tests {
#[test]
fn dir_cache_add() {
let d1 = Node::directory(DummyDirectory);
let d2 = Node::directory(DummyDirectory);
let f1 = Node::regular(DummyFile);
let d1 = Node::directory(DummyDirectory, NodeFlags::empty());
let d2 = Node::directory(DummyDirectory, NodeFlags::empty());
let f1 = Node::regular(DummyFile, NodeFlags::empty());
assert!(Arc::ptr_eq(&f1.parent(), &f1));
assert_eq!(d1.children_len().unwrap(), 0);
@ -164,14 +283,14 @@ mod tests {
#[test]
fn in_mem_dir_size_coherence() {
let d = Node::directory(DummyDirectory);
let d = Node::directory(DummyDirectory, NodeFlags::IN_MEMORY_SIZE);
for i in 0..10 {
let name = format!("f{}", i);
let node = Node::regular(DummyFile);
let node = Node::regular(DummyFile, NodeFlags::empty());
d.add_child(name, node).unwrap();
assert_eq!(d.size().unwrap(), d.children_len().unwrap());
assert_eq!(d.size().unwrap(), d.children_len().unwrap() as u64);
}
}
@ -188,7 +307,7 @@ mod tests {
}
impl DirectoryImpl for AnyData {}
let d = Node::directory(AnyData { value: 1234 });
let d = Node::directory(AnyData { value: 1234 }, NodeFlags::empty());
let r = d.data_as_ref::<AnyData>();
assert_eq!(r.value, 1234);

View File

@ -7,7 +7,7 @@ use yggdrasil_abi::{
use crate::file::{File, FileRef};
use super::{Node, NodeImpl, NodeRef};
use super::{CreateInfo, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
impl Node {
pub fn open(self: &NodeRef, opts: OpenOptions) -> Result<FileRef, Error> {
@ -19,7 +19,7 @@ impl Node {
NodeImpl::Block(dev) => File::block(dev.clone(), self.clone(), opts),
NodeImpl::Char(dev) => File::char(dev.clone(), self.clone(), opts),
// TODO: maybe merge open_directory and open?
NodeImpl::Directory(_) => todo!(),
NodeImpl::Directory(_) | NodeImpl::Symlink(_) => todo!(),
}
}
@ -39,9 +39,58 @@ impl Node {
self.as_directory()?.imp.read_entries(self, pos, entries)
}
pub fn lookup_or_load(self: &NodeRef, name: &str) -> Result<NodeRef, Error> {
let dir = self.as_directory()?;
let children = dir.children.lock();
if let Some((_, node)) = children.iter().find(|(name_, _)| name_ == name) {
return Ok(node.clone());
}
// TODO lookup in real FS
Err(Error::DoesNotExist)
}
pub fn create(self: &NodeRef, info: &CreateInfo) -> Result<NodeRef, Error> {
let directory = self.as_directory()?;
let node = directory.imp.create(self, info)?;
// Attach the created node to the directory in memory cache
self.add_child(&info.name, node.clone())?;
Ok(node)
}
// Common
pub fn size(self: &NodeRef) -> Result<usize, Error> {
self.data_as_common().size(self)
pub fn metadata(self: &NodeRef) -> Result<Metadata, Error> {
if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
let props = self.props.lock();
return Ok(props.metadata);
}
self.data_as_common().metadata(self)
}
pub fn size(self: &NodeRef) -> Result<u64, Error> {
// Try to fetch the size from the cache
let mut props = self.props.lock();
if let Some(size) = props.size {
return Ok(size);
}
if self.flags.contains(NodeFlags::IN_MEMORY_SIZE) {
if let Ok(dir) = self.as_directory() {
return Ok(dir.children.lock().len() as _);
}
Err(Error::NotImplemented)
} else {
// Fetch the size from the node
let size = self.data_as_common().size(self)?;
props.size = Some(size);
Ok(size)
}
}
}

View File

@ -1,14 +1,14 @@
use alloc::boxed::Box;
use alloc::{boxed::Box, string::String};
use core::{any::Any, mem::MaybeUninit};
use yggdrasil_abi::{
error::Error,
io::{DirectoryEntry, FileAttr, OpenOptions},
io::{DirectoryEntry, OpenOptions},
};
use crate::file::DirectoryOpenPosition;
use super::NodeRef;
use super::{CreateInfo, Metadata, NodeRef};
#[allow(unused)]
pub trait CommonImpl {
@ -16,16 +16,12 @@ pub trait CommonImpl {
unimplemented!();
}
fn metadata(&self, node: &NodeRef) -> Result<FileAttr, Error> {
fn metadata(&self, node: &NodeRef) -> Result<Metadata, Error> {
Err(Error::NotImplemented)
}
fn size(&self, node: &NodeRef) -> Result<usize, Error> {
// TODO this only works for dirs
if node.is_directory() {
node.children_len()
} else {
Err(Error::NotImplemented)
}
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
Err(Error::NotImplemented)
}
}
@ -78,6 +74,10 @@ pub trait DirectoryImpl: CommonImpl {
Err(Error::NotImplemented)
}
fn create(&self, parent: &NodeRef, info: &CreateInfo) -> Result<NodeRef, Error> {
Err(Error::NotImplemented)
}
fn lookup(&self, node: &NodeRef, name: &str) -> Result<NodeRef, Error> {
Err(Error::NotImplemented)
}
@ -86,3 +86,24 @@ pub trait DirectoryImpl: CommonImpl {
Err(Error::NotImplemented)
}
}
#[allow(unused)]
pub trait SymlinkImpl: CommonImpl {
fn target(&self, node: &NodeRef) -> Result<NodeRef, Error> {
Err(Error::NotImplemented)
}
fn read_to_string(&self) -> Result<String, Error> {
let mut data = Box::new([0; 512]);
let len = self.read_link(data.as_mut())?;
if len == data.len() {
return Err(Error::InvalidFile);
}
let str = core::str::from_utf8(&data[..len]).map_err(|_| Error::InvalidFile)?;
Ok(String::from(str))
}
fn read_link(&self, buf: &mut [u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
}

View File

@ -32,7 +32,6 @@ impl Node {
assert!(child.parent.replace(Some(self.clone())).is_none());
children.push((name, child));
// children.insert(name, child);
Ok(())
}

0
lib/vfs/src/path.rs Normal file
View File