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::{FileOpsCore, Locked, Mutex};
13use starnix_uapi::auth::FsCred;
14use starnix_uapi::device_type::DeviceType;
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 {
23 fs: FileSystemHandle,
24 pub directory: Arc<SimpleDirectory>,
25}
26
27impl SimpleDirectoryMutator {
28 pub fn new(fs: FileSystemHandle, directory: Arc<SimpleDirectory>) -> Self {
29 Self { fs, directory }
30 }
31
32 pub fn node(&self, name: FsString, node: FsNodeHandle) {
33 self.directory.entries.lock().insert(name, node);
34 }
35
36 pub fn entry(&self, name: &str, ops: impl Into<Box<dyn FsNodeOps>>, mode: FileMode) {
37 let name: FsString = name.into();
38 let node =
39 self.fs.create_node_and_allocate_node_id(ops, FsNodeInfo::new(mode, FsCred::root()));
40 self.node(name, node);
41 }
42
43 pub fn entry_etc(
44 &self,
45 name: FsString,
46 ops: impl Into<Box<dyn FsNodeOps>>,
47 mode: FileMode,
48 dev: DeviceType,
49 creds: FsCred,
50 ) {
51 let mut info = FsNodeInfo::new(mode, creds);
52 info.rdev = dev;
53 let node = self.fs.create_node_and_allocate_node_id(ops, info);
54 self.node(name, node);
55 }
56
57 pub fn symlink(&self, name: &FsStr, target: &FsStr) {
58 let (ops, info) = SymlinkNode::new(target, FsCred::root());
59 let node = self.fs.create_node_and_allocate_node_id(ops, info);
60 self.node(name.into(), node);
61 }
62
63 pub fn subdir(&self, name: &str, mode: u32, build_subdir: impl FnOnce(&Self)) {
64 let name: &FsStr = name.into();
65 self.subdir2(name, mode, build_subdir);
66 }
67
68 pub fn subdir2(&self, name: &FsStr, mode: u32, build_subdir: impl FnOnce(&Self)) {
70 let dir = self.directory.subdir(&self.fs, name, mode);
71 let mutator = SimpleDirectoryMutator::new(self.fs.clone(), dir);
72 build_subdir(&mutator);
73 }
74
75 pub fn remove(&self, name: &FsStr) {
76 self.directory.remove(name);
77 }
78}
79
80pub struct SimpleDirectory {
81 entries: Mutex<BTreeMap<FsString, FsNodeHandle>>,
82}
83
84impl SimpleDirectory {
85 pub fn new() -> Arc<Self> {
86 Arc::new(SimpleDirectory { entries: Default::default() })
87 }
88
89 pub fn remove(&self, name: &FsStr) {
90 self.entries.lock().remove(name);
91 }
92
93 fn walk<'a>(self: &Arc<Self>, path: &'a FsStr) -> Option<(Arc<Self>, &'a FsStr)> {
94 fn check_component(component: &FsStr) {
95 assert!(!component.is_empty());
96
97 let dot: &FsStr = b".".into();
98 assert_ne!(component, dot);
99
100 let dotdot: &FsStr = b"..".into();
101 assert_ne!(component, dotdot);
102 }
103
104 let mut components = path.split(|c| *c == b'/');
105 let basename = components.next_back()?;
106 let basename: &FsStr = basename.into();
107 check_component(basename);
108 let mut parent = self.clone();
109 while let Some(component) = components.next() {
110 let component: &FsStr = component.into();
111 check_component(component);
112 let Some(next) = parent.get_dir(component) else {
113 return None;
114 };
115 parent = next;
116 }
117 Some((parent, basename))
118 }
119
120 pub fn edit(
121 self: &Arc<Self>,
122 fs: &FileSystemHandle,
123 callback: impl FnOnce(&SimpleDirectoryMutator),
124 ) {
125 let mutator = SimpleDirectoryMutator::new(fs.clone(), self.clone());
126 callback(&mutator);
127 }
128
129 pub fn subdir(&self, fs: &FileSystemHandle, name: &FsStr, mode: u32) -> Arc<SimpleDirectory> {
130 let mut entries = self.entries.lock();
131 if let Some(node) = entries.get(name) {
132 assert!(node.info().mode == mode!(IFDIR, mode));
133 let dir =
134 node.downcast_ops::<Arc<SimpleDirectory>>().expect("subdir is a SimpleDirectory");
135 dir.clone()
136 } else {
137 let dir = SimpleDirectory::new();
138 let info = FsNodeInfo::new(mode!(IFDIR, mode), FsCred::root());
139 let node = fs.create_node_and_allocate_node_id(dir.clone(), info);
140 entries.insert(name.into(), node);
141 dir
142 }
143 }
144
145 fn get(&self, name: &FsStr) -> Option<FsNodeHandle> {
146 let entries = self.entries.lock();
147 entries.get(name).cloned()
148 }
149
150 fn get_dir(&self, name: &FsStr) -> Option<Arc<SimpleDirectory>> {
151 let entries = self.entries.lock();
152 entries
153 .get(name)
154 .and_then(|node| node.downcast_ops::<Arc<SimpleDirectory>>())
155 .map(Arc::clone)
156 }
157
158 pub fn lookup(self: &Arc<Self>, path: &FsStr) -> Option<FsNodeHandle> {
159 let (parent, basename) = self.walk(path)?;
160 parent.get(basename)
161 }
162
163 pub fn into_node(self: Arc<Self>, fs: &FileSystemHandle, mode: u32) -> FsNodeHandle {
164 let info = FsNodeInfo::new(mode!(IFDIR, mode), FsCred::root());
165 fs.create_node_and_allocate_node_id(self, info)
166 }
167}
168
169impl FsNodeOps for Arc<SimpleDirectory> {
170 fs_node_impl_dir_readonly!();
171
172 fn create_file_ops(
173 &self,
174 _locked: &mut Locked<FileOpsCore>,
175 _node: &FsNode,
176 _current_task: &CurrentTask,
177 _flags: OpenFlags,
178 ) -> Result<Box<dyn FileOps>, Errno> {
179 Ok(Box::new(self.clone()))
180 }
181
182 fn lookup(
183 &self,
184 _locked: &mut Locked<FileOpsCore>,
185 _node: &FsNode,
186 _current_task: &CurrentTask,
187 name: &FsStr,
188 ) -> Result<FsNodeHandle, Errno> {
189 let entries = self.entries.lock();
190 entries.get(name).cloned().ok_or_else(|| {
191 errno!(
192 ENOENT,
193 format!(
194 "looking for {name} in {:?}",
195 entries.keys().map(|e| e.to_string()).collect::<Vec<_>>()
196 )
197 )
198 })
199 }
200}
201
202impl CloseFreeSafe for SimpleDirectory {}
204impl FileOps for SimpleDirectory {
205 fileops_impl_directory!();
206 fileops_impl_noop_sync!();
207 fileops_impl_unbounded_seek!();
208
209 fn readdir(
210 &self,
211 _locked: &mut Locked<FileOpsCore>,
212 file: &FileObject,
213 _current_task: &CurrentTask,
214 sink: &mut dyn DirentSink,
215 ) -> Result<(), Errno> {
216 emit_dotdot(file, sink)?;
217
218 let entries = self.entries.lock();
221 for (name, node) in entries.iter().skip(sink.offset() as usize - 2) {
222 sink.add(
223 node.ino,
224 sink.offset() + 1,
225 DirectoryEntryType::from_mode(node.info().mode),
226 name.as_ref(),
227 )?;
228 }
229 Ok(())
230 }
231}