1use alloc::{string::String, sync::Arc, vec::Vec};
6use axerrno::{AxError, AxResult, ax_err};
7use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult};
8use axns::{ResArc, def_resource};
9use axsync::Mutex;
10use lazyinit::LazyInit;
11use spin::RwLock;
12
13use crate::{
14 api::FileType,
15 fs::{self},
16 mounts,
17};
18
19def_resource! {
20 pub static CURRENT_DIR_PATH: ResArc<Mutex<String>> = ResArc::new();
21 pub static CURRENT_DIR: ResArc<Mutex<VfsNodeRef>> = ResArc::new();
22}
23
24impl CURRENT_DIR_PATH {
25 pub fn copy_inner(&self) -> Mutex<String> {
27 Mutex::new(self.lock().clone())
28 }
29}
30
31impl CURRENT_DIR {
32 pub fn copy_inner(&self) -> Mutex<VfsNodeRef> {
34 Mutex::new(self.lock().clone())
35 }
36}
37
38struct MountPoint {
39 path: &'static str,
40 fs: Arc<dyn VfsOps>,
41}
42
43struct RootDirectory {
44 main_fs: Arc<dyn VfsOps>,
45 mounts: RwLock<Vec<MountPoint>>,
46}
47
48static ROOT_DIR: LazyInit<Arc<RootDirectory>> = LazyInit::new();
49
50impl MountPoint {
51 pub fn new(path: &'static str, fs: Arc<dyn VfsOps>) -> Self {
52 Self { path, fs }
53 }
54}
55
56impl Drop for MountPoint {
57 fn drop(&mut self) {
58 self.fs.umount().ok();
59 }
60}
61
62impl RootDirectory {
63 pub const fn new(main_fs: Arc<dyn VfsOps>) -> Self {
64 Self {
65 main_fs,
66 mounts: RwLock::new(Vec::new()),
67 }
68 }
69
70 pub fn mount(&self, path: &'static str, fs: Arc<dyn VfsOps>) -> AxResult {
71 if path == "/" {
72 return ax_err!(InvalidInput, "cannot mount root filesystem");
73 }
74 if !path.starts_with('/') {
75 return ax_err!(InvalidInput, "mount path must start with '/'");
76 }
77 if self.mounts.read().iter().any(|mp| mp.path == path) {
78 return ax_err!(InvalidInput, "mount point already exists");
79 }
80 self.main_fs.root_dir().create(path, FileType::Dir)?;
82 fs.mount(path, self.main_fs.root_dir().lookup(path)?)?;
83 self.mounts.write().push(MountPoint::new(path, fs));
84 Ok(())
85 }
86
87 pub fn _umount(&self, path: &str) {
88 self.mounts.write().retain(|mp| mp.path != path);
89 }
90
91 pub fn contains(&self, path: &str) -> bool {
92 self.mounts.read().iter().any(|mp| mp.path == path)
93 }
94
95 fn lookup_mounted_fs<F, T>(&self, path: &str, f: F) -> AxResult<T>
96 where
97 F: FnOnce(Arc<dyn VfsOps>, &str) -> AxResult<T>,
98 {
99 debug!("lookup at root: {}", path);
100 let path = path.trim_matches('/');
101 if let Some(rest) = path.strip_prefix("./") {
102 return self.lookup_mounted_fs(rest, f);
103 }
104
105 let mut idx = 0;
106 let mut max_len = 0;
107
108 for (i, mp) in self.mounts.read().iter().enumerate() {
111 if path.starts_with(&mp.path[1..]) && mp.path.len() - 1 > max_len {
113 max_len = mp.path.len() - 1;
114 idx = i;
115 }
116 }
117
118 if max_len == 0 {
119 f(self.main_fs.clone(), path) } else {
121 f(self.mounts.read()[idx].fs.clone(), &path[max_len..]) }
123 }
124}
125
126impl VfsNodeOps for RootDirectory {
127 axfs_vfs::impl_vfs_dir_default! {}
128
129 fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
130 self.main_fs.root_dir().get_attr()
131 }
132
133 fn lookup(self: Arc<Self>, path: &str) -> VfsResult<VfsNodeRef> {
134 self.lookup_mounted_fs(path, |fs, rest_path| fs.root_dir().lookup(rest_path))
135 }
136
137 fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult {
138 self.lookup_mounted_fs(path, |fs, rest_path| {
139 if rest_path.is_empty() {
140 Ok(()) } else {
142 fs.root_dir().create(rest_path, ty)
143 }
144 })
145 }
146
147 fn remove(&self, path: &str) -> VfsResult {
148 self.lookup_mounted_fs(path, |fs, rest_path| {
149 if rest_path.is_empty() {
150 ax_err!(PermissionDenied) } else {
152 fs.root_dir().remove(rest_path)
153 }
154 })
155 }
156
157 fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult {
158 self.lookup_mounted_fs(src_path, |fs, rest_path| {
159 if rest_path.is_empty() {
160 ax_err!(PermissionDenied) } else {
162 fs.root_dir().rename(rest_path, dst_path)
163 }
164 })
165 }
166}
167
168pub(crate) fn init_rootfs(disk: crate::dev::Disk) {
169 cfg_if::cfg_if! {
170 if #[cfg(feature = "myfs")] { let main_fs = fs::myfs::new_myfs(disk);
172 } else if #[cfg(feature = "lwext4_rs")] {
173 static EXT4_FS: LazyInit<Arc<fs::lwext4_rust::Ext4FileSystem>> = LazyInit::new();
174 EXT4_FS.init_once(Arc::new(fs::lwext4_rust::Ext4FileSystem::new(disk)));
175 let main_fs = EXT4_FS.clone();
176 } else if #[cfg(feature = "fatfs")] {
177 static FAT_FS: LazyInit<Arc<fs::fatfs::FatFileSystem>> = LazyInit::new();
178 FAT_FS.init_once(Arc::new(fs::fatfs::FatFileSystem::new(disk)));
179 FAT_FS.init();
180 let main_fs = FAT_FS.clone();
181 }
182 }
183
184 let root_dir = RootDirectory::new(main_fs);
185
186 #[cfg(feature = "devfs")]
187 root_dir
188 .mount("/dev", mounts::devfs())
189 .expect("failed to mount devfs at /dev");
190
191 #[cfg(feature = "ramfs")]
192 root_dir
193 .mount("/tmp", mounts::ramfs())
194 .expect("failed to mount ramfs at /tmp");
195
196 #[cfg(feature = "procfs")]
198 root_dir .mount("/proc", mounts::procfs().unwrap())
200 .expect("fail to mount procfs at /proc");
201
202 #[cfg(feature = "sysfs")]
204 root_dir .mount("/sys", mounts::sysfs().unwrap())
206 .expect("fail to mount sysfs at /sys");
207
208 ROOT_DIR.init_once(Arc::new(root_dir));
209 info!("rootfs initialized");
210 CURRENT_DIR.init_new(Mutex::new(ROOT_DIR.clone()));
211 info!("test");
212 CURRENT_DIR_PATH.init_new(Mutex::new("/".into()));
213}
214
215fn parent_node_of(dir: Option<&VfsNodeRef>, path: &str) -> VfsNodeRef {
216 if path.starts_with('/') {
217 ROOT_DIR.clone()
218 } else {
219 dir.cloned().unwrap_or_else(|| CURRENT_DIR.lock().clone())
220 }
221}
222
223pub(crate) fn absolute_path(path: &str) -> AxResult<String> {
224 if path.starts_with('/') {
225 Ok(axfs_vfs::path::canonicalize(path))
226 } else {
227 let path = CURRENT_DIR_PATH.lock().clone() + path;
228 Ok(axfs_vfs::path::canonicalize(&path))
229 }
230}
231
232pub(crate) fn lookup(dir: Option<&VfsNodeRef>, path: &str) -> AxResult<VfsNodeRef> {
233 if path.is_empty() {
234 return ax_err!(NotFound);
235 }
236 let node = parent_node_of(dir, path).lookup(path)?;
237 if path.ends_with('/') && !node.get_attr()?.is_dir() {
238 ax_err!(NotADirectory)
239 } else {
240 Ok(node)
241 }
242}
243
244pub(crate) fn create_file(dir: Option<&VfsNodeRef>, path: &str) -> AxResult<VfsNodeRef> {
245 if path.is_empty() {
246 return ax_err!(NotFound);
247 } else if path.ends_with('/') {
248 return ax_err!(NotADirectory);
249 }
250 let parent = parent_node_of(dir, path);
251 parent.create(path, VfsNodeType::File)?;
252 parent.lookup(path)
253}
254
255pub(crate) fn create_dir(dir: Option<&VfsNodeRef>, path: &str) -> AxResult {
256 match lookup(dir, path) {
257 Ok(_) => ax_err!(AlreadyExists),
258 Err(AxError::NotFound) => parent_node_of(dir, path).create(path, VfsNodeType::Dir),
259 Err(e) => Err(e),
260 }
261}
262
263pub(crate) fn remove_file(dir: Option<&VfsNodeRef>, path: &str) -> AxResult {
264 let node = lookup(dir, path)?;
265 let attr = node.get_attr()?;
266 if attr.is_dir() {
267 ax_err!(IsADirectory)
268 } else if !attr.perm().owner_writable() {
269 ax_err!(PermissionDenied)
270 } else {
271 parent_node_of(dir, path).remove(path)
272 }
273}
274
275pub(crate) fn remove_dir(dir: Option<&VfsNodeRef>, path: &str) -> AxResult {
276 if path.is_empty() {
277 return ax_err!(NotFound);
278 }
279 let path_check = path.trim_matches('/');
280 if path_check.is_empty() {
281 return ax_err!(DirectoryNotEmpty); } else if path_check == "."
283 || path_check == ".."
284 || path_check.ends_with("/.")
285 || path_check.ends_with("/..")
286 {
287 return ax_err!(InvalidInput);
288 }
289 if ROOT_DIR.contains(&absolute_path(path)?) {
290 return ax_err!(PermissionDenied);
291 }
292
293 let node = lookup(dir, path)?;
294 let attr = node.get_attr()?;
295 if !attr.is_dir() {
296 ax_err!(NotADirectory)
297 } else if !attr.perm().owner_writable() {
298 ax_err!(PermissionDenied)
299 } else {
300 parent_node_of(dir, path).remove(path)
301 }
302}
303
304pub(crate) fn current_dir() -> AxResult<String> {
305 Ok(CURRENT_DIR_PATH.lock().clone())
306}
307
308pub(crate) fn set_current_dir(path: &str) -> AxResult {
309 let mut abs_path = absolute_path(path)?;
310 if !abs_path.ends_with('/') {
311 abs_path += "/";
312 }
313 if abs_path == "/" {
314 *CURRENT_DIR.lock() = ROOT_DIR.clone();
315 *CURRENT_DIR_PATH.lock() = "/".into();
316 return Ok(());
317 }
318
319 let node = lookup(None, &abs_path)?;
320 let attr = node.get_attr()?;
321 if !attr.is_dir() {
322 ax_err!(NotADirectory)
323 } else if !attr.perm().owner_executable() {
324 ax_err!(PermissionDenied)
325 } else {
326 *CURRENT_DIR.lock() = node;
327 *CURRENT_DIR_PATH.lock() = abs_path;
328 Ok(())
329 }
330}
331
332pub(crate) fn rename(old: &str, new: &str) -> AxResult {
333 if parent_node_of(None, new).lookup(new).is_ok() {
334 warn!("dst file already exist, now remove it");
335 remove_file(None, new)?;
336 }
337 parent_node_of(None, old).rename(old, new)
338}