Expand description
挂载表管理
该模块实现了 VFS 的挂载功能,支持在不同路径挂载多个文件系统,类似 Linux 的挂载机制。
§核心组件
MountTable- 全局挂载表,管理所有挂载点MountPoint- 单个挂载点信息MountFlags- 挂载标志(只读、禁止执行等)MOUNT_TABLE- 全局挂载表单例get_root_dentry- 获取根文件系统的 Dentry
§核心概念
§挂载的本质
挂载是将一个文件系统的根目录“覆盖“到另一个文件系统的某个目录上:
挂载前:
/
├── mnt/ (tmpfs 目录)
│ └── old_file
└── etc/
挂载 fat32 到 /mnt 后:
/
├── mnt/ ─┬─> tmpfs 的 /mnt/ (被覆盖)
│ └─> fat32 的 / (新可见)
│ ├── file1.txt
│ └── file2.txt
└── etc/§挂载点栈
同一路径可以多次挂载,形成栈结构,最后挂载的文件系统覆盖前面的:
/mnt → [tmpfs1, tmpfs2, fat32]
↑
当前可见
umount /mnt → [tmpfs1, tmpfs2]
↑
tmpfs2 变为可见§最长前缀匹配
访问路径时,选择最长匹配的挂载点:
挂载情况:
/ → tmpfs
/mnt → fat32
/mnt/data → ext4
访问 "/mnt/data/file" → 使用 ext4 (最长匹配)
访问 "/mnt/other" → 使用 fat32
访问 "/etc/passwd" → 使用 tmpfs (根目录)§挂载标志
§MountFlags
bitflags! {
pub struct MountFlags: u32 {
const READ_ONLY = 1 << 0; // 只读挂载
const NO_EXEC = 1 << 1; // 禁止执行
const NO_SUID = 1 << 2; // 忽略 SUID/SGID
const SYNC = 1 << 3; // 同步写入
const NO_DEV = 1 << 4; // 禁止设备文件
}
}§标志用途
- READ_ONLY: 防止修改文件系统(如挂载只读光盘)
- NO_EXEC: 安全策略,防止执行恶意程序
- NO_SUID: 防止特权提升攻击
- SYNC: 确保数据持久化(牺牲性能)
- NO_DEV: 防止通过设备文件访问硬件
§MountPoint 结构
pub struct MountPoint {
pub fs: Arc<dyn FileSystem>, // 挂载的文件系统
pub root: Arc<Dentry>, // 文件系统的根 Dentry
pub flags: MountFlags, // 挂载标志
pub device: Option<String>, // 设备路径(如 "/dev/sda1")
pub mount_path: String, // 挂载路径
}§挂载表实现
§数据结构
MountTable
┌─────────────┬────────────────────────┐
│ "/" │ [tmpfs_root] │
│ "/mnt" │ [fat32_mp] │
│ "/mnt/data" │ [ext4_mp1, ext4_mp2] │
└─────────────┴────────────────────────┘
↑
栈顶=当前可见§线程安全
挂载表使用 SpinLock 保护,支持并发访问:
struct MountTable {
mounts: SpinLock<BTreeMap<String, Vec<Arc<MountPoint>>>>,
}§使用示例
§挂载文件系统
use vfs::{MOUNT_TABLE, MountFlags};
use alloc::sync::Arc;
// 1. 创建文件系统
let tmpfs = Arc::new(TmpFs::new());
// 2. 挂载到 /tmp
MOUNT_TABLE.mount(
tmpfs,
"/tmp",
MountFlags::empty(),
None
)?;
// 3. 只读挂载
MOUNT_TABLE.mount(
fat32_fs,
"/mnt/usb",
MountFlags::READ_ONLY | MountFlags::NO_EXEC,
Some(String::from("/dev/sda1"))
)?;§卸载文件系统
// 卸载 /tmp
MOUNT_TABLE.umount("/tmp")?;
// 注意:不能卸载根文件系统
MOUNT_TABLE.umount("/")?; // 返回 Err(NotSupported)§查找挂载点
// 查找路径对应的挂载点
if let Some(mp) = MOUNT_TABLE.find_mount("/mnt/data/file") {
println!("文件系统类型: {}", mp.fs.fs_type());
println!("挂载路径: {}", mp.mount_path);
}§列出所有挂载点
// 调试用:列出所有挂载
let mounts = MOUNT_TABLE.list_mounts();
for (path, fstype) in mounts {
println!("{} on {} type {}", path, path, fstype);
}
// 输出:
// / on / type tmpfs
// /mnt on /mnt type fat32
// /mnt/data on /mnt/data type ext4§系统初始化挂载根文件系统
pub fn init_rootfs() -> Result<(), FsError> {
// 创建 tmpfs 作为根文件系统
let tmpfs = Arc::new(TmpFs::new());
// 挂载到 /
MOUNT_TABLE.mount(tmpfs, "/", MountFlags::empty(), None)?;
// 创建基本目录结构
let root = get_root_dentry()?;
root.inode.mkdir("dev", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
root.inode.mkdir("etc", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
root.inode.mkdir("tmp", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
root.inode.mkdir("mnt", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
Ok(())
}§挂载与 Dentry 缓存的交互
挂载和卸载时会自动更新 Dentry 缓存:
// 挂载时:如果挂载点 Dentry 在缓存中,更新其挂载信息
if let Some(dentry) = DENTRY_CACHE.lookup(&mount_path) {
dentry.set_mount(&mount_point.root);
}
// 卸载时:恢复为下层挂载或清除挂载标记
if let Some(dentry) = DENTRY_CACHE.lookup(&mount_path) {
if let Some(underlying) = underlying_mount {
dentry.set_mount(&underlying.root);
} else {
dentry.clear_mount();
}
}Structs§
Functions§
- get_
root_ dentry - 获取根 dentry