|
1 | 1 | use crate::clock::realtime::date; |
2 | 2 | use crate::{ |
3 | 3 | drivers::{DM, Driver, block::get_block_device_by_descriptor}, |
4 | | - process::Task, |
| 4 | + process::{TASK_LIST, Task}, |
5 | 5 | sync::SpinLock, |
6 | 6 | }; |
7 | 7 | use alloc::{borrow::ToOwned, boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec}; |
@@ -45,6 +45,7 @@ impl Inode for DummyInode { |
45 | 45 | } |
46 | 46 |
|
47 | 47 | /// Represents a mounted filesystem. |
| 48 | +#[derive(Clone)] |
48 | 49 | struct Mount { |
49 | 50 | fs: Arc<dyn Filesystem>, |
50 | 51 | root_inode: Arc<dyn Inode>, |
@@ -88,10 +89,40 @@ impl VfsState { |
88 | 89 | } |
89 | 90 |
|
90 | 91 | /// Removes a mount point by its inode ID. |
91 | | - fn remove_mount(&mut self, mount_point_id: &InodeId) -> Option<()> { |
| 92 | + fn remove_mount(&mut self, mount_point_id: &InodeId) -> Option<Mount> { |
92 | 93 | let mount = self.mounts.remove(mount_point_id)?; |
93 | 94 | self.filesystems.remove(&mount.fs.id())?; |
94 | | - Some(()) |
| 95 | + Some(mount) |
| 96 | + } |
| 97 | + |
| 98 | + /// Collects the mount identified by `mount_point_id` and every nested mount |
| 99 | + /// reachable beneath it. |
| 100 | + fn collect_mount_subtree(&self, mount_point_id: InodeId) -> Option<Vec<(InodeId, Mount)>> { |
| 101 | + let mut pending = Vec::new(); |
| 102 | + let mut subtree = Vec::new(); |
| 103 | + |
| 104 | + pending.push(mount_point_id); |
| 105 | + |
| 106 | + while let Some(current_mount_point_id) = pending.pop() { |
| 107 | + let mount = self.mounts.get(¤t_mount_point_id)?.clone(); |
| 108 | + let current_fs_id = mount.fs.id(); |
| 109 | + |
| 110 | + subtree.push((current_mount_point_id, mount)); |
| 111 | + |
| 112 | + for child_mount_point_id in self.mounts.keys().copied() { |
| 113 | + if child_mount_point_id != current_mount_point_id |
| 114 | + && child_mount_point_id.fs_id() == current_fs_id |
| 115 | + && !subtree |
| 116 | + .iter() |
| 117 | + .any(|(seen, _)| *seen == child_mount_point_id) |
| 118 | + && !pending.contains(&child_mount_point_id) |
| 119 | + { |
| 120 | + pending.push(child_mount_point_id); |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + Some(subtree) |
95 | 126 | } |
96 | 127 |
|
97 | 128 | /// Checks if an inode is a mount point and returns the root inode of the |
@@ -192,15 +223,68 @@ impl VFS { |
192 | 223 | Ok(()) |
193 | 224 | } |
194 | 225 |
|
195 | | - #[expect(unused)] |
196 | | - pub async fn umount(&self, mount_point: Arc<dyn Inode>) -> Result<()> { |
197 | | - let mount_point_id = mount_point.id(); |
| 226 | + pub async fn umount(&self, mount_point: Arc<dyn Inode>, detach: bool) -> Result<()> { |
| 227 | + let mount_point_id = self |
| 228 | + .mount_point_for_root(mount_point.id()) |
| 229 | + .unwrap_or(mount_point.id()); |
198 | 230 |
|
199 | | - // Lock the state and remove the mount. |
200 | | - self.state |
201 | | - .lock_save_irq() |
202 | | - .remove_mount(&mount_point_id) |
203 | | - .ok_or(FsError::NotFound)?; |
| 231 | + if mount_point_id |
| 232 | + == self |
| 233 | + .root_inode |
| 234 | + .lock_save_irq() |
| 235 | + .as_ref() |
| 236 | + .ok_or(FsError::NotFound)? |
| 237 | + .id() |
| 238 | + { |
| 239 | + return Err(KernelError::InUse); |
| 240 | + } |
| 241 | + |
| 242 | + let subtree = { |
| 243 | + let state = self.state.lock_save_irq(); |
| 244 | + state |
| 245 | + .collect_mount_subtree(mount_point_id) |
| 246 | + .ok_or(KernelError::InvalidValue)? |
| 247 | + }; |
| 248 | + |
| 249 | + if !detach && subtree.len() > 1 { |
| 250 | + return Err(KernelError::InUse); |
| 251 | + } |
| 252 | + |
| 253 | + let target_fs_id = subtree |
| 254 | + .first() |
| 255 | + .map(|(_, mount)| mount.fs.id()) |
| 256 | + .ok_or(KernelError::InvalidValue)?; |
| 257 | + |
| 258 | + if !detach { |
| 259 | + let tasks: Vec<_> = TASK_LIST |
| 260 | + .lock_save_irq() |
| 261 | + .values() |
| 262 | + .filter_map(|work| work.upgrade()) |
| 263 | + .collect(); |
| 264 | + |
| 265 | + for work in tasks { |
| 266 | + let task = work.task.t_shared.clone(); |
| 267 | + |
| 268 | + if task.root.lock_save_irq().0.id().fs_id() == target_fs_id |
| 269 | + || task.cwd.lock_save_irq().0.id().fs_id() == target_fs_id |
| 270 | + || task.fd_table.lock_save_irq().any_inode_on_fs(target_fs_id) |
| 271 | + { |
| 272 | + return Err(KernelError::InUse); |
| 273 | + } |
| 274 | + } |
| 275 | + } |
| 276 | + |
| 277 | + let filesystems: Vec<_> = subtree.iter().map(|(_, mount)| mount.fs.clone()).collect(); |
| 278 | + for fs in filesystems { |
| 279 | + fs.sync().await?; |
| 280 | + } |
| 281 | + |
| 282 | + let mut state = self.state.lock_save_irq(); |
| 283 | + for (mount_point_id, _) in subtree { |
| 284 | + state |
| 285 | + .remove_mount(&mount_point_id) |
| 286 | + .ok_or(KernelError::InvalidValue)?; |
| 287 | + } |
204 | 288 |
|
205 | 289 | Ok(()) |
206 | 290 | } |
|
0 commit comments