feat: start vfs impl
This commit is contained in:
Generated
+63
@@ -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"
|
||||
|
||||
@@ -9,6 +9,7 @@ edition = "2018"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"fs/vfs",
|
||||
"kernel",
|
||||
"libusr",
|
||||
"init",
|
||||
|
||||
@@ -8,4 +8,5 @@ pub enum Errno {
|
||||
OutOfMemory,
|
||||
WouldBlock,
|
||||
AlreadyExists,
|
||||
NotImplemented,
|
||||
}
|
||||
|
||||
@@ -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.*"
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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};
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user