parent
c7d65a5a7c
commit
2d01181104
@ -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…
Reference in new issue