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