This commit is contained in:
2021-09-13 15:37:15 +03:00
parent 7140af8ccf
commit 605ab4fe09
42 changed files with 2208 additions and 314 deletions
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "vfs"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
error = { path = "../../error" }
spin = "*"
+187
View File
@@ -0,0 +1,187 @@
use crate::{path_element, Node, NodeRef};
use alloc::rc::Rc;
use error::Errno;
pub struct Ioctx {
pub root: NodeRef,
pub cwd: NodeRef,
}
impl Ioctx {
fn lookup_or_load(parent: NodeRef, name: &[u8]) -> Result<NodeRef, Errno> {
let p = parent.borrow_mut();
if !p.is_directory() {
return Err(Errno::NotADirectory);
}
if let Some(node) = p.children().find(|&node| node.borrow().name() == name) {
return Ok(node.clone());
}
if let Some(ops) = p.ops.as_ref() {
todo!();
}
Err(Errno::DoesNotExist)
}
fn _find(&self, mut at: NodeRef, path: &[u8]) -> Result<NodeRef, Errno> {
let mut child_path: &[u8];
let mut element_name: &[u8];
if path.is_empty() {
return Ok(at);
}
child_path = path;
loop {
let r = path_element(child_path);
element_name = r.0;
child_path = r.1;
match element_name {
b"." => {
if child_path.is_empty() {
return Ok(at);
}
}
b".." => {
let parent = at.borrow().parent().unwrap_or(at.clone());
at = parent;
if child_path.is_empty() {
return Ok(at);
}
}
_ => break,
}
}
if element_name.is_empty() && child_path.is_empty() {
return Ok(at);
}
assert!(!element_name.is_empty());
let child = Self::lookup_or_load(at, element_name)?;
if child_path.is_empty() {
Ok(child)
} else {
self._find(child, child_path)
}
}
pub fn find(&self, at: Option<NodeRef>, mut path: &[u8]) -> Result<NodeRef, Errno> {
if path.is_empty() {
return at.ok_or(Errno::DoesNotExist);
}
let at = if path[0] == b'/' {
let index = path
.iter()
.position(|&x| x != b'/')
.unwrap_or(path.len() - 1);
path = &path[index..];
self.root.clone()
} else if let Some(node) = at {
node
} else {
self.cwd.clone()
};
self._find(at, path)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Node;
#[test]
fn ioctx_find_at() {
let r = Node::directory(b"");
let d0 = Node::directory(b"dir0");
let d1 = Node::directory(b"dir1");
let d0d0 = Node::directory(b"dir0");
let d0f0 = Node::file(b"file0");
Node::attach(r.clone(), d0.clone());
Node::attach(r.clone(), d1.clone());
Node::attach(d0.clone(), d0d0.clone());
Node::attach(d0.clone(), d0f0.clone());
let ioctx = Ioctx {
root: r.clone(),
cwd: r.clone(),
};
assert!(Rc::ptr_eq(
ioctx.find(Some(r.clone()), b"dir0").as_ref().unwrap(),
&d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"dir0").as_ref().unwrap(),
&d0d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(r.clone()), b"dir0/dir0").as_ref().unwrap(),
&d0d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"file0").as_ref().unwrap(),
&d0f0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(r.clone()), b"dir0/file0").as_ref().unwrap(),
&d0f0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"../dir1").as_ref().unwrap(),
&d1
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"./dir0").as_ref().unwrap(),
&d0d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././dir0/.").as_ref().unwrap(),
&d0d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././dir0/./").as_ref().unwrap(),
&d0d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././dir0/").as_ref().unwrap(),
&d0d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././dir0/..").as_ref().unwrap(),
&d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././dir0/../..").as_ref().unwrap(),
&r
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././dir0/../../../").as_ref().unwrap(),
&r
));
// TODO make these illegal
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././file0/..").as_ref().unwrap(),
&d0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././file0/").as_ref().unwrap(),
&d0f0
));
assert!(Rc::ptr_eq(
ioctx.find(Some(d0.clone()), b"././file0/.").as_ref().unwrap(),
&d0f0
));
ioctx.find(Some(r.clone()), b"dir0/dir1").expect_err("");
}
}
+28
View File
@@ -0,0 +1,28 @@
#![no_std]
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
#[cfg(test)]
#[macro_use]
extern crate std;
use error::Errno;
pub mod node;
mod util;
pub use node::{Node, NodeOperations, NodeRef, NodeType};
mod ioctx;
pub use ioctx::Ioctx;
pub fn path_element(path: &[u8]) -> (&[u8], &[u8]) {
if let Some(mut index) = path.iter().position(|&x| x == b'/') {
let elem = &path[..index];
while index < path.len() && path[index] == b'/' {
index += 1;
}
(elem, &path[index..])
} else {
(path, b"")
}
}
+181
View File
@@ -0,0 +1,181 @@
use crate::util::{iter::LockedIterator, FixedStr};
use alloc::{boxed::Box, rc::Rc, vec::Vec};
use core::any::Any;
use core::cell::RefCell;
use core::ffi::c_void;
use core::fmt;
use core::ptr::null_mut;
use error::Errno;
use spin::Mutex;
pub type NodeRef = Rc<RefCell<Node>>;
pub enum NodeType {
Regular,
Directory { children: Vec<NodeRef> },
}
pub const NODE_MEMORY: u32 = 1 << 0;
pub struct NodeOperations {
lookup: Option<fn(parent: NodeRef, name: &[u8]) -> Result<NodeRef, Errno>>,
drop: Option<fn(node: &mut Node)>,
}
pub struct Node {
name: FixedStr<64>,
typ: NodeType,
flags: u32,
pub(crate) ops: Option<&'static NodeOperations>,
pub data: *mut c_void,
target: Option<NodeRef>,
parent: Option<NodeRef>,
}
impl Node {
pub fn new(name: &[u8], typ: NodeType) -> Self {
let mut r = Self {
name: FixedStr::empty(),
typ,
flags: 0,
ops: None,
data: null_mut(),
parent: None,
target: None,
};
r.name.copy_from_slice(name);
r
}
pub fn directory(name: &[u8]) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self::new(
name,
NodeType::Directory { children: vec![] },
)))
}
pub fn file(name: &[u8]) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self::new(name, NodeType::Regular)))
}
pub fn name(&self) -> &FixedStr<64> {
&self.name
}
pub fn parent(&self) -> Option<NodeRef> {
self.parent.clone()
}
pub fn is_directory(&self) -> bool {
matches!(&self.typ, NodeType::Directory { .. })
}
pub fn children(&self) -> impl Iterator<Item = &NodeRef> {
let lock = TREE_MUTEX.lock();
match &self.typ {
NodeType::Directory { children } => LockedIterator::new(children.iter(), lock),
_ => panic!("Not a directory"),
}
}
pub fn children_mut(&mut self) -> impl Iterator<Item = &mut NodeRef> {
let lock = TREE_MUTEX.lock();
match &mut self.typ {
NodeType::Directory { children } => LockedIterator::new(children.iter_mut(), lock),
_ => panic!("Not a directory"),
}
}
pub fn attach(parent: NodeRef, child: NodeRef) {
let _lock = TREE_MUTEX.lock();
assert!(child.borrow().parent.is_none());
match &mut parent.borrow_mut().typ {
NodeType::Directory { children } => children.push(child.clone()),
_ => panic!("Not a directory"),
}
child.borrow_mut().parent.replace(parent.clone());
}
pub fn detach(child: NodeRef) {
let _lock = TREE_MUTEX.lock();
assert!(child.borrow().parent.is_some());
let parent = child.borrow_mut().parent.take().unwrap();
match &mut parent.borrow_mut().typ {
NodeType::Directory { children } => {
children.remove(children.iter().position(|x| Rc::ptr_eq(x, &child)).unwrap());
}
_ => panic!("Not a directory"),
};
}
}
impl Drop for Node {
fn drop(&mut self) {
if let Some(ops) = self.ops.as_ref() {
if let Some(do_drop) = ops.drop.as_ref() {
do_drop(self);
}
}
}
}
impl fmt::Debug for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Node {{ name={:?}, typ=??? }}", self.name)
}
}
static TREE_MUTEX: Mutex<()> = Mutex::new(());
#[cfg(test)]
mod tests {
use super::*;
use core::any::{type_name, TypeId};
use core::sync::atomic::{AtomicBool, Ordering};
#[test]
fn node_new() {
let r = Node::directory(b"");
let n = r.borrow();
assert_eq!(n.name, b""[..]);
assert!(matches!(n.typ, NodeType::Directory { .. }));
assert!(n.ops.is_none());
assert!(n.target.is_none());
assert!(n.parent.is_none());
let r = Node::file(b"file1");
let n = r.borrow();
assert_eq!(n.name, b"file1"[..]);
assert!(matches!(n.typ, NodeType::Regular));
assert!(n.ops.is_none());
assert!(n.target.is_none());
assert!(n.parent.is_none());
}
#[test]
fn node_attach() {
let r0 = Node::directory(b"");
let r1 = Node::directory(b"1234");
// Attach
Node::attach(r0.clone(), r1.clone());
assert!(Rc::ptr_eq(r1.borrow().parent.as_ref().unwrap(), &r0));
{
let n0 = r0.borrow();
let mut it = n0.children();
assert!(Rc::ptr_eq(&it.next().unwrap(), &r1));
assert!(it.next().is_none());
}
// Detach
Node::detach(r1.clone());
assert!(r1.borrow().parent.is_none());
assert_eq!(r0.borrow().children().count(), 0);
}
}
+22
View File
@@ -0,0 +1,22 @@
use spin::MutexGuard;
pub struct LockedIterator<'a, T: ?Sized, I: Iterator<Item = T>> {
inner: I,
#[allow(dead_code)]
lock: MutexGuard<'a, ()>,
}
impl<'a, T, I: Iterator<Item = T>> Iterator for LockedIterator<'a, T, I> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl<'a, T, I: Iterator<Item = T>> LockedIterator<'a, T, I> {
#[inline]
pub fn new(inner: I, lock: MutexGuard<'a, ()>) -> Self {
Self { inner, lock }
}
}
+94
View File
@@ -0,0 +1,94 @@
use alloc::boxed::Box;
use core::alloc::Layout;
use core::ffi::c_void;
use core::fmt;
pub mod iter;
#[repr(transparent)]
pub struct FixedStr<const N: usize> {
inner: [u8; N],
}
impl<const N: usize> FixedStr<N> {
pub fn new(s: &[u8]) -> Self {
let mut r = Self::empty();
r.copy_from_slice(s);
r
}
pub const fn empty() -> Self {
Self { inner: [0; N] }
}
pub fn len(&self) -> usize {
self.inner.iter().position(|&n| n == 0).unwrap_or(N)
}
pub fn copy_from_slice(&mut self, src: &[u8]) {
let src_len = src.len();
if src_len == 0 {
self.inner[0] = 0;
return;
}
if src_len >= N {
panic!("String buffer overflow");
}
let dst = &mut self.inner[..src_len];
dst.copy_from_slice(src);
self.inner[src_len] = 0;
}
}
impl<const N: usize> PartialEq<[u8]> for FixedStr<N> {
fn eq(&self, other: &[u8]) -> bool {
let self_len = self.len();
if self_len != other.len() {
return false;
}
&self.inner[..self_len] == other
}
}
impl<const N: usize> fmt::Display for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.inner {
if byte == 0 {
break;
}
write!(f, "{}", byte as char)?;
}
Ok(())
}
}
impl<const N: usize> fmt::Debug for FixedStr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fixed_str_display() {
let s = FixedStr::<64>::new(b"test");
assert_eq!(format!("{}", s), "test");
}
#[test]
fn fixed_str_length() {
assert_eq!(FixedStr::<64>::empty().len(), 0);
assert_eq!(FixedStr::<64>::new(b"test1").len(), 5);
assert_eq!(FixedStr::<6>::new(b"test1").len(), 5);
}
#[test]
fn fixed_str_eq_slice() {
assert_eq!(FixedStr::<64>::empty(), b""[..]);
assert_eq!(FixedStr::<64>::new(b"1234"), b"1234"[..]);
}
}