os/vfs/
mount.rs

1//! 挂载表管理
2//!
3//! 该模块实现了 VFS 的挂载功能,支持在不同路径挂载多个文件系统,类似 Linux 的挂载机制。
4//!
5//! # 核心组件
6//!
7//! - [`MountTable`] - 全局挂载表,管理所有挂载点
8//! - [`MountPoint`] - 单个挂载点信息
9//! - [`MountFlags`] - 挂载标志(只读、禁止执行等)
10//! - [`MOUNT_TABLE`] - 全局挂载表单例
11//! - [`get_root_dentry`] - 获取根文件系统的 Dentry
12//!
13//! # 核心概念
14//!
15//! ## 挂载的本质
16//!
17//! 挂载是将一个文件系统的根目录"覆盖"到另一个文件系统的某个目录上:
18//!
19//! ```text
20//! 挂载前:
21//! /
22//! ├── mnt/          (tmpfs 目录)
23//! │   └── old_file
24//! └── etc/
25//!
26//! 挂载 fat32 到 /mnt 后:
27//! /
28//! ├── mnt/ ─┬─> tmpfs 的 /mnt/ (被覆盖)
29//! │         └─> fat32 的 / (新可见)
30//! │             ├── file1.txt
31//! │             └── file2.txt
32//! └── etc/
33//! ```
34//!
35//! ## 挂载点栈
36//!
37//! 同一路径可以多次挂载,形成栈结构,最后挂载的文件系统覆盖前面的:
38//!
39//! ```text
40//! /mnt → [tmpfs1, tmpfs2, fat32]
41//!                          ↑
42//!                      当前可见
43//!
44//! umount /mnt → [tmpfs1, tmpfs2]
45//!                        ↑
46//!                    tmpfs2 变为可见
47//! ```
48//!
49//! ## 最长前缀匹配
50//!
51//! 访问路径时,选择最长匹配的挂载点:
52//!
53//! ```text
54//! 挂载情况:
55//! /          → tmpfs
56//! /mnt       → fat32
57//! /mnt/data  → ext4
58//!
59//! 访问 "/mnt/data/file" → 使用 ext4 (最长匹配)
60//! 访问 "/mnt/other"     → 使用 fat32
61//! 访问 "/etc/passwd"    → 使用 tmpfs (根目录)
62//! ```
63//!
64//! # 挂载标志
65//!
66//! ## MountFlags
67//!
68//! ```rust
69//! bitflags! {
70//!     pub struct MountFlags: u32 {
71//!         const READ_ONLY  = 1 << 0;  // 只读挂载
72//!         const NO_EXEC    = 1 << 1;  // 禁止执行
73//!         const NO_SUID    = 1 << 2;  // 忽略 SUID/SGID
74//!         const SYNC       = 1 << 3;  // 同步写入
75//!         const NO_DEV     = 1 << 4;  // 禁止设备文件
76//!     }
77//! }
78//! ```
79//!
80//! ## 标志用途
81//!
82//! - **READ_ONLY**: 防止修改文件系统(如挂载只读光盘)
83//! - **NO_EXEC**: 安全策略,防止执行恶意程序
84//! - **NO_SUID**: 防止特权提升攻击
85//! - **SYNC**: 确保数据持久化(牺牲性能)
86//! - **NO_DEV**: 防止通过设备文件访问硬件
87//!
88//! # MountPoint 结构
89//!
90//! ```rust
91//! pub struct MountPoint {
92//!     pub fs: Arc<dyn FileSystem>,  // 挂载的文件系统
93//!     pub root: Arc<Dentry>,        // 文件系统的根 Dentry
94//!     pub flags: MountFlags,        // 挂载标志
95//!     pub device: Option<String>,   // 设备路径(如 "/dev/sda1")
96//!     pub mount_path: String,       // 挂载路径
97//! }
98//! ```
99//!
100//! # 挂载表实现
101//!
102//! ## 数据结构
103//!
104//! ```text
105//! MountTable
106//! ┌─────────────┬────────────────────────┐
107//! │ "/"         │ [tmpfs_root]           │
108//! │ "/mnt"      │ [fat32_mp]             │
109//! │ "/mnt/data" │ [ext4_mp1, ext4_mp2]   │
110//! └─────────────┴────────────────────────┘
111//!                              ↑
112//!                          栈顶=当前可见
113//! ```
114//!
115//! ## 线程安全
116//!
117//! 挂载表使用 `SpinLock` 保护,支持并发访问:
118//!
119//! ```rust
120//! struct MountTable {
121//!     mounts: SpinLock<BTreeMap<String, Vec<Arc<MountPoint>>>>,
122//! }
123//! ```
124//!
125//! # 使用示例
126//!
127//! ## 挂载文件系统
128//!
129//! ```rust
130//! use vfs::{MOUNT_TABLE, MountFlags};
131//! use alloc::sync::Arc;
132//!
133//! // 1. 创建文件系统
134//! let tmpfs = Arc::new(TmpFs::new());
135//!
136//! // 2. 挂载到 /tmp
137//! MOUNT_TABLE.mount(
138//!     tmpfs,
139//!     "/tmp",
140//!     MountFlags::empty(),
141//!     None
142//! )?;
143//!
144//! // 3. 只读挂载
145//! MOUNT_TABLE.mount(
146//!     fat32_fs,
147//!     "/mnt/usb",
148//!     MountFlags::READ_ONLY | MountFlags::NO_EXEC,
149//!     Some(String::from("/dev/sda1"))
150//! )?;
151//! ```
152//!
153//! ## 卸载文件系统
154//!
155//! ```rust
156//! // 卸载 /tmp
157//! MOUNT_TABLE.umount("/tmp")?;
158//!
159//! // 注意:不能卸载根文件系统
160//! MOUNT_TABLE.umount("/")?;  // 返回 Err(NotSupported)
161//! ```
162//!
163//! ## 查找挂载点
164//!
165//! ```rust
166//! // 查找路径对应的挂载点
167//! if let Some(mp) = MOUNT_TABLE.find_mount("/mnt/data/file") {
168//!     println!("文件系统类型: {}", mp.fs.fs_type());
169//!     println!("挂载路径: {}", mp.mount_path);
170//! }
171//! ```
172//!
173//! ## 列出所有挂载点
174//!
175//! ```rust
176//! // 调试用:列出所有挂载
177//! let mounts = MOUNT_TABLE.list_mounts();
178//! for (path, fstype) in mounts {
179//!     println!("{} on {} type {}", path, path, fstype);
180//! }
181//! // 输出:
182//! // / on / type tmpfs
183//! // /mnt on /mnt type fat32
184//! // /mnt/data on /mnt/data type ext4
185//! ```
186//!
187//! ## 系统初始化挂载根文件系统
188//!
189//! ```rust
190//! pub fn init_rootfs() -> Result<(), FsError> {
191//!     // 创建 tmpfs 作为根文件系统
192//!     let tmpfs = Arc::new(TmpFs::new());
193//!
194//!     // 挂载到 /
195//!     MOUNT_TABLE.mount(tmpfs, "/", MountFlags::empty(), None)?;
196//!
197//!     // 创建基本目录结构
198//!     let root = get_root_dentry()?;
199//!     root.inode.mkdir("dev", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
200//!     root.inode.mkdir("etc", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
201//!     root.inode.mkdir("tmp", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
202//!     root.inode.mkdir("mnt", FileMode::S_IFDIR | FileMode::S_IRWXU)?;
203//!
204//!     Ok(())
205//! }
206//! ```
207//!
208//! # 挂载与 Dentry 缓存的交互
209//!
210//! 挂载和卸载时会自动更新 Dentry 缓存:
211//!
212//! ```rust
213//! // 挂载时:如果挂载点 Dentry 在缓存中,更新其挂载信息
214//! if let Some(dentry) = DENTRY_CACHE.lookup(&mount_path) {
215//!     dentry.set_mount(&mount_point.root);
216//! }
217//!
218//! // 卸载时:恢复为下层挂载或清除挂载标记
219//! if let Some(dentry) = DENTRY_CACHE.lookup(&mount_path) {
220//!     if let Some(underlying) = underlying_mount {
221//!         dentry.set_mount(&underlying.root);
222//!     } else {
223//!         dentry.clear_mount();
224//!     }
225//! }
226//! ```
227
228use crate::sync::SpinLock;
229use crate::vfs::{Dentry, FileMode, FileSystem, FsError};
230use alloc::collections::BTreeMap;
231use alloc::string::String;
232use alloc::sync::Arc;
233use alloc::vec::Vec;
234
235/// 挂载标志
236bitflags::bitflags! {
237    pub struct MountFlags: u32 {
238        /// 只读挂载
239        const READ_ONLY  = 1 << 0;
240
241        /// 禁止执行
242        const NO_EXEC    = 1 << 1;
243
244        /// 忽略 SUID/SGID 位
245        const NO_SUID    = 1 << 2;
246
247        /// 同步写入
248        const SYNC       = 1 << 3;
249
250        /// 禁止设备文件
251        const NO_DEV     = 1 << 4;
252    }
253}
254
255/// 挂载点信息
256pub struct MountPoint {
257    /// 挂载的文件系统
258    pub fs: Arc<dyn FileSystem>,
259
260    /// 挂载点的根 dentry
261    pub root: Arc<Dentry>,
262
263    /// 挂载标志
264    pub flags: MountFlags,
265
266    /// 设备路径(如果有)
267    pub device: Option<String>,
268
269    /// 挂载路径
270    pub mount_path: String,
271}
272
273impl MountPoint {
274    /// 创建新的挂载点
275    pub fn new(
276        fs: Arc<dyn FileSystem>,
277        mount_path: String,
278        flags: MountFlags,
279        device: Option<String>,
280    ) -> Arc<Self> {
281        let root_inode = fs.root_inode();
282        let root = Dentry::new(String::from("/"), root_inode);
283
284        Arc::new(Self {
285            fs,
286            root,
287            flags,
288            device,
289            mount_path,
290        })
291    }
292}
293
294/// 全局挂载表
295pub struct MountTable {
296    /// 挂载路径 -> 挂载点栈(最后一个是当前可见的)
297    mounts: SpinLock<BTreeMap<String, Vec<Arc<MountPoint>>>>,
298}
299
300impl MountTable {
301    /// 创建新的挂载表
302    pub const fn new() -> Self {
303        Self {
304            mounts: SpinLock::new(BTreeMap::new()),
305        }
306    }
307
308    /// 挂载文件系统
309    pub fn mount(
310        &self,
311        fs: Arc<dyn FileSystem>,
312        path: &str,
313        flags: MountFlags,
314        device: Option<String>,
315    ) -> Result<(), FsError> {
316        use crate::vfs::normalize_path;
317
318        let normalized_path = normalize_path(path);
319
320        // 创建挂载点
321        let mount_point = MountPoint::new(fs, normalized_path.clone(), flags, device);
322
323        // 添加到挂载栈
324        let mut mounts = self.mounts.lock();
325        mounts
326            .entry(normalized_path.clone())
327            .or_insert_with(Vec::new)
328            .push(mount_point.clone());
329
330        // 如果挂载点的 dentry 已经存在于缓存中,更新其挂载信息
331        if let Some(dentry) = crate::vfs::DENTRY_CACHE.lookup(&normalized_path) {
332            dentry.set_mount(&mount_point.root);
333        }
334
335        Ok(())
336    }
337
338    /// 卸载文件系统
339    pub fn umount(&self, path: &str) -> Result<(), FsError> {
340        use crate::vfs::normalize_path;
341
342        let normalized_path = normalize_path(path);
343
344        // 不允许卸载根文件系统
345        if normalized_path == "/" {
346            return Err(FsError::NotSupported);
347        }
348
349        let mut mounts = self.mounts.lock();
350        let stack = mounts.get_mut(&normalized_path).ok_or(FsError::NotFound)?;
351
352        // 弹出栈顶的挂载点
353        let mount_point = stack.pop().ok_or(FsError::NotFound)?;
354
355        // 如果栈为空,移除整个条目
356        if stack.is_empty() {
357            mounts.remove(&normalized_path);
358        }
359
360        // 释放锁,避免在同步/卸载时持有锁
361        drop(mounts);
362
363        // 同步文件系统
364        mount_point.fs.sync()?;
365
366        // 执行卸载清理
367        mount_point.fs.umount()?;
368
369        // 更新 dentry 缓存
370        if let Some(dentry) = crate::vfs::DENTRY_CACHE.lookup(&normalized_path) {
371            // 如果还有下层挂载,更新为下层挂载点
372            let mounts = self.mounts.lock();
373            if let Some(stack) = mounts.get(&normalized_path) {
374                if let Some(underlying_mount) = stack.last() {
375                    dentry.set_mount(&underlying_mount.root);
376                } else {
377                    dentry.clear_mount();
378                }
379            } else {
380                dentry.clear_mount();
381            }
382        }
383
384        Ok(())
385    }
386
387    /// 查找给定路径的挂载点
388    ///
389    /// 返回最长匹配的挂载点(栈顶)
390    pub fn find_mount(&self, path: &str) -> Option<Arc<MountPoint>> {
391        use crate::vfs::normalize_path;
392
393        let normalized_path = normalize_path(path);
394        let mounts = self.mounts.lock();
395
396        // 查找最长匹配的挂载点
397        let mut best_match = None;
398        let mut best_len = 0;
399
400        for (mount_path, stack) in mounts.iter() {
401            if normalized_path.starts_with(mount_path) && mount_path.len() > best_len {
402                // 返回栈顶的挂载点(当前可见的)
403                if let Some(mp) = stack.last() {
404                    best_match = Some(mp.clone());
405                    best_len = mount_path.len();
406                }
407            }
408        }
409
410        best_match
411    }
412
413    /// 获取根挂载点
414    pub fn root_mount(&self) -> Option<Arc<MountPoint>> {
415        self.mounts
416            .lock()
417            .get("/")
418            .and_then(|stack| stack.last())
419            .cloned()
420    }
421
422    /// 列出所有挂载点(用于调试)
423    pub fn list_mounts(&self) -> Vec<(String, String)> {
424        let mounts = self.mounts.lock();
425        mounts
426            .iter()
427            .flat_map(|(path, stack)| {
428                stack
429                    .iter()
430                    .map(|mp| (path.clone(), String::from(mp.fs.fs_type())))
431            })
432            .collect()
433    }
434
435    pub fn list_all(&self) -> BTreeMap<String, Arc<MountPoint>> {
436        let mounts = self.mounts.lock();
437        mounts
438            .iter() // 获取引用,不消耗原 Map
439            .filter_map(|(key, stack)| {
440                // 1. stack.last(): 获取栈顶元素的引用(如果不为空)
441                // 2. map(...): 如果栈顶存在,执行闭包
442                stack.last().map(|mount_point| {
443                    (
444                        key.clone(),         // 必须克隆 String,因为新 Map 需要拥有 Key 的所有权
445                        mount_point.clone(), // 克隆 Arc,这非常廉价(只增加引用计数),不涉及深拷贝
446                    )
447                })
448            })
449            .collect()
450    }
451}
452
453// 全局挂载表
454lazy_static::lazy_static! {
455    pub static ref MOUNT_TABLE: MountTable = MountTable::new();
456}
457
458/// 获取根 dentry
459pub fn get_root_dentry() -> Result<Arc<Dentry>, FsError> {
460    MOUNT_TABLE
461        .root_mount()
462        .map(|mp| mp.root.clone())
463        .ok_or(FsError::NotSupported)
464}