feat: start vfs impl

This commit is contained in:
2021-10-23 17:35:50 +03:00
parent ad7406a993
commit 65fc4bc4fe
8 changed files with 429 additions and 0 deletions
Generated
+63
View File
@@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "autocfg"
version = "1.0.1"
@@ -61,6 +72,26 @@ dependencies = [
"unsafe_unwrap",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "init"
version = "0.1.0"
@@ -80,6 +111,12 @@ dependencies = [
"tock-registers",
]
[[package]]
name = "libc"
version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce"
[[package]]
name = "libusr"
version = "0.1.0"
@@ -113,6 +150,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "osdev5"
version = "0.1.0"
@@ -193,3 +236,23 @@ name = "unsafe_unwrap"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "vfs"
version = "0.1.0"
dependencies = [
"error",
"hashbrown",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+1
View File
@@ -9,6 +9,7 @@ edition = "2018"
[workspace]
members = [
"fs/vfs",
"kernel",
"libusr",
"init",
+1
View File
@@ -8,4 +8,5 @@ pub enum Errno {
OutOfMemory,
WouldBlock,
AlreadyExists,
NotImplemented,
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "vfs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
error = { path = "../../error" }
hashbrown = "0.11.*"
+8
View File
@@ -0,0 +1,8 @@
use crate::{VnodeKind, VnodeRef};
use alloc::rc::Rc;
use error::Errno;
pub trait Filesystem {
fn root(self: Rc<Self>) -> Result<VnodeRef, Errno>;
fn create_node(self: Rc<Self>, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno>;
}
+10
View File
@@ -0,0 +1,10 @@
#![no_std]
extern crate alloc;
pub mod fs;
pub use fs::Filesystem;
pub mod stat;
pub use stat::FileMode;
pub mod node;
pub use node::{VnodeRef, Vnode, VnodeKind, VnodeImpl};
+250
View File
@@ -0,0 +1,250 @@
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec};
use core::cell::{RefCell, RefMut};
use crate::{Filesystem, FileMode};
use error::Errno;
pub type VnodeRef = Rc<Vnode>;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum VnodeKind {
Directory,
Regular,
}
pub struct TreeNode {
parent: Option<VnodeRef>,
children: Vec<VnodeRef>,
}
pub struct VnodeData {
// Filesystem itself
pub fs: Rc<dyn Filesystem>,
// Something like "inode" struct + "ops" table
pub node: Box<dyn VnodeImpl>,
}
pub struct VnodeProps {
mode: FileMode,
}
pub struct Vnode {
name: String,
tree: RefCell<TreeNode>,
props: RefCell<VnodeProps>,
kind: VnodeKind,
pub data: RefCell<Option<VnodeData>>,
}
pub trait VnodeImpl {
fn create(&mut self, at: VnodeRef, node: VnodeRef) -> Result<(), Errno>;
fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>;
}
impl Vnode {
pub fn new(name: &str, kind: VnodeKind) -> VnodeRef {
Rc::new(Self {
name: name.to_owned(),
kind,
props: RefCell::new(VnodeProps {
mode: FileMode::empty(),
}),
tree: RefCell::new(TreeNode {
parent: None,
children: Vec::new(),
}),
data: RefCell::new(None),
})
}
pub fn set_data(&self, data: VnodeData) {
*self.data.borrow_mut() = Some(data);
}
pub fn data(&self) -> RefMut<Option<VnodeData>> {
self.data.borrow_mut()
}
// Tree operations
fn attach(self: &VnodeRef, child: VnodeRef) {
let parent_clone = self.clone();
let mut parent_borrow = self.tree.borrow_mut();
assert!(child
.tree
.borrow_mut()
.parent
.replace(parent_clone)
.is_none());
parent_borrow.children.push(child);
}
fn detach(self: &VnodeRef) {
let mut self_borrow = self.tree.borrow_mut();
let parent = self_borrow.parent.take().unwrap();
let mut parent_borrow = parent.tree.borrow_mut();
let index = parent_borrow
.children
.iter()
.position(|it| Rc::ptr_eq(&it, self))
.unwrap();
parent_borrow.children.remove(index);
}
pub fn parent(self: &VnodeRef) -> VnodeRef {
self.tree.borrow().parent.as_ref().unwrap_or(self).clone()
}
pub fn lookup(self: &VnodeRef, name: &str) -> Option<VnodeRef> {
self.tree
.borrow()
.children
.iter()
.find(|e| e.name == name)
.cloned()
}
pub fn mkdir(self: &VnodeRef, name: &str, mode: FileMode) -> Result<VnodeRef, Errno> {
if self.kind != VnodeKind::Directory {
return Err(Errno::NotADirectory);
}
if let Some(ref mut data) = *self.data.borrow_mut() {
let vnode = data.fs.clone().create_node(name, VnodeKind::Directory)?;
vnode.props.borrow_mut().mode = mode;
if let Err(err) = data.node.create(self.clone(), vnode.clone()) {
if err != Errno::NotImplemented {
return Err(err);
}
}
self.attach(vnode.clone());
Ok(vnode)
} else {
Err(Errno::NotImplemented)
}
}
pub fn unlink(self: &VnodeRef, name: &str) -> Result<(), Errno> {
if self.kind != VnodeKind::Directory {
return Err(Errno::NotADirectory);
}
if let Some(ref mut data) = *self.data.borrow_mut() {
let vnode = self.lookup(name).ok_or(Errno::DoesNotExist)?;
if let Err(err) = data.node.remove(self.clone(), name) {
if err != Errno::NotImplemented {
return Err(err);
}
}
vnode.detach();
Ok(())
} else {
Err(Errno::NotImplemented)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Filesystem;
pub struct DummyInode;
pub struct DummyFs;
impl VnodeImpl for DummyInode {
fn create(&mut self, _at: VnodeRef, _node: VnodeRef) -> Result<(), Errno> {
Err(Errno::NotImplemented)
}
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
Err(Errno::NotImplemented)
}
}
impl Filesystem for DummyFs {
fn root(self: Rc<Self>) -> Result<VnodeRef, Errno> {
todo!()
}
fn create_node(self: Rc<Self>, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
let node = Vnode::new(name, kind);
node.set_data(VnodeData {
node: Box::new(DummyInode {}),
fs: self,
});
Ok(node)
}
}
#[test]
fn test_parent() {
let root = Vnode::new("", VnodeKind::Directory);
let node = Vnode::new("dir0", VnodeKind::Directory);
root.attach(node.clone());
assert!(Rc::ptr_eq(&root.parent(), &root));
assert!(Rc::ptr_eq(&node.parent(), &root));
}
#[test]
fn test_mkdir_unlink() {
let fs = Rc::new(DummyFs {});
let root = Vnode::new("", VnodeKind::Directory);
root.set_data(VnodeData {
node: Box::new(DummyInode {}),
fs: fs.clone(),
});
let node = root.mkdir("test", FileMode::default_dir()).unwrap();
assert_eq!(node.props.borrow().mode, FileMode::default_dir());
assert!(Rc::ptr_eq(&node, &root.lookup("test").unwrap()));
assert!(node.data.borrow().is_some());
root.unlink("test").unwrap();
assert!(root.lookup("test").is_none());
}
#[test]
fn test_lookup_attach_detach() {
let root = Vnode::new("", VnodeKind::Directory);
let dir0 = Vnode::new("dir0", VnodeKind::Directory);
let dir1 = Vnode::new("dir1", VnodeKind::Directory);
root.attach(dir0.clone());
root.attach(dir1.clone());
assert!(Rc::ptr_eq(&dir0, &root.lookup("dir0").unwrap()));
assert!(Rc::ptr_eq(&dir1, &root.lookup("dir1").unwrap()));
assert!(Rc::ptr_eq(
&root,
dir0.tree.borrow().parent.as_ref().unwrap()
));
assert!(Rc::ptr_eq(
&root,
dir1.tree.borrow().parent.as_ref().unwrap()
));
assert!(root.lookup("dir2").is_none());
dir0.detach();
assert!(Rc::ptr_eq(&dir1, &root.lookup("dir1").unwrap()));
assert!(Rc::ptr_eq(
&root,
dir1.tree.borrow().parent.as_ref().unwrap()
));
assert!(dir0.tree.borrow().parent.is_none());
assert!(root.lookup("dir0").is_none());
assert!(root.lookup("dir2").is_none());
}
}
+86
View File
@@ -0,0 +1,86 @@
use core::fmt;
#[derive(Clone, Copy, PartialEq)]
#[repr(transparent)]
pub struct FileMode(u16);
impl FileMode {
const USER_READ: u16 = 1 << 8;
const USER_WRITE: u16 = 1 << 7;
const USER_EXEC: u16 = 1 << 6;
const GROUP_READ: u16 = 1 << 5;
const GROUP_WRITE: u16 = 1 << 4;
const GROUP_EXEC: u16 = 1 << 3;
const OTHER_READ: u16 = 1 << 2;
const OTHER_WRITE: u16 = 1 << 1;
const OTHER_EXEC: u16 = 1 << 0;
pub const fn empty() -> Self {
Self(0)
}
pub const fn default_dir() -> Self {
Self(0o755)
}
pub const fn default_reg() -> Self {
Self(0o644)
}
}
impl fmt::Debug for FileMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FileMode (")?;
if self.0 & Self::USER_READ != 0 {
write!(f, "r")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::USER_WRITE != 0 {
write!(f, "w")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::USER_EXEC != 0 {
write!(f, "x")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::GROUP_READ != 0 {
write!(f, "r")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::GROUP_WRITE != 0 {
write!(f, "w")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::GROUP_EXEC != 0 {
write!(f, "x")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::OTHER_READ != 0 {
write!(f, "r")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::OTHER_WRITE != 0 {
write!(f, "w")?;
} else {
write!(f, "-")?;
}
if self.0 & Self::OTHER_EXEC != 0 {
write!(f, "x")?;
} else {
write!(f, "-")?;
}
write!(f, ")")?;
Ok(())
}
}