From 2d01181104f08545b15752431400209a3c009ef9 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Mon, 15 Jul 2019 15:37:59 +0800 Subject: [PATCH] add MountFS from kernel --- Cargo.toml | 3 +- README.md | 1 + rcore-fs-mountfs/Cargo.toml | 14 ++ rcore-fs-mountfs/src/lib.rs | 312 ++++++++++++++++++++++++++++++++++ rcore-fs-mountfs/src/tests.rs | 18 ++ 5 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 rcore-fs-mountfs/Cargo.toml create mode 100644 rcore-fs-mountfs/src/lib.rs create mode 100644 rcore-fs-mountfs/src/tests.rs diff --git a/Cargo.toml b/Cargo.toml index 8d882d9..f624e45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "rcore-fs-sefs", "rcore-fs-fuse", "rcore-fs-ext2", - "rcore-fs-ramfs" + "rcore-fs-ramfs", + "rcore-fs-mountfs" ] exclude = ["sefs-fuse"] diff --git a/README.md b/README.md index 4528f13..4861451 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Specific file systems: * `rcore-fs-sefs`: Simple Encrypted File System * `rcore-fs-ext2`: Ext2 * `rcore-fs-ramfs`: RAM based FS +* `rcore-fs-mountfs`: Mountable FS wrapper Utilities: diff --git a/rcore-fs-mountfs/Cargo.toml b/rcore-fs-mountfs/Cargo.toml new file mode 100644 index 0000000..fbbf73c --- /dev/null +++ b/rcore-fs-mountfs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rcore-fs-mountfs" +version = "0.1.0" +authors = ["gjz010 ", "WangRunji "] +edition = "2018" + +[dependencies] +rcore-fs = { path = "../rcore-fs" } +spin = "0.5" +log = "0.4" +lazy_static = { version = "1.3", features = ["spin_no_std"] } + +[dev-dependencies] +rcore-fs-ramfs = { path = "../rcore-fs-ramfs" } diff --git a/rcore-fs-mountfs/src/lib.rs b/rcore-fs-mountfs/src/lib.rs new file mode 100644 index 0000000..edd4b2b --- /dev/null +++ b/rcore-fs-mountfs/src/lib.rs @@ -0,0 +1,312 @@ +#![cfg_attr(not(any(test, feature = "std")), no_std)] +#![feature(alloc)] + +extern crate alloc; +#[macro_use] +extern crate log; +#[macro_use] +extern crate lazy_static; + +use alloc::{ + collections::BTreeMap, + string::String, + sync::{Arc, Weak}, +}; +use core::any::Any; +use core::mem::uninitialized; +use rcore_fs::vfs::*; +use spin::RwLock; + +#[cfg(test)] +mod tests; + +/// The filesystem on which all the other filesystems are mounted +pub struct MountFS { + filesystem: Arc, + mountpoints: RwLock>>, + self_mountpoint: Option>, + self_ref: Weak, +} + +type INodeId = usize; + +lazy_static! { + // XXX: what's the meaning? + // The unsafe filesystem for Stdin, Stdout, anonymous pipe and so on. + // If you don't touch it you will not break it. + // But in fact you should detect file operations (e.g. fstat) on virtual files and prevent them. + static ref ANONYMOUS_FS: Arc + = Arc::new(unsafe { uninitialized() }); +} + +pub struct MNode { + pub inode: Arc, + pub vfs: Arc, + self_ref: Weak, +} + +impl MountFS { + pub fn new(fs: Arc) -> Arc { + MountFS { + filesystem: fs, + mountpoints: RwLock::new(BTreeMap::new()), + self_mountpoint: None, + self_ref: Weak::default(), + } + .wrap() + } + + /// Wrap pure `MountFS` with `Arc<..>`. + /// Used in constructors. + fn wrap(self) -> Arc { + // Create an Arc, make a Weak from it, then put it into the struct. + // It's a little tricky. + let fs = Arc::new(self); + let weak = Arc::downgrade(&fs); + let ptr = Arc::into_raw(fs) as *mut Self; + unsafe { + (*ptr).self_ref = weak; + Arc::from_raw(ptr) + } + } + + fn _root_inode(&self) -> Arc { + MNode { + inode: self.filesystem.root_inode(), + vfs: self.self_ref.upgrade().unwrap(), + self_ref: Weak::default(), + } + .wrap() + } +} + +impl MNode { + /// Wrap pure `INode` with `Arc<..>`. + /// Used in constructors. + fn wrap(self) -> Arc { + // Create an Arc, make a Weak from it, then put it into the struct. + // It's a little tricky. + let inode = Arc::new(self); + let weak = Arc::downgrade(&inode); + let ptr = Arc::into_raw(inode) as *mut Self; + unsafe { + (*ptr).self_ref = weak; + Arc::from_raw(ptr) + } + } + + /// Mount file system `fs` at this INode + pub fn mount(&self, fs: Arc) -> Result> { + let new_fs = MountFS { + filesystem: fs, + mountpoints: RwLock::new(BTreeMap::new()), + self_mountpoint: Some(self.self_ref.upgrade().unwrap()), + self_ref: Weak::default(), + } + .wrap(); + let inode_id = self.inode.metadata()?.inode; + self.vfs + .mountpoints + .write() + .insert(inode_id, new_fs.clone()); + Ok(new_fs) + } + + /// Get the root INode of the mounted fs at here. + /// Return self if no mounted fs. + fn overlaid_inode(&self) -> Arc { + let inode_id = self.metadata().unwrap().inode; + if let Some(sub_vfs) = self.vfs.mountpoints.read().get(&inode_id) { + sub_vfs._root_inode() + } else { + self.self_ref.upgrade().unwrap() + } + } + + fn is_root_inode(&self) -> bool { + self.inode.fs().root_inode().metadata().unwrap().inode + == self.inode.metadata().unwrap().inode + } + + /// Creates an anonymous inode. + /// Should not be used as a location at any time, or be totally released at any time. + pub unsafe fn anonymous_inode(inode: Arc) -> Arc { + MNode { + inode, + vfs: ANONYMOUS_FS.clone(), + self_ref: Weak::default(), + } + .wrap() + } + pub unsafe fn is_anonymous(&self) -> bool { + Arc::ptr_eq(&self.vfs, &ANONYMOUS_FS) + } + + pub fn create(&self, name: &str, type_: FileType, mode: u32) -> Result> { + Ok(MNode { + inode: self.inode.create(name, type_, mode)?, + vfs: self.vfs.clone(), + self_ref: Weak::default(), + } + .wrap()) + } + + /// Does a one-level finding. + pub fn find(&self, root: bool, name: &str) -> Result> { + match name { + "" | "." => Ok(self.self_ref.upgrade().unwrap()), + ".." => { + // Going Up + // We need to check these things: + // 1. Is going forward allowed, considering the current root? + // 2. Is going forward trespassing the filesystem border, + // thus requires falling back to parent of original_mountpoint? + // TODO: check going up. + if root { + Ok(self.self_ref.upgrade().unwrap()) + } else if self.is_root_inode() { + // Here is mountpoint. + match &self.vfs.self_mountpoint { + Some(inode) => inode.find(root, ".."), + // root fs + None => Ok(self.self_ref.upgrade().unwrap()), + } + } else { + // Not trespassing filesystem border. Parent and myself in the same filesystem. + Ok(MNode { + inode: self.inode.find(name)?, // Going up is handled by the filesystem. A better API? + vfs: self.vfs.clone(), + self_ref: Weak::default(), + } + .wrap()) + } + } + _ => { + // Going down may trespass the filesystem border. + // An INode replacement is required here. + Ok(MNode { + inode: self.overlaid_inode().inode.find(name)?, + vfs: self.vfs.clone(), + self_ref: Weak::default(), + } + .wrap() + .overlaid_inode()) + } + } + } + + /// If `child` is a child of `self`, return its name. + pub fn find_name_by_child(&self, child: &Arc) -> Result { + for index in 0.. { + let name = self.inode.get_entry(index)?; + match name.as_ref() { + "." | ".." => {} + _ => { + let queryback = self.find(false, &name)?.overlaid_inode(); + // TODO: mountpoint check! + debug!("checking name {}", name); + if Arc::ptr_eq(&queryback.vfs, &child.vfs) + && queryback.inode.metadata()?.inode == child.inode.metadata()?.inode + { + return Ok(name); + } + } + } + } + Err(FsError::EntryNotFound) + } +} + +impl FileSystem for MountFS { + fn sync(&self) -> Result<()> { + self.filesystem.sync()?; + Ok(()) + } + + fn root_inode(&self) -> Arc { + self._root_inode() + } + + fn info(&self) -> FsInfo { + self.filesystem.info() + } +} + +// unwrap `MNode` and forward methods to inner except `find()` +impl INode for MNode { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { + self.inode.read_at(offset, buf) + } + + fn write_at(&self, offset: usize, buf: &[u8]) -> Result { + self.inode.write_at(offset, buf) + } + + fn poll(&self) -> Result { + self.inode.poll() + } + + fn metadata(&self) -> Result { + self.inode.metadata() + } + + fn set_metadata(&self, metadata: &Metadata) -> Result<()> { + self.inode.set_metadata(metadata) + } + + fn sync_all(&self) -> Result<()> { + self.inode.sync_all() + } + + fn sync_data(&self) -> Result<()> { + self.inode.sync_data() + } + + fn resize(&self, len: usize) -> Result<()> { + self.inode.resize(len) + } + + fn create(&self, name: &str, type_: FileType, mode: u32) -> Result> { + Ok(self.create(name, type_, mode)?) + } + + fn link(&self, name: &str, other: &Arc) -> Result<()> { + let other = &other + .downcast_ref::() + .ok_or(FsError::NotSameFs)? + .inode; + self.inode.link(name, other) + } + + fn unlink(&self, name: &str) -> Result<()> { + self.inode.unlink(name) + } + + fn move_(&self, old_name: &str, target: &Arc, new_name: &str) -> Result<()> { + let target = &target + .downcast_ref::() + .ok_or(FsError::NotSameFs)? + .inode; + self.inode.move_(old_name, target, new_name) + } + + fn find(&self, name: &str) -> Result> { + Ok(self.find(false, name)?) + } + + fn get_entry(&self, id: usize) -> Result { + self.inode.get_entry(id) + } + + fn io_control(&self, cmd: u32, data: usize) -> Result<()> { + self.inode.io_control(cmd, data) + } + + fn fs(&self) -> Arc { + self.inode.fs() + } + + fn as_any_ref(&self) -> &dyn Any { + self + } +} diff --git a/rcore-fs-mountfs/src/tests.rs b/rcore-fs-mountfs/src/tests.rs new file mode 100644 index 0000000..b796fc7 --- /dev/null +++ b/rcore-fs-mountfs/src/tests.rs @@ -0,0 +1,18 @@ +use crate::*; +use rcore_fs::vfs::*; +use rcore_fs_ramfs::RamFS; + +#[test] +fn mount() { + let rootfs = MountFS::new(RamFS::new()); + let root = rootfs.root_inode(); + let mnt = root.create("mnt", FileType::Dir, 0o777).unwrap(); + + let ramfs = RamFS::new(); + let root1 = ramfs.root_inode(); + root1.create("file", FileType::File, 0o777).unwrap(); + + assert!(mnt.downcast_ref::().unwrap().mount(ramfs).is_ok()); + assert!(mnt.find("file").is_ok()); + assert!(root.lookup("mnt/file").is_ok()); +}