add MountFS from kernel

master
WangRunji 5 years ago
parent c7d65a5a7c
commit 2d01181104

@ -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"]

@ -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:

@ -0,0 +1,14 @@
[package]
name = "rcore-fs-mountfs"
version = "0.1.0"
authors = ["gjz010 <gjz010944@gmail.com>", "WangRunji <wangrunji0408@163.com>"]
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" }

@ -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<dyn FileSystem>,
mountpoints: RwLock<BTreeMap<INodeId, Arc<MountFS>>>,
self_mountpoint: Option<Arc<MNode>>,
self_ref: Weak<MountFS>,
}
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<MountFS>
= Arc::new(unsafe { uninitialized() });
}
pub struct MNode {
pub inode: Arc<dyn INode>,
pub vfs: Arc<MountFS>,
self_ref: Weak<MNode>,
}
impl MountFS {
pub fn new(fs: Arc<dyn FileSystem>) -> Arc<Self> {
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<Self> {
// 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> {
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<Self> {
// 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<dyn FileSystem>) -> Result<Arc<MountFS>> {
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<MNode> {
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<dyn INode>) -> Arc<MNode> {
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<Arc<Self>> {
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<Arc<Self>> {
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<MNode>) -> Result<String> {
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<dyn INode> {
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<usize> {
self.inode.read_at(offset, buf)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
self.inode.write_at(offset, buf)
}
fn poll(&self) -> Result<PollStatus> {
self.inode.poll()
}
fn metadata(&self) -> Result<Metadata> {
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<Arc<dyn INode>> {
Ok(self.create(name, type_, mode)?)
}
fn link(&self, name: &str, other: &Arc<dyn INode>) -> Result<()> {
let other = &other
.downcast_ref::<Self>()
.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<dyn INode>, new_name: &str) -> Result<()> {
let target = &target
.downcast_ref::<Self>()
.ok_or(FsError::NotSameFs)?
.inode;
self.inode.move_(old_name, target, new_name)
}
fn find(&self, name: &str) -> Result<Arc<dyn INode>> {
Ok(self.find(false, name)?)
}
fn get_entry(&self, id: usize) -> Result<String> {
self.inode.get_entry(id)
}
fn io_control(&self, cmd: u32, data: usize) -> Result<()> {
self.inode.io_control(cmd, data)
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.inode.fs()
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}

@ -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::<MNode>().unwrap().mount(ramfs).is_ok());
assert!(mnt.find("file").is_ok());
assert!(root.lookup("mnt/file").is_ok());
}
Loading…
Cancel
Save