1use alloc::{
2 string::String,
3 sync::{Arc, Weak},
4 vec,
5};
6use core::{
7 iter, mem,
8 sync::atomic::{AtomicU64, Ordering},
9 task::Context,
10};
11
12use axpoll::{IoEvents, Pollable};
13use hashbrown::HashMap;
14use inherit_methods_macro::inherit_methods;
15
16use crate::{
17 DirEntry, DirEntrySink, Filesystem, FilesystemOps, Metadata, MetadataUpdate, Mutex, MutexGuard,
18 NodeFlags, NodePermission, NodeType, OpenOptions, ReferenceKey, TypeMap, VfsError, VfsResult,
19 path::{DOT, DOTDOT, PathBuf},
20};
21
22#[derive(Debug)]
23pub struct Mountpoint {
24 root: DirEntry,
26 location: Option<Location>,
28 children: Mutex<HashMap<ReferenceKey, Weak<Self>>>,
30 device: u64,
32}
33
34impl Mountpoint {
35 pub fn new(fs: &Filesystem, location_in_parent: Option<Location>) -> Arc<Self> {
36 static DEVICE_COUNTER: AtomicU64 = AtomicU64::new(1);
37
38 let root = fs.root_dir();
39 Arc::new(Self {
40 root,
41 location: location_in_parent,
42 children: Mutex::default(),
43 device: DEVICE_COUNTER.fetch_add(1, Ordering::Relaxed),
44 })
45 }
46
47 pub fn new_root(fs: &Filesystem) -> Arc<Self> {
48 Self::new(fs, None)
49 }
50
51 pub fn root_location(self: &Arc<Self>) -> Location {
52 Location::new(self.clone(), self.root.clone())
53 }
54
55 pub fn location(&self) -> Option<Location> {
57 self.location.clone()
58 }
59
60 pub fn is_root(&self) -> bool {
61 self.location.is_none()
62 }
63
64 pub(crate) fn effective_mountpoint(self: &Arc<Self>) -> Arc<Mountpoint> {
71 let mut mountpoint = self.clone();
72 while let Some(mount) = mountpoint.root.as_dir().unwrap().mountpoint() {
73 mountpoint = mount;
74 }
75 mountpoint
76 }
77
78 pub fn device(self: &Arc<Self>) -> u64 {
79 self.device
80 }
81}
82
83#[derive(Debug, Clone)]
84pub struct Location {
85 mountpoint: Arc<Mountpoint>,
86 entry: DirEntry,
87}
88
89#[inherit_methods(from = "self.entry")]
90impl Location {
91 pub fn inode(&self) -> u64;
92
93 pub fn filesystem(&self) -> &dyn FilesystemOps;
94
95 pub fn update_metadata(&self, update: MetadataUpdate) -> VfsResult<()>;
96
97 #[allow(clippy::len_without_is_empty)]
98 pub fn len(&self) -> VfsResult<u64>;
99
100 pub fn sync(&self, data_only: bool) -> VfsResult<()>;
101
102 pub fn is_file(&self) -> bool;
103
104 pub fn is_dir(&self) -> bool;
105
106 pub fn node_type(&self) -> NodeType;
107
108 pub fn is_root_of_mount(&self) -> bool;
109
110 pub fn read_link(&self) -> VfsResult<String>;
111
112 pub fn ioctl(&self, cmd: u32, arg: usize) -> VfsResult<usize>;
113
114 pub fn flags(&self) -> NodeFlags;
115
116 pub fn user_data(&self) -> MutexGuard<'_, TypeMap>;
117}
118
119impl Location {
120 pub fn new(mountpoint: Arc<Mountpoint>, entry: DirEntry) -> Self {
121 Self { mountpoint, entry }
122 }
123
124 fn wrap(&self, entry: DirEntry) -> Self {
125 Self::new(self.mountpoint.clone(), entry)
126 }
127
128 pub fn mountpoint(&self) -> &Arc<Mountpoint> {
129 &self.mountpoint
130 }
131
132 pub fn entry(&self) -> &DirEntry {
133 &self.entry
134 }
135
136 pub fn name(&self) -> &str {
137 if self.is_root_of_mount() {
138 self.mountpoint.location.as_ref().map_or("", Location::name)
139 } else {
140 self.entry.name()
141 }
142 }
143
144 pub fn parent(&self) -> Option<Self> {
145 if !self.is_root_of_mount() {
146 return Some(self.wrap(self.entry.parent().unwrap()));
147 }
148 self.mountpoint.location()?.parent()
149 }
150
151 pub fn is_root(&self) -> bool {
152 self.mountpoint.is_root() && self.entry.is_root_of_mount()
153 }
154
155 pub fn check_is_dir(&self) -> VfsResult<()> {
156 self.entry.as_dir().map(|_| ())
157 }
158
159 pub fn check_is_file(&self) -> VfsResult<()> {
160 self.entry.as_file().map(|_| ())
161 }
162
163 pub fn metadata(&self) -> VfsResult<Metadata> {
164 let mut metadata = self.entry.metadata()?;
165 metadata.device = self.mountpoint.device();
166 Ok(metadata)
167 }
168
169 pub fn absolute_path(&self) -> VfsResult<PathBuf> {
170 let mut components = vec![];
171 let mut cur = self.clone();
172 loop {
173 cur.entry.collect_absolute_path(&mut components);
174 cur = match cur.mountpoint.location() {
175 Some(loc) => loc,
176 None => break,
177 }
178 }
179 Ok(iter::once("/")
180 .chain(components.iter().map(String::as_str).rev())
181 .collect())
182 }
183
184 pub fn ptr_eq(&self, other: &Self) -> bool {
185 Arc::ptr_eq(&self.mountpoint, &other.mountpoint) && self.entry.ptr_eq(&other.entry)
186 }
187
188 pub fn is_mountpoint(&self) -> bool {
189 self.entry.as_dir().is_ok_and(|it| it.is_mountpoint())
190 }
191
192 fn resolve_mountpoint(self) -> Self {
194 let Some(mountpoint) = self.entry.as_dir().ok().and_then(|it| it.mountpoint()) else {
195 return self;
196 };
197 let mountpoint = mountpoint.effective_mountpoint();
198 let entry = mountpoint.root.clone();
199 Self::new(mountpoint, entry)
200 }
201
202 pub fn lookup_no_follow(&self, name: &str) -> VfsResult<Self> {
203 Ok(match name {
204 DOT => self.clone(),
205 DOTDOT => self.parent().unwrap_or_else(|| self.clone()),
206 _ => {
207 let loc = Self::new(self.mountpoint.clone(), self.entry.as_dir()?.lookup(name)?);
208 loc.resolve_mountpoint()
209 }
210 })
211 }
212
213 pub fn create(
214 &self,
215 name: &str,
216 node_type: NodeType,
217 permission: NodePermission,
218 ) -> VfsResult<Self> {
219 self.entry
220 .as_dir()?
221 .create(name, node_type, permission)
222 .map(|entry| self.wrap(entry))
223 }
224
225 pub fn link(&self, name: &str, node: &Self) -> VfsResult<Self> {
226 if !Arc::ptr_eq(&self.mountpoint, &node.mountpoint) {
227 return Err(VfsError::CrossesDevices);
228 }
229 self.entry
230 .as_dir()?
231 .link(name, &node.entry)
232 .map(|entry| self.wrap(entry))
233 }
234
235 pub fn rename(&self, src_name: &str, dst_dir: &Self, dst_name: &str) -> VfsResult<()> {
236 if !Arc::ptr_eq(&self.mountpoint, &dst_dir.mountpoint) {
237 return Err(VfsError::CrossesDevices);
238 }
239 if !self.ptr_eq(dst_dir) && self.entry.is_ancestor_of(&dst_dir.entry)? {
240 return Err(VfsError::InvalidInput);
241 }
242 self.entry
243 .as_dir()?
244 .rename(src_name, dst_dir.entry.as_dir()?, dst_name)
245 }
246
247 pub fn unlink(&self, name: &str, is_dir: bool) -> VfsResult<()> {
248 self.entry.as_dir()?.unlink(name, is_dir)
249 }
250
251 pub fn open_file(&self, name: &str, options: &OpenOptions) -> VfsResult<Location> {
252 self.entry
253 .as_dir()?
254 .open_file(name, options)
255 .map(|entry| self.wrap(entry).resolve_mountpoint())
256 }
257
258 pub fn read_dir(&self, offset: u64, sink: &mut dyn DirEntrySink) -> VfsResult<usize> {
259 self.entry.as_dir()?.read_dir(offset, sink)
260 }
261
262 pub fn mount(&self, fs: &Filesystem) -> VfsResult<Arc<Mountpoint>> {
263 let mut mountpoint = self.entry.as_dir()?.mountpoint.lock();
264 if mountpoint.is_some() {
265 return Err(VfsError::ResourceBusy);
266 }
267 let result = Mountpoint::new(fs, Some(self.clone()));
268 *mountpoint = Some(result.clone());
269 self.mountpoint
270 .children
271 .lock()
272 .insert(self.entry.key(), Arc::downgrade(&result));
273 Ok(result)
274 }
275
276 pub fn unmount(&self) -> VfsResult<()> {
277 if !self.is_root_of_mount() {
278 return Err(VfsError::InvalidInput);
279 }
280 if !self.mountpoint.children.lock().is_empty() {
281 return Err(VfsError::ResourceBusy);
282 }
283 assert!(self.entry.ptr_eq(&self.mountpoint.root));
284 self.entry.as_dir()?.forget();
285 if let Some(parent_loc) = &self.mountpoint.location {
286 *parent_loc.entry.as_dir()?.mountpoint.lock() = None;
287 }
288 Ok(())
289 }
290
291 pub fn unmount_all(&self) -> VfsResult<()> {
292 if !self.is_root_of_mount() {
293 return Err(VfsError::InvalidInput);
294 }
295 let children = mem::take(&mut *self.mountpoint.children.lock());
296 for (_, child) in children {
297 if let Some(child) = child.upgrade() {
298 child.root_location().unmount_all()?;
299 }
300 }
301 self.unmount()
302 }
303}
304
305#[inherit_methods(from = "self.entry")]
306impl Pollable for Location {
307 fn poll(&self) -> IoEvents;
308
309 fn register(&self, context: &mut Context<'_>, events: IoEvents);
310}