Module mount

Module mount 

Source
Expand description

挂载表管理

该模块实现了 VFS 的挂载功能,支持在不同路径挂载多个文件系统,类似 Linux 的挂载机制。

§核心组件

§核心概念

§挂载的本质

挂载是将一个文件系统的根目录“覆盖“到另一个文件系统的某个目录上:

挂载前:
/
├── 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§

MOUNT_TABLE
MountFlags
MountPoint
挂载点信息
MountTable
全局挂载表

Functions§

get_root_dentry
获取根 dentry