1use crate::task::CurrentTask;
6use crate::vfs::{
7 CloseFreeSafe, DirectoryEntryType, DirentSink, FileObject, FileOps, FileSystemHandle, FsNode,
8 FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString, SymlinkNode, emit_dotdot,
9 fileops_impl_directory, fileops_impl_noop_sync, fileops_impl_unbounded_seek,
10 fs_node_impl_dir_readonly,
11};
12use starnix_sync::{LockDepMutex, SimpleDirectoryEntriesLock};
13use starnix_uapi::auth::FsCred;
14use starnix_uapi::device_id::DeviceId;
15use starnix_uapi::errno;
16use starnix_uapi::errors::Errno;
17use starnix_uapi::file_mode::{FileMode, mode};
18use starnix_uapi::open_flags::OpenFlags;
19use std::collections::BTreeMap;
20use std::sync::Arc;
21
22pub struct SimpleDirectoryMutator {
24 fs: FileSystemHandle,
25 pub directory: Arc<SimpleDirectory>,
26}
27
28impl SimpleDirectoryMutator {
29 pub fn new(fs: FileSystemHandle, directory: Arc<SimpleDirectory>) -> Self {
31 Self { fs, directory }
32 }
33
34 pub fn node(&self, name: FsString, node: FsNodeHandle) {
35 self.directory.entries.lock().insert(name, node);
36 }
37
38 pub fn entry(&self, name: &str, ops: impl Into<Box<dyn FsNodeOps>>, mode: FileMode) {
39 let name: FsString = name.into();
40 let node =
41 self.fs.create_node_and_allocate_node_id(ops, FsNodeInfo::new(mode, FsCred::root()));
42 self.node(name, node);
43 }
44
45 pub fn entry_etc(
46 &self,
47 name: FsString,
48 ops: impl Into<Box<dyn FsNodeOps>>,
49 mode: FileMode,
50 dev: DeviceId,
51 creds: FsCred,
52 ) {
53 let mut info = FsNodeInfo::new(mode, creds);
54 info.rdev = dev;
55 let node = self.fs.create_node_and_allocate_node_id(ops, info);
56 self.node(name, node);
57 }
58
59 pub fn symlink(&self, name: &FsStr, target: &FsStr) {
60 let (ops, info) = SymlinkNode::new(target, FsCred::root());
61 let node = self.fs.create_node_and_allocate_node_id(ops, info);
62 self.node(name.into(), node);
63 }
64
65 pub fn subdir(&self, name: &str, mode: u32, build_subdir: impl FnOnce(&Self)) {
66 let name: &FsStr = name.into();
67 self.subdir2(name, mode, build_subdir);
68 }
69
70 pub fn subdir2(&self, name: &FsStr, mode: u32, build_subdir: impl FnOnce(&Self)) {
72 let dir = self.directory.subdir(&self.fs, name, mode);
73 let mutator = SimpleDirectoryMutator::new(self.fs.clone(), dir);
74 build_subdir(&mutator);
75 }
76
77 pub fn remove(&self, name: &FsStr) {
78 self.directory.remove(name);
79 }
80}
81
82pub struct SimpleDirectory {
87 entries: LockDepMutex<BTreeMap<FsString, FsNodeHandle>, SimpleDirectoryEntriesLock>,
88 not_found_handler:
89 Box<dyn Fn(&FsStr, &BTreeMap<FsString, FsNodeHandle>) -> Errno + Send + Sync + 'static>,
90}
91
92impl SimpleDirectory {
93 pub fn new() -> Arc<Self> {
96 Self::new_with_handler(|name, locked_entries| {
97 errno!(
98 ENOENT,
99 format!(
100 "looking for {name} in {:?}",
101 locked_entries.keys().map(|e| e.to_string()).collect::<Vec<_>>()
102 )
103 )
104 })
105 }
106
107 pub fn new_with_handler(
113 not_found_handler: impl Fn(&FsStr, &BTreeMap<FsString, FsNodeHandle>) -> Errno
114 + Send
115 + Sync
116 + 'static,
117 ) -> Arc<Self> {
118 let not_found_handler = Box::new(not_found_handler);
119 Arc::new(SimpleDirectory { entries: Default::default(), not_found_handler })
120 }
121
122 pub fn remove(&self, name: &FsStr) {
123 self.entries.lock().remove(name);
124 }
125
126 fn walk<'a>(self: &Arc<Self>, path: &'a FsStr) -> Option<(Arc<Self>, &'a FsStr)> {
127 fn check_component(component: &FsStr) {
128 assert!(!component.is_empty());
129
130 let dot: &FsStr = b".".into();
131 assert_ne!(component, dot);
132
133 let dotdot: &FsStr = b"..".into();
134 assert_ne!(component, dotdot);
135 }
136
137 let mut components = path.split(|c| *c == b'/');
138 let basename = components.next_back()?;
139 let basename: &FsStr = basename.into();
140 check_component(basename);
141 let mut parent = self.clone();
142 while let Some(component) = components.next() {
143 let component: &FsStr = component.into();
144 check_component(component);
145 let Some(next) = parent.get_dir(component) else {
146 return None;
147 };
148 parent = next;
149 }
150 Some((parent, basename))
151 }
152
153 pub fn edit(
154 self: &Arc<Self>,
155 fs: &FileSystemHandle,
156 callback: impl FnOnce(&SimpleDirectoryMutator),
157 ) {
158 let mutator = SimpleDirectoryMutator::new(fs.clone(), self.clone());
159 callback(&mutator);
160 }
161
162 pub fn subdir(&self, fs: &FileSystemHandle, name: &FsStr, mode: u32) -> Arc<SimpleDirectory> {
163 let mut entries = self.entries.lock();
164 if let Some(node) = entries.get(name) {
165 assert!(node.info().mode == mode!(IFDIR, mode));
166 let dir =
167 node.downcast_ops::<Arc<SimpleDirectory>>().expect("subdir is a SimpleDirectory");
168 dir.clone()
169 } else {
170 let dir = SimpleDirectory::new();
171 let info = FsNodeInfo::new(mode!(IFDIR, mode), FsCred::root());
172 let node = fs.create_node_and_allocate_node_id(dir.clone(), info);
173 entries.insert(name.into(), node);
174 dir
175 }
176 }
177
178 fn get(&self, name: &FsStr) -> Option<FsNodeHandle> {
179 let entries = self.entries.lock();
180 entries.get(name).cloned()
181 }
182
183 fn get_dir(&self, name: &FsStr) -> Option<Arc<SimpleDirectory>> {
184 let entries = self.entries.lock();
185 entries
186 .get(name)
187 .and_then(|node| node.downcast_ops::<Arc<SimpleDirectory>>())
188 .map(Arc::clone)
189 }
190
191 pub fn lookup(self: &Arc<Self>, path: &FsStr) -> Option<FsNodeHandle> {
192 let (parent, basename) = self.walk(path)?;
193 parent.get(basename)
194 }
195
196 pub fn into_node(self: Arc<Self>, fs: &FileSystemHandle, mode: u32) -> FsNodeHandle {
197 let info = FsNodeInfo::new(mode!(IFDIR, mode), FsCred::root());
198 fs.create_node_and_allocate_node_id(self, info)
199 }
200}
201
202impl FsNodeOps for Arc<SimpleDirectory> {
203 fs_node_impl_dir_readonly!();
204
205 fn create_file_ops(
206 &self,
207 _node: &FsNode,
208 _current_task: &CurrentTask,
209 _flags: OpenFlags,
210 ) -> Result<Box<dyn FileOps>, Errno> {
211 Ok(Box::new(self.clone()))
212 }
213
214 fn lookup(
215 &self,
216 _node: &FsNode,
217 _current_task: &CurrentTask,
218 name: &FsStr,
219 ) -> Result<FsNodeHandle, Errno> {
220 let entries = self.entries.lock();
221 entries.get(name).cloned().ok_or_else(|| (self.not_found_handler)(name, &entries))
222 }
223}
224
225impl CloseFreeSafe for SimpleDirectory {}
227impl FileOps for SimpleDirectory {
228 fileops_impl_directory!();
229 fileops_impl_noop_sync!();
230 fileops_impl_unbounded_seek!();
231
232 fn readdir(
233 &self,
234 file: &FileObject,
235 _current_task: &CurrentTask,
236 sink: &mut dyn DirentSink,
237 ) -> Result<(), Errno> {
238 emit_dotdot(file, sink)?;
239
240 let entries = self.entries.lock();
243 for (name, node) in entries.iter().skip(sink.offset() as usize - 2) {
244 sink.add(
245 node.ino,
246 sink.offset() + 1,
247 DirectoryEntryType::from_mode(node.info().mode),
248 name.as_ref(),
249 )?;
250 }
251 Ok(())
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::testing::spawn_kernel_and_run;
259 use crate::vfs::FsNodeOps;
260 use starnix_uapi::errno;
261
262 #[fuchsia::test]
263 async fn test_default_not_found_handler() {
264 spawn_kernel_and_run(async |current_task| {
265 let dir = SimpleDirectory::new();
266 let node = dir.clone().into_node(¤t_task.fs().root().entry.node.fs(), 0o777);
267 let result = FsNodeOps::lookup(&dir, &node, ¤t_task, "nonexistent".into());
268 assert_eq!(result.unwrap_err(), errno!(ENOENT));
269 })
270 .await;
271 }
272
273 #[fuchsia::test]
274 async fn test_custom_not_found_handler() {
275 spawn_kernel_and_run(async |current_task| {
276 let dir = SimpleDirectory::new_with_handler(|name, _entries| {
277 if name == "special" { errno!(EACCES) } else { errno!(ENOENT) }
278 });
279 let node = dir.clone().into_node(¤t_task.fs().root().entry.node.fs(), 0o777);
280
281 let result_special = FsNodeOps::lookup(&dir, &node, ¤t_task, "special".into());
282 assert_eq!(result_special.unwrap_err(), errno!(EACCES));
283
284 let result_other = FsNodeOps::lookup(&dir, &node, ¤t_task, "other".into());
285 assert_eq!(result_other.unwrap_err(), errno!(ENOENT));
286 })
287 .await;
288 }
289
290 #[fuchsia::test]
291 async fn test_simple_directory_lookups() {
292 spawn_kernel_and_run(async |current_task| {
293 let fs = current_task.fs().root().entry.node.fs();
294 let dir = SimpleDirectory::new();
295 let mutator = SimpleDirectoryMutator::new(fs.clone(), dir.clone());
296
297 mutator.symlink("link".into(), "target".into());
299
300 mutator.subdir("subdir", 0o755, |sub_mutator| {
302 sub_mutator.symlink("sublink".into(), "subtarget".into());
303 });
304
305 let node = dir.clone().into_node(&fs, 0o777);
306
307 let node1 =
309 FsNodeOps::lookup(&dir, &node, ¤t_task, "link".into()).expect("lookup link");
310 let node2 = FsNodeOps::lookup(&dir, &node, ¤t_task, "link".into())
311 .expect("lookup link again");
312
313 assert!(Arc::ptr_eq(&node1, &node2));
314 assert!(node1.info().mode.is_lnk());
315
316 let subdir1 = FsNodeOps::lookup(&dir, &node, ¤t_task, "subdir".into())
318 .expect("lookup subdir");
319 let subdir2 = FsNodeOps::lookup(&dir, &node, ¤t_task, "subdir".into())
320 .expect("lookup subdir again");
321
322 assert!(Arc::ptr_eq(&subdir1, &subdir2));
323 assert!(subdir1.info().mode.is_dir());
324
325 let sublink = dir.lookup("subdir/sublink".into()).expect("lookup subdir/sublink");
327 assert!(sublink.info().mode.is_lnk());
328
329 mutator.remove("link".into());
331 let result = FsNodeOps::lookup(&dir, &node, ¤t_task, "link".into());
332 assert_eq!(result.unwrap_err(), errno!(ENOENT));
333 })
334 .await;
335 }
336}