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}