arceos_posix_api/imp/
path_link.rs1use 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#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
13pub struct FilePath(String);
14
15impl FilePath {
16 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 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 pub fn as_str(&self) -> &str {
38 &self.0
39 }
40
41 pub fn parent(&self) -> AxResult<&str> {
43 if self.is_root() {
44 return Ok("/");
45 }
46
47 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 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 pub fn is_root(&self) -> bool {
79 self.0 == "/"
80 }
81
82 pub fn is_dir(&self) -> bool {
84 self.0.ends_with('/')
85 }
86
87 pub fn is_file(&self) -> bool {
89 !self.is_dir()
90 }
91
92 pub fn exists(&self) -> bool {
94 axfs::api::absolute_path_exists(&self.0)
95 }
96
97 pub fn starts_with(&self, prefix: &FilePath) -> bool {
99 self.0.starts_with(&prefix.0)
100 }
101
102 pub fn ends_with(&self, suffix: &FilePath) -> bool {
104 self.0.ends_with(&suffix.0)
105 }
106
107 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 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#[derive(Debug)]
151pub enum LinkError {
152 LinkExists, InvalidPath, NotFound, NotFile, }
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
169pub static HARDLINK_MANAGER: HardlinkManager = HardlinkManager::new();
171
172pub struct HardlinkManager {
174 inner: RwLock<LinkManagerInner>,
175}
176struct LinkManagerInner {
177 links: BTreeMap<String, String>,
178 ref_counts: BTreeMap<String, usize>,
179}
180
181impl 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 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 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 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 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 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
284pub const AT_FDCWD: isize = -100;
286
287pub fn handle_file_path(
297 dir_fd: isize,
298 path_addr: Option<*const u8>,
299 force_dir: bool,
300) -> AxResult<FilePath> {
301 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 let mut path = if path.is_empty() {
317 handle_empty_path(dir_fd)?
318 } else {
319 path
320 };
321
322 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 path = adjust_path_suffix(path, force_dir);
333
334 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 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
370fn 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 path.push('/');
378 }
379 path
380}