doc(fs/vfs): add vfs module docs

This commit is contained in:
2021-10-29 13:06:16 +03:00
parent df14db3833
commit 88c03804b7
10 changed files with 113 additions and 85 deletions
-5
View File
@@ -1,6 +1,5 @@
use crate::{Bpb, FileInode};
use alloc::{borrow::ToOwned, boxed::Box, string::String};
use core::ffi::c_void;
use error::Errno;
use libcommon::{read_le16, read_le32};
use vfs::{BlockDevice, Vnode, VnodeImpl, VnodeKind, VnodeRef};
@@ -97,10 +96,6 @@ impl VnodeImpl for DirectoryInode {
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
fn ioctl(&mut self, _node: VnodeRef, _cmd: u64, _value: *mut c_void) -> Result<isize, Errno> {
todo!()
}
}
impl Iterator for FatIterator<'_> {
-5
View File
@@ -1,5 +1,4 @@
use vfs::{VnodeImpl, VnodeRef, VnodeKind};
use core::ffi::c_void;
use error::Errno;
use crate::Bpb;
@@ -73,8 +72,4 @@ impl VnodeImpl for FileInode {
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
fn ioctl(&mut self, _node: VnodeRef, _cmd: u64, _value: *mut c_void) -> Result<isize, Errno> {
todo!()
}
}
+3 -15
View File
@@ -13,10 +13,9 @@ extern crate std;
use alloc::{boxed::Box, rc::Rc};
use core::cell::{RefCell, Ref};
use core::ffi::c_void;
use error::Errno;
use libcommon::*;
use vfs::{Filesystem, Vnode, VnodeImpl, VnodeKind, VnodeRef, BlockDevice};
use vfs::{Filesystem, Vnode, VnodeImpl, VnodeKind, VnodeRef, BlockDevice, FileMode};
use core::any::Any;
pub mod block;
@@ -78,10 +77,6 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
Ok(self.data.size())
}
fn ioctl(&mut self, _node: VnodeRef, _cmd: u64, _value: *mut c_void) -> Result<isize, Errno> {
todo!()
}
}
impl VnodeImpl for DirInode {
@@ -125,10 +120,6 @@ impl VnodeImpl for DirInode {
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
fn ioctl(&mut self, _node: VnodeRef, _cmd: u64, _value: *mut c_void) -> Result<isize, Errno> {
todo!()
}
}
impl<A: BlockAllocator + Copy + 'static> Filesystem for Ramfs<A> {
@@ -199,11 +190,8 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
if !do_create {
return Err(Errno::DoesNotExist);
}
// TODO use at.create() instead?
let node = self
.clone()
.create_node_initial(element, VnodeKind::Directory);
at.attach(node.clone());
// TODO file modes
let node = at.mkdir(element, FileMode::default_dir())?;
node
}
};
+3
View File
@@ -1,7 +1,10 @@
use error::Errno;
/// Block device interface
pub trait BlockDevice {
/// Reads blocks at offset `pos` into `buf`
fn read(&self, pos: usize, buf: &mut [u8]) -> Result<(), Errno>;
/// Writes blocks at offset `pos` from `buf`
fn write(&self, pos: usize, buf: &[u8]) -> Result<(), Errno>;
// TODO ioctl and stuff
}
+3 -11
View File
@@ -15,6 +15,7 @@ enum FileInner {
Socket,
}
/// Structure representing a file/socket opened for access
pub struct File {
inner: FileInner,
}
@@ -69,6 +70,7 @@ impl Seek for File {
}
impl File {
/// Constructs a new file handle for a regular file
pub fn normal(vnode: VnodeRef, pos: usize) -> Self {
Self {
inner: FileInner::Normal(NormalFile { vnode, pos }),
@@ -82,9 +84,8 @@ mod tests {
use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::ffi::c_void;
pub struct DummyInode;
struct DummyInode;
impl VnodeImpl for DummyInode {
fn create(&mut self, _at: VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
@@ -132,15 +133,6 @@ mod tests {
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
Err(Errno::NotImplemented)
}
fn ioctl(
&mut self,
_node: VnodeRef,
_cmd: u64,
_value: *mut c_void,
) -> Result<isize, Errno> {
Err(Errno::NotImplemented)
}
}
#[test]
+4
View File
@@ -4,8 +4,12 @@ use core::any::Any;
use alloc::rc::Rc;
use error::Errno;
/// General filesystem interface
pub trait Filesystem {
/// Returns root node of the filesystem
fn root(self: Rc<Self>) -> Result<VnodeRef, Errno>;
/// Returns storage device of the filesystem (if any)
fn dev(self: Rc<Self>) -> Option<&'static dyn BlockDevice>;
/// Returns filesystem's private data struct (if any)
fn data<'a>(&'a self) -> Option<Ref<'a, dyn Any>>;
}
+16 -12
View File
@@ -2,12 +2,14 @@ use crate::{FileMode, VnodeRef};
use error::Errno;
use libcommon::{path_component_left, path_component_right};
/// I/O context structure
pub struct Ioctx {
root: VnodeRef,
cwd: VnodeRef,
}
impl Ioctx {
/// Creates a new I/O context with given root node
pub fn new(root: VnodeRef) -> Self {
Self {
cwd: root.clone(),
@@ -49,6 +51,7 @@ impl Ioctx {
}
}
/// Looks up a path in given ioctx
pub fn find(&self, at: Option<VnodeRef>, mut path: &str) -> Result<VnodeRef, Errno> {
let at = if path.starts_with('/') {
path = path.trim_start_matches('/');
@@ -62,7 +65,13 @@ impl Ioctx {
self._find(at, path)
}
pub fn mkdir(&self, at: Option<VnodeRef>, path: &str, mode: FileMode) -> Result<VnodeRef, Errno> {
/// Creates a new directory
pub fn mkdir(
&self,
at: Option<VnodeRef>,
path: &str,
mode: FileMode,
) -> Result<VnodeRef, Errno> {
let (parent, name) = path_component_right(path);
self.find(at, parent)?.mkdir(name, mode)
}
@@ -73,12 +82,16 @@ mod tests {
use super::*;
use crate::{Vnode, VnodeImpl, VnodeKind};
use alloc::{boxed::Box, rc::Rc};
use core::ffi::c_void;
pub struct DummyInode;
impl VnodeImpl for DummyInode {
fn create(&mut self, _at: VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
fn create(
&mut self,
_at: VnodeRef,
name: &str,
kind: VnodeKind,
) -> Result<VnodeRef, Errno> {
let vnode = Vnode::new(name, kind, 0);
vnode.set_data(Box::new(DummyInode {}));
Ok(vnode)
@@ -115,15 +128,6 @@ mod tests {
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
fn ioctl(
&mut self,
_node: VnodeRef,
_cmd: u64,
_value: *mut c_void,
) -> Result<isize, Errno> {
todo!()
}
}
#[test]
+8 -6
View File
@@ -1,17 +1,19 @@
//! Virtual filesystem API and facilities
#![warn(missing_docs)]
#![feature(destructuring_assignment)]
#![no_std]
extern crate alloc;
pub mod block;
mod block;
pub use block::BlockDevice;
pub mod fs;
mod fs;
pub use fs::Filesystem;
pub mod stat;
mod stat;
pub use stat::FileMode;
pub mod node;
mod node;
pub use node::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
pub mod ioctx;
mod ioctx;
pub use ioctx::Ioctx;
pub mod file;
mod file;
pub use file::File;
+54 -22
View File
@@ -1,27 +1,33 @@
use crate::{File, FileMode, Filesystem};
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec};
use core::cell::{RefCell, RefMut};
use core::ffi::c_void;
use core::fmt;
use error::Errno;
/// Convenience type alias for [Rc<Vnode>]
pub type VnodeRef = Rc<Vnode>;
/// List of possible vnode types
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum VnodeKind {
/// Node is a directory with create/lookup/remove operations
Directory,
/// Node is a regular file
Regular,
}
pub struct TreeNode {
pub(crate) struct TreeNode {
parent: Option<VnodeRef>,
children: Vec<VnodeRef>,
}
/// File property cache struct
pub struct VnodeProps {
mode: FileMode,
}
/// Virtual filesystem node struct, generalizes access to
/// underlying real filesystems
pub struct Vnode {
name: String,
tree: RefCell<TreeNode>,
@@ -34,26 +40,42 @@ pub struct Vnode {
data: RefCell<Option<Box<dyn VnodeImpl>>>,
}
/// Interface for "inode" of a real filesystem
pub trait VnodeImpl {
// Directory-only operations
/// Creates a new vnode, sets it up, attaches it (in real FS) to `at` with `name` and
/// returns it
fn create(&mut self, at: VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno>;
/// Removes the filesystem inode from its parent by erasing its directory entry
fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>;
/// Looks up a corresponding directory entry for `name`. If present, loads its inode from
/// storage medium and returns a new vnode associated with it.
fn lookup(&mut self, at: VnodeRef, name: &str) -> Result<VnodeRef, Errno>;
/// Opens a vnode for access. Returns initial file position.
fn open(&mut self, node: VnodeRef /* TODO open mode */) -> Result<usize, Errno>;
/// Closes a vnode
fn close(&mut self, node: VnodeRef) -> Result<(), Errno>;
/// Changes file's underlying storage size
fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno>;
/// Reads `data.len()` bytes into the buffer from file offset `pos`
fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno>;
/// Writes `data.len()` bytes from the buffer to file offset `pos`.
/// Resizes the file storage if necessary.
fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno>;
/// Reports the size of this filesystem object in bytes
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno>;
fn ioctl(&mut self, node: VnodeRef, cmd: u64, value: *mut c_void) -> Result<isize, Errno>;
}
impl Vnode {
/// If set, allows [File] structures associated with a [Vnode] to
/// be seeked to arbitrary offsets
pub const SEEKABLE: u32 = 1 << 0;
/// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node
/// then needs to have [Vnode::set_data()] called on it to be usable.
pub fn new(name: &str, kind: VnodeKind, flags: u32) -> VnodeRef {
Rc::new(Self {
name: name.to_owned(),
@@ -71,30 +93,37 @@ impl Vnode {
})
}
/// Sets an associated [VnodeImpl] for the [Vnode]
pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
*self.data.borrow_mut() = Some(data);
}
/// Sets an associated [Filesystem] for the [Vnode]
pub fn set_fs(&self, fs: Rc<dyn Filesystem>) {
*self.fs.borrow_mut() = Some(fs);
}
/// Returns a reference to the associated [VnodeImpl]
pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
self.data.borrow_mut()
}
/// Returns the associated [Fileystem]
pub fn fs(&self) -> Option<Rc<dyn Filesystem>> {
self.fs.borrow().clone()
}
/// Returns `true` if the vnode represents a directory
pub fn is_directory(&self) -> bool {
self.kind == VnodeKind::Directory
}
/// Returns `true` if the vnode allows arbitrary seeking
pub fn is_seekable(&self) -> bool {
self.flags & Self::SEEKABLE != 0
}
/// Returns kind of the vnode
#[inline(always)]
pub const fn kind(&self) -> VnodeKind {
self.kind
@@ -102,6 +131,9 @@ impl Vnode {
// Tree operations
/// Attaches `child` vnode to `self` in in-memory tree. NOTE: does not
/// actually perform any real filesystem operations. Used to build
/// hierarchies for in-memory or volatile filesystems.
pub fn attach(self: &VnodeRef, child: VnodeRef) {
let parent_clone = self.clone();
let mut parent_borrow = self.tree.borrow_mut();
@@ -126,11 +158,14 @@ impl Vnode {
parent_borrow.children.remove(index);
}
/// Returns this vnode's parent or itself if it has none
pub fn parent(self: &VnodeRef) -> VnodeRef {
self.tree.borrow().parent.as_ref().unwrap_or(self).clone()
}
/// Looks up a child `name` in in-memory tree cache
pub fn lookup(self: &VnodeRef, name: &str) -> Option<VnodeRef> {
assert!(self.is_directory());
self.tree
.borrow()
.children
@@ -139,6 +174,8 @@ impl Vnode {
.cloned()
}
/// Looks up a child `name` in `self`. Will first try looking up a cached
/// vnode and will load it from disk if it's missing.
pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
if let Some(node) = self.lookup(name) {
return Ok(node);
@@ -156,13 +193,14 @@ impl Vnode {
}
}
/// Creates a new directory `name` in `self`
pub fn mkdir(self: &VnodeRef, name: &str, mode: FileMode) -> Result<VnodeRef, Errno> {
if self.kind != VnodeKind::Directory {
return Err(Errno::NotADirectory);
}
match self.lookup_or_load(name) {
Err(Errno::DoesNotExist) => {},
Err(Errno::DoesNotExist) => {}
Ok(_) => return Err(Errno::AlreadyExists),
e => return e,
};
@@ -180,6 +218,7 @@ impl Vnode {
}
}
/// Removes a directory entry `name` from `self`
pub fn unlink(self: &VnodeRef, name: &str) -> Result<(), Errno> {
if self.kind != VnodeKind::Directory {
return Err(Errno::NotADirectory);
@@ -195,6 +234,7 @@ impl Vnode {
}
}
/// Opens a vnode for access
pub fn open(self: &VnodeRef) -> Result<File, Errno> {
if self.kind != VnodeKind::Regular {
return Err(Errno::IsADirectory);
@@ -208,6 +248,7 @@ impl Vnode {
}
}
/// Reads data from offset `pos` into `buf`
pub fn read(self: &VnodeRef, pos: usize, buf: &mut [u8]) -> Result<usize, Errno> {
if self.kind != VnodeKind::Regular {
return Err(Errno::IsADirectory);
@@ -220,6 +261,7 @@ impl Vnode {
}
}
/// Writes data from `buf` to offset `pos`
pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result<usize, Errno> {
if self.kind != VnodeKind::Regular {
return Err(Errno::IsADirectory);
@@ -232,6 +274,7 @@ impl Vnode {
}
}
/// Resizes the vnode data
pub fn truncate(self: &VnodeRef, size: usize) -> Result<(), Errno> {
if self.kind != VnodeKind::Regular {
return Err(Errno::IsADirectory);
@@ -244,6 +287,7 @@ impl Vnode {
}
}
/// Returns current vnode data size
pub fn size(self: &VnodeRef) -> Result<usize, Errno> {
if let Some(ref mut data) = *self.data() {
data.size(self.clone())
@@ -251,14 +295,6 @@ impl Vnode {
Err(Errno::NotImplemented)
}
}
pub fn ioctl(self: &VnodeRef, cmd: u64, value: *mut c_void) -> Result<isize, Errno> {
if let Some(ref mut data) = *self.data() {
data.ioctl(self.clone(), cmd, value)
} else {
Err(Errno::NotImplemented)
}
}
}
impl fmt::Debug for Vnode {
@@ -274,7 +310,12 @@ mod tests {
pub struct DummyInode;
impl VnodeImpl for DummyInode {
fn create(&mut self, _at: VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
fn create(
&mut self,
_at: VnodeRef,
name: &str,
kind: VnodeKind,
) -> Result<VnodeRef, Errno> {
let node = Vnode::new(name, kind, 0);
node.set_data(Box::new(DummyInode {}));
Ok(node)
@@ -311,15 +352,6 @@ mod tests {
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
Err(Errno::NotImplemented)
}
fn ioctl(
&mut self,
_node: VnodeRef,
_cmd: u64,
_value: *mut c_void,
) -> Result<isize, Errno> {
Err(Errno::NotImplemented)
}
}
#[test]
+22 -9
View File
@@ -1,28 +1,41 @@
use core::fmt;
/// Wrapper type for file mode/permissions
#[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;
/// File is user-readable
pub const USER_READ: u16 = 1 << 8;
/// File is user-writeable
pub const USER_WRITE: u16 = 1 << 7;
/// File is user-executable
pub const USER_EXEC: u16 = 1 << 6;
/// File is group-readable
pub const GROUP_READ: u16 = 1 << 5;
/// File is group-writeable
pub const GROUP_WRITE: u16 = 1 << 4;
/// File is group-executable
pub const GROUP_EXEC: u16 = 1 << 3;
/// File is readable by anyone
pub const OTHER_READ: u16 = 1 << 2;
/// File is writable by anyone
pub const OTHER_WRITE: u16 = 1 << 1;
/// File is executable by anyone
pub const OTHER_EXEC: u16 = 1 << 0;
/// Returns an empty permission set
pub const fn empty() -> Self {
Self(0)
}
/// Returns default permission set for directories
pub const fn default_dir() -> Self {
Self(0o755)
}
/// Returns default permission set for regular files
pub const fn default_reg() -> Self {
Self(0o644)
}