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}