arceos_posix_api/imp/
path_link.rs

1use alloc::collections::BTreeMap;
2use alloc::format;
3use core::fmt;
4use core::ops::Deref;
5use spin::RwLock;
6
7use alloc::string::{String, ToString};
8use axerrno::{AxError, AxResult};
9use axfs::api::{canonicalize, current_dir};
10
11/// 一个规范化的文件路径表示
12#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
13pub struct FilePath(String);
14
15impl FilePath {
16    /// 从路径字符串创建一个新的 `FilePath`,路径将被规范化。
17    /// 输入路径可以是绝对路径或相对路径。
18    pub fn new<P: AsRef<str>>(path: P) -> AxResult<Self> {
19        let path = path.as_ref();
20        let canonical = canonicalize(path).map_err(|_| AxError::NotFound)?;
21        let mut new_path = canonical.trim().to_string();
22
23        // 如果原始路径以 '/' 结尾,那么规范化后的路径也应以 '/' 结尾
24        if path.ends_with('/') && !new_path.ends_with('/') {
25            new_path.push('/');
26        }
27
28        assert!(
29            new_path.starts_with('/'),
30            "canonical path should start with /"
31        );
32
33        Ok(Self(HARDLINK_MANAGER.real_path(&new_path)))
34    }
35
36    /// 返回底层路径的字符串切片
37    pub fn as_str(&self) -> &str {
38        &self.0
39    }
40
41    /// 返回父目录路径
42    pub fn parent(&self) -> AxResult<&str> {
43        if self.is_root() {
44            return Ok("/");
45        }
46
47        // 查找最后一个斜杠,考虑可能的尾部斜杠
48        let mut path = self.as_str();
49        if path.ends_with('/') {
50            path = path.strip_suffix('/').unwrap();
51        }
52        let pos = path.rfind('/').ok_or(AxError::NotFound)?;
53
54        Ok(&path[..=pos])
55    }
56
57    /// 返回文件名或目录名组件
58    pub fn name(&self) -> AxResult<&str> {
59        if self.is_root() {
60            return Ok("/");
61        }
62
63        let mut path = self.as_str();
64        if path.ends_with('/') {
65            path = path.strip_suffix('/').unwrap();
66        }
67        let start_pos = path.rfind('/').ok_or(AxError::NotFound)?;
68
69        let end_pos = if path.ends_with('/') {
70            path.len() - 1
71        } else {
72            path.len()
73        };
74        Ok(&path[start_pos + 1..end_pos])
75    }
76
77    /// 判断是否为根目录
78    pub fn is_root(&self) -> bool {
79        self.0 == "/"
80    }
81
82    /// 判断是否为目录(以 '/' 结尾)
83    pub fn is_dir(&self) -> bool {
84        self.0.ends_with('/')
85    }
86
87    /// 判断是否为常规文件(不以 '/' 结尾)
88    pub fn is_file(&self) -> bool {
89        !self.is_dir()
90    }
91
92    /// Whether the path exists
93    pub fn exists(&self) -> bool {
94        axfs::api::absolute_path_exists(&self.0)
95    }
96
97    /// 判断此路径是否以给定前缀路径开头
98    pub fn starts_with(&self, prefix: &FilePath) -> bool {
99        self.0.starts_with(&prefix.0)
100    }
101
102    /// 判断此路径是否以给定后缀路径结尾
103    pub fn ends_with(&self, suffix: &FilePath) -> bool {
104        self.0.ends_with(&suffix.0)
105    }
106
107    /// 将此路径与相对路径组件连接
108    pub fn join<P: AsRef<str>>(&self, path: P) -> AxResult<Self> {
109        let mut new_path = self.0.clone();
110        if !new_path.ends_with('/') {
111            new_path.push('/');
112        }
113        new_path.push_str(path.as_ref());
114        FilePath::new(new_path)
115    }
116
117    /// 返回此路径组件的迭代器
118    pub fn components(&self) -> impl Iterator<Item = &str> {
119        self.0.trim_matches('/').split('/')
120    }
121}
122
123impl fmt::Display for FilePath {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        f.write_str(&self.0)
126    }
127}
128
129impl AsRef<str> for FilePath {
130    fn as_ref(&self) -> &str {
131        &self.0
132    }
133}
134
135impl From<&str> for FilePath {
136    fn from(s: &str) -> Self {
137        FilePath::new(s).unwrap()
138    }
139}
140
141impl Deref for FilePath {
142    type Target = str;
143
144    fn deref(&self) -> &Self::Target {
145        &self.0
146    }
147}
148
149/// 错误类型
150#[derive(Debug)]
151pub enum LinkError {
152    LinkExists,  // 链接已存在
153    InvalidPath, // 无效路径
154    NotFound,    // 文件不存在
155    NotFile,     // 不是文件
156}
157
158impl From<LinkError> for AxError {
159    fn from(err: LinkError) -> AxError {
160        match err {
161            LinkError::LinkExists => AxError::AlreadyExists,
162            LinkError::InvalidPath => AxError::InvalidInput,
163            LinkError::NotFound => AxError::NotFound,
164            LinkError::NotFile => AxError::InvalidInput,
165        }
166    }
167}
168
169/// A global hardlink manager
170pub static HARDLINK_MANAGER: HardlinkManager = HardlinkManager::new();
171
172/// A manager for hardlinks
173pub struct HardlinkManager {
174    inner: RwLock<LinkManagerInner>,
175}
176struct LinkManagerInner {
177    links: BTreeMap<String, String>,
178    ref_counts: BTreeMap<String, usize>,
179}
180
181// 关于innner的操作都在atomic_开头的函数中
182impl HardlinkManager {
183    pub const fn new() -> Self {
184        Self {
185            inner: RwLock::new(LinkManagerInner {
186                links: BTreeMap::new(),
187                ref_counts: BTreeMap::new(),
188            }),
189        }
190    }
191
192    /// 创建链接
193    /// 如果目标路径不存在,则返回 `LinkError::NotFound`
194    /// 如果目标路径不是文件,则返回 `LinkError::NotFile`
195    pub fn create_link(&self, src: &FilePath, dst: &FilePath) -> Result<(), LinkError> {
196        if !dst.exists() {
197            return Err(LinkError::NotFound);
198        }
199        if !dst.is_dir() {
200            return Err(LinkError::NotFile);
201        }
202
203        let mut inner = self.inner.write();
204        self.atomic_link_update(&mut inner, src, dst);
205        Ok(())
206    }
207
208    /// 移除链接
209    /// 链接数量为零 或 没有链接时, 删除文件
210    /// 如果路径对应的链接不存在 或 路径对应的文件不存在,则返回 `None`
211    /// 否则返回链接的目标路径
212    pub fn remove_link(&self, src: &FilePath) -> Option<String> {
213        let mut inner = self.inner.write();
214        self.atomic_link_remove(&mut inner, src).or_else(|| {
215            axfs::api::remove_file(src.as_str())
216                .ok()
217                .map(|_| src.to_string())
218        })
219    }
220
221    pub fn real_path(&self, path: &str) -> String {
222        self.inner
223            .read()
224            .links
225            .get(path)
226            .cloned()
227            .unwrap_or_else(|| path.to_string())
228    }
229
230    pub fn link_count(&self, path: &FilePath) -> usize {
231        let inner = self.inner.read();
232        inner
233            .ref_counts
234            .get(path.as_str())
235            .copied()
236            .unwrap_or_else(|| if path.exists() { 1 } else { 0 })
237    }
238
239    // 原子操作helpers
240
241    /// 创建或更新链接
242    /// 如果链接已存在,则更新目标路径
243    /// 如果目标路径不存在,则返回 `LinkError::NotFound`
244    fn atomic_link_update(&self, inner: &mut LinkManagerInner, src: &FilePath, dst: &FilePath) {
245        if let Some(old_dst) = inner.links.get(src.as_str()) {
246            if old_dst == dst.as_str() {
247                return;
248            }
249            self.decrease_ref_count(inner, &old_dst.to_string());
250        }
251        inner.links.insert(src.to_string(), dst.to_string());
252        *inner.ref_counts.entry(dst.to_string()).or_insert(0) += 1;
253    }
254
255    /// 移除链接
256    /// 如果链接不存在,则返回 `None`,否则返回链接的目标路径
257    fn atomic_link_remove(&self, inner: &mut LinkManagerInner, src: &FilePath) -> Option<String> {
258        inner.links.remove(src.as_str()).inspect(|dst| {
259            self.decrease_ref_count(inner, dst);
260        })
261    }
262
263    /// 减少引用计数
264    /// 如果引用计数为零,则删除链接,并删除文件,如果删除文件失败,则返回 `None`
265    /// 如果链接不存在,则返回 `None`
266    fn decrease_ref_count(&self, inner: &mut LinkManagerInner, path: &str) -> Option<()> {
267        match inner.ref_counts.get_mut(path) {
268            Some(count) => {
269                *count -= 1;
270                if *count == 0 {
271                    inner.ref_counts.remove(path);
272                    axfs::api::remove_file(path).ok()?
273                }
274                Some(())
275            }
276            None => {
277                axlog::error!("link exists but ref count is zero");
278                None
279            }
280        }
281    }
282}
283
284/// A constant representing the current working directory
285pub const AT_FDCWD: isize = -100;
286
287/// 处理路径并返回规范化后的 `FilePath`
288///
289/// * `dir_fd` - 目录的文件描述符,如果是 `AT_FDCWD`,则操作当前工作目录
290///
291/// * `path_addr` - 路径的地址,如果为 `None`,则操作由 `dir_fd` 指定的文件
292///
293/// * `force_dir` - 如果为 `true`,则将路径视为目录
294///
295/// 该函数会处理链接并规范化路径
296pub fn handle_file_path(
297    dir_fd: isize,
298    path_addr: Option<*const u8>,
299    force_dir: bool,
300) -> AxResult<FilePath> {
301    // 获取路径字符串
302    let path = match path_addr {
303        Some(addr) => {
304            if addr.is_null() {
305                axlog::warn!("路径地址为空");
306                return Err(AxError::BadAddress);
307            }
308            crate::utils::char_ptr_to_str(addr as *const _)
309                .map_err(|_| AxError::NotFound)?
310                .to_string()
311        }
312        None => String::new(),
313    };
314
315    // 处理空路径的情况
316    let mut path = if path.is_empty() {
317        handle_empty_path(dir_fd)?
318    } else {
319        path
320    };
321
322    // 处理相对路径的情况
323    if !path.starts_with('/') {
324        if dir_fd == AT_FDCWD {
325            path = prepend_cwd(&path)?;
326        } else {
327            path = handle_relative_path(dir_fd, &path)?;
328        }
329    }
330
331    // 根据 `force_dir` 和路径结尾调整路径
332    path = adjust_path_suffix(path, force_dir);
333
334    // 创建并返回 `FilePath`
335    FilePath::new(&path)
336}
337
338fn handle_empty_path(dir_fd: isize) -> AxResult<String> {
339    const AT_FDCWD: isize = -100;
340    if dir_fd == AT_FDCWD {
341        return Ok(String::from("."));
342    }
343
344    super::fs::Directory::from_fd(dir_fd as i32)
345        .map(|dir| dir.path().to_string())
346        .map_err(|_| AxError::NotFound)
347}
348
349fn handle_relative_path(dir_fd: isize, path: &str) -> AxResult<String> {
350    match super::fs::Directory::from_fd(dir_fd as i32) {
351        Ok(dir) => {
352            // 假设目录路径以 '/' 结尾,无需手动添加
353            let combined_path = format!("{}{}", dir.path(), path);
354            axlog::info!("处理后的路径: {} (目录: {})", combined_path, dir.path());
355            Ok(combined_path)
356        }
357        Err(_) => {
358            axlog::warn!("文件描述符不存在");
359            Err(AxError::NotFound)
360        }
361    }
362}
363
364fn prepend_cwd(path: &str) -> AxResult<String> {
365    let cwd = current_dir().map_err(|_| AxError::NotFound)?;
366    debug_assert!(cwd.ends_with('/'), "当前工作目录路径应以 '/' 结尾");
367    Ok(format!("{cwd}{path}"))
368}
369
370/// 根据 `force_dir` 和路径结尾调整路径
371fn adjust_path_suffix(mut path: String, force_dir: bool) -> String {
372    if force_dir && !path.ends_with('/') {
373        path.push('/');
374    }
375    if path.ends_with('.') {
376        // 防止路径以 '.' 结尾
377        path.push('/');
378    }
379    path
380}