os/vfs/
path.rs

1//! 路径解析引擎
2//!
3//! 该模块实现了 VFS 的路径解析功能,负责将路径字符串转换为 Dentry,支持绝对路径、
4//! 相对路径和符号链接解析。
5//!
6//! # 核心组件
7//!
8//! - [`PathComponent`] - 路径组件枚举(Root、Parent、Current、Normal)
9//! - [`parse_path`] - 路径字符串解析器
10//! - [`normalize_path`] - 路径规范化函数
11//! - [`split_path`] - 路径分割函数
12//! - [`vfs_lookup`] - 主路径查找函数
13//! - [`vfs_lookup_no_follow`] - 不跟随符号链接的查找
14//!
15//! # 路径解析流程
16//!
17//! 完整的路径查找需要经过多个步骤:
18//!
19//! ```text
20//! 用户输入: "/home/../etc/./passwd"
21//!     ↓
22//! parse_path() → [Root, Normal("home"), Parent, Normal("etc"), Current, Normal("passwd")]
23//!     ↓
24//! normalize_path() → "/etc/passwd"
25//!     ↓
26//! 检查缓存 (DENTRY_CACHE)
27//!     ├─ 命中 → 返回缓存的 Dentry
28//!     └─ 未命中 ↓
29//! vfs_lookup() - 逐级查找
30//!     ├─ "/" → 根 Dentry
31//!     ├─ "etc" → 查找子项,检查挂载点
32//!     └─ "passwd" → 最终 Dentry
33//! ```
34//!
35//! # 路径组件
36//!
37//! ## PathComponent 枚举
38//!
39//! ```rust
40//! pub enum PathComponent {
41//!     Root,           // "/"
42//!     Current,        // "."
43//!     Parent,         // ".."
44//!     Normal(String), // 普通文件名
45//! }
46//! ```
47//!
48//! ### 解析示例
49//!
50//! ```text
51//! "/home/user/file.txt"  → [Root, Normal("home"), Normal("user"), Normal("file.txt")]
52//! "../file.txt"          → [Parent, Normal("file.txt")]
53//! "./file.txt"           → [Current, Normal("file.txt")]
54//! "//foo///bar//"        → [Root, Normal("foo"), Normal("bar")]
55//! ```
56//!
57//! # 路径规范化
58//!
59//! `normalize_path()` 处理 `.` 和 `..`,移除冗余斜杠:
60//!
61//! ```rust
62//! normalize_path("/home/../etc/./passwd")  // → "/etc/passwd"
63//! normalize_path("./file.txt")             // → "file.txt"
64//! normalize_path("///foo//bar//")          // → "/foo/bar"
65//! normalize_path("/../../../etc")          // → "/etc" (不能越过根目录)
66//! ```
67//!
68//! # 符号链接处理
69//!
70//! ## 跟随符号链接
71//!
72//! `vfs_lookup()` 默认跟随符号链接,最多 8 层:
73//!
74//! ```text
75//! /link1 → /link2 → /link3 → /real_file
76//!   ↓        ↓        ↓         ↓
77//! 解析    解析     解析      返回
78//! ```
79//!
80//! ## 不跟随符号链接
81//!
82//! `vfs_lookup_no_follow()` 返回符号链接本身:
83//!
84//! ```rust
85//! // 对于符号链接 /link → /target
86//! vfs_lookup("/link")?;           // 返回 /target 的 Dentry
87//! vfs_lookup_no_follow("/link")?; // 返回 /link 的 Dentry
88//! ```
89//!
90//! # 挂载点处理
91//!
92//! 查找过程中自动处理挂载点:
93//!
94//! ```text
95//! /mnt 挂载了 tmpfs
96//!
97//! 查找 "/mnt/file":
98//!   1. 找到 /mnt 的 Dentry
99//!   2. 检测到挂载点,切换到 tmpfs 的根 Dentry
100//!   3. 在 tmpfs 中查找 "file"
101//! ```
102//!
103//! # 相对路径处理
104//!
105//! 相对路径基于当前工作目录(cwd):
106//!
107//! ```rust
108//! // 当前工作目录: /home/user
109//! vfs_lookup("file.txt")?;      // → /home/user/file.txt
110//! vfs_lookup("../other")?;      // → /home/other
111//! ```
112//!
113//! # 使用示例
114//!
115//! ## 基本路径查找
116//!
117//! ```rust
118//! use vfs::vfs_lookup;
119//!
120//! // 查找绝对路径
121//! let dentry = vfs_lookup("/etc/passwd")?;
122//!
123//! // 查找相对路径(基于当前工作目录)
124//! let dentry = vfs_lookup("file.txt")?;
125//! ```
126//!
127//! ## 路径解析和规范化
128//!
129//! ```rust
130//! use vfs::{parse_path, normalize_path, PathComponent};
131//!
132//! // 解析路径
133//! let components = parse_path("/home/../etc");
134//! // → [Root, Normal("home"), Parent, Normal("etc")]
135//!
136//! // 规范化路径
137//! let normalized = normalize_path("/home/../etc");
138//! // → "/etc"
139//! ```
140//!
141//! ## 分割路径
142//!
143//! ```rust
144//! use vfs::split_path;
145//!
146//! // 分割为目录和文件名
147//! let (dir, name) = split_path("/etc/passwd")?;
148//! // dir = "/etc", name = "passwd"
149//!
150//! let (dir, name) = split_path("/file")?;
151//! // dir = "/", name = "file"
152//! ```
153//!
154//! ## 符号链接操作
155//!
156//! ```rust
157//! use vfs::{vfs_lookup, vfs_lookup_no_follow};
158//!
159//! // 创建符号链接 /link → /target
160//! parent.inode.symlink("link", "/target")?;
161//!
162//! // 跟随符号链接
163//! let target = vfs_lookup("/link")?;  // 返回 /target
164//!
165//! // 不跟随符号链接
166//! let link = vfs_lookup_no_follow("/link")?;  // 返回 /link
167//! let link_target = link.inode.readlink()?;   // 读取链接目标
168//! ```
169
170use crate::kernel::current_task;
171use crate::vfs::{Dentry, FsError, get_root_dentry};
172use alloc::string::String;
173use alloc::sync::Arc;
174use alloc::vec::Vec;
175
176#[derive(Debug, Clone, PartialEq, Eq)]
177pub enum PathComponent {
178    Root,           // "/"
179    Current,        // "."
180    Parent,         // ".."
181    Normal(String), // 正常的文件名
182}
183
184/// 将路径字符串解析为组件列表
185///
186/// 参数:
187///     - path: 待解析的路径字符串(支持绝对路径和相对路径)
188///
189/// 返回:路径组件向量,包含 Root、Current、Parent 或 Normal 组件
190pub fn parse_path(path: &str) -> Vec<PathComponent> {
191    let mut components = Vec::new();
192
193    // 绝对路径以 Root 开始
194    if path.starts_with('/') {
195        components.push(PathComponent::Root);
196    }
197
198    // 分割路径并解析每个部分
199    for part in path.split('/').filter(|s| !s.is_empty()) {
200        let component = match part {
201            "." => PathComponent::Current,
202            ".." => PathComponent::Parent,
203            name => PathComponent::Normal(String::from(name)),
204        };
205        components.push(component);
206    }
207
208    components
209}
210
211/// 规范化路径(处理 ".." 和 ".")
212///
213/// 参数:
214///     - path: 待规范化的路径字符串
215///
216/// 返回:规范化后的路径字符串,移除冗余的 `.` 和 `..` 组件
217pub fn normalize_path(path: &str) -> String {
218    let components = parse_path(path);
219    let mut stack: Vec<String> = Vec::new();
220    let mut is_absolute = false;
221
222    for component in components {
223        match component {
224            PathComponent::Root => {
225                is_absolute = true;
226            }
227            PathComponent::Current => {
228                // "." 不做任何操作
229            }
230            PathComponent::Parent => {
231                if is_absolute {
232                    // 绝对路径:不能越过根目录
233                    if !stack.is_empty() {
234                        stack.pop();
235                    }
236                } else {
237                    // 相对路径:
238                    if let Some(last) = stack.last() {
239                        if last == ".." {
240                            // 栈顶是 ".." (例如 "/../.."),继续添加 ".."
241                            stack.push(String::from(".."));
242                        } else {
243                            // 栈顶是普通目录 (例如 "a/b/"),弹出一个 (变为 "a/")
244                            stack.pop();
245                        }
246                    } else {
247                        // 栈是空的 (即 "/"),添加 ".."
248                        stack.push(String::from(".."));
249                    }
250                }
251            }
252            PathComponent::Normal(name) => {
253                stack.push(name);
254            }
255        }
256    }
257
258    // 构造结果
259    if stack.is_empty() {
260        if is_absolute {
261            String::from("/")
262        } else {
263            String::from(".")
264        }
265    } else if is_absolute {
266        String::from("/") + &stack.join("/")
267    } else {
268        stack.join("/")
269    }
270}
271
272/// 将路径分割为目录部分和文件名部分
273///
274/// 参数:
275///     - path: 待分割的路径字符串
276///
277/// 返回:Ok((目录, 文件名)) 分割成功;Err(FsError::InvalidArgument) 路径以斜杠结尾或文件名为空
278pub fn split_path(path: &str) -> Result<(String, String), FsError> {
279    // 如果路径以斜杠结尾,说明是目录而非文件,返回错误
280    if path.ends_with('/') && path.len() > 1 {
281        return Err(FsError::InvalidArgument);
282    }
283
284    // 先规范化路径,处理多余的斜杠和 . / ..
285    let normalized = normalize_path(path);
286
287    if let Some(pos) = normalized.rfind('/') {
288        let dir = if pos == 0 {
289            String::from("/")
290        } else {
291            String::from(&normalized[..pos])
292        };
293        let filename = String::from(&normalized[pos + 1..]);
294
295        if filename.is_empty() {
296            return Err(FsError::InvalidArgument);
297        }
298
299        Ok((dir, filename))
300    } else {
301        // 相对路径,使用当前目录
302        Ok((String::from("."), String::from(normalized)))
303    }
304}
305
306/// 将路径字符串解析为 Dentry(支持绝对/相对路径、符号链接解析)
307///
308/// 参数:
309///     - path: 文件或目录路径(绝对路径从根目录开始,相对路径从当前工作目录开始)
310///
311/// 返回:`Ok(Arc<Dentry>)` 路径对应的目录项;`Err(FsError::NotFound)` 路径不存在;`Err(FsError::NotDirectory)` 中间组件不是目录
312pub fn vfs_lookup(path: &str) -> Result<Arc<Dentry>, FsError> {
313    let components = parse_path(path);
314
315    // 确定起始 dentry
316    let mut current_dentry = if components.first() == Some(&PathComponent::Root) {
317        // 绝对路径:从根目录开始
318        get_root_dentry()?
319    } else {
320        // 相对路径:从当前工作目录开始
321        get_cur_dir()?
322    };
323
324    // 逐个解析路径组件
325    for component in components {
326        current_dentry = resolve_component(current_dentry, component)?;
327    }
328
329    Ok(current_dentry)
330}
331
332/// 从指定的 base dentry 开始解析路径。
333///
334/// 如果 `path` 是绝对路径(以'/'开头),此函数会忽略路径中的根组件("/"),
335/// 并从传入的 `base` dentry 开始解析。因此,调用者有责任在处理绝对路径时
336/// 提供根 dentry 作为 `base`。
337///
338/// # 参数
339/// - `base`: 开始查找的目录项。
340/// - `path`: 要解析的路径字符串。
341pub fn vfs_lookup_from(base: Arc<Dentry>, path: &str) -> Result<Arc<Dentry>, FsError> {
342    let components = parse_path(path);
343    let mut current_dentry = base;
344
345    for component in components {
346        if component == PathComponent::Root {
347            continue;
348        }
349        current_dentry = resolve_component(current_dentry, component)?;
350    }
351
352    Ok(current_dentry)
353}
354
355/// 解析单个路径组件,处理 `.`、`..`、普通文件名和符号链接
356fn resolve_component(base: Arc<Dentry>, component: PathComponent) -> Result<Arc<Dentry>, FsError> {
357    match component {
358        PathComponent::Root => {
359            // 已经在根目录,无需操作
360            get_root_dentry()
361        }
362        PathComponent::Current => {
363            // "." 表示当前目录
364            Ok(base)
365        }
366        PathComponent::Parent => {
367            // ".." 表示父目录
368            match base.parent() {
369                Some(parent) => check_mount_point(parent),
370                None => Ok(base), // 根目录的父目录是自己
371            }
372        }
373        PathComponent::Normal(name) => {
374            // 正常文件名:查找子项
375
376            // 1. 先检查 dentry 缓存
377            if let Some(child) = base.lookup_child(&name) {
378                // 即使缓存命中,也要检查挂载点(可能后来挂载了)
379                return check_mount_point(child);
380            }
381
382            // 2. 缓存未命中,通过 inode 查找
383            let child_inode = base.inode.lookup(&name)?;
384
385            // 3. 创建新的 dentry 并加入缓存
386            let child_dentry = Dentry::new(name.clone(), child_inode);
387            base.add_child(child_dentry.clone());
388
389            // 4. 加入全局缓存
390            crate::vfs::DENTRY_CACHE.insert(&child_dentry);
391
392            // 5. 检查是否有挂载点
393            check_mount_point(child_dentry)
394        }
395    }
396}
397
398/// 检查给定的 dentry 是否有挂载点,如果有则返回挂载点的根 dentry
399fn check_mount_point(dentry: Arc<Dentry>) -> Result<Arc<Dentry>, FsError> {
400    // 快速路径:检查 dentry 本地缓存
401    if let Some(mounted_root) = dentry.get_mount() {
402        return Ok(mounted_root);
403    }
404
405    // 慢速路径:查找挂载表(首次访问或缓存失效)
406    let full_path = dentry.full_path();
407    if let Some(mount_point) = crate::vfs::MOUNT_TABLE.find_mount(&full_path) {
408        if mount_point.mount_path == full_path {
409            // 更新 dentry 的挂载缓存
410            dentry.set_mount(&mount_point.root);
411            return Ok(mount_point.root.clone());
412        }
413    }
414
415    Ok(dentry)
416}
417
418/// 获取当前任务的工作目录
419fn get_cur_dir() -> Result<Arc<Dentry>, FsError> {
420    current_task()
421        .lock()
422        .fs
423        .lock()
424        .cwd
425        .clone()
426        .ok_or(FsError::NotSupported)
427}
428
429/// 查找路径但不跟随最后一个符号链接
430///
431/// # 参数
432/// * `path` - 要查找的路径
433///
434/// # 返回值
435/// * `Ok(dentry)` - 找到的 dentry(如果最后一个组件是符号链接,返回链接本身)
436/// * `Err(FsError)` - 查找失败
437///
438/// # 行为
439/// 与 `vfs_lookup` 类似,但最后一个路径组件如果是符号链接,
440/// 不会跟随它,而是直接返回链接文件的 dentry。
441/// 路径中间的符号链接仍然会被跟随。
442pub fn vfs_lookup_no_follow(path: &str) -> Result<Arc<Dentry>, FsError> {
443    let components = parse_path(path);
444
445    if components.is_empty() {
446        return Err(FsError::InvalidArgument);
447    }
448
449    // 确定起始 dentry
450    let mut current_dentry = if components.first() == Some(&PathComponent::Root) {
451        // 绝对路径:从根目录开始
452        get_root_dentry()?
453    } else {
454        // 相对路径:从当前工作目录开始
455        get_cur_dir()?
456    };
457
458    // 如果只有一个 Root 组件,直接返回根目录
459    if components.len() == 1 && components[0] == PathComponent::Root {
460        return Ok(current_dentry);
461    }
462
463    // 解析除最后一个组件外的所有组件(这些符号链接需要跟随)
464    let len = components.len();
465    for i in 0..len - 1 {
466        current_dentry = resolve_component(current_dentry, components[i].clone())?;
467    }
468
469    // 解析最后一个组件,但不跟随符号链接
470    let last_component = &components[len - 1];
471    match last_component {
472        PathComponent::Root => {
473            // 最后一个是根,不应该发生,但处理一下
474            get_root_dentry()
475        }
476        PathComponent::Current => {
477            // "." 表示当前目录
478            Ok(current_dentry)
479        }
480        PathComponent::Parent => {
481            // ".." 表示父目录
482            match current_dentry.parent() {
483                Some(parent) => Ok(parent),
484                None => Ok(current_dentry), // 根目录的父目录是自己
485            }
486        }
487        PathComponent::Normal(name) => {
488            // 正常文件名:查找但不跟随符号链接
489
490            // 1. 先检查 dentry 缓存
491            if let Some(child) = current_dentry.lookup_child(name) {
492                return Ok(child);
493            }
494
495            // 2. 缓存未命中,通过 inode 查找
496            let child_inode = current_dentry.inode.lookup(name)?;
497
498            // 3. 创建新的 dentry 并加入缓存(不跟随符号链接)
499            let child_dentry = Dentry::new(name.clone(), child_inode);
500            current_dentry.add_child(child_dentry.clone());
501
502            // 4. 加入全局缓存
503            crate::vfs::DENTRY_CACHE.insert(&child_dentry);
504
505            Ok(child_dentry)
506        }
507    }
508}