1use core::sync::atomic::{AtomicUsize, Ordering};
2
3use crate::{
4 sync::{Mutex, SpinLock},
5 uapi::time::TimeSpec,
6 vfs::{DirEntry, FileMode, FsError, Inode, InodeMetadata, InodeType},
7};
8use alloc::{
9 collections::BTreeMap,
10 string::{String, ToString},
11 sync::Arc,
12 vec::Vec,
13};
14
15pub trait ContentGenerator: Send + Sync {
17 fn generate(&self) -> Result<Vec<u8>, FsError>;
19}
20
21pub struct ProcInode {
22 metadata: SpinLock<InodeMetadata>,
24
25 content: ProcInodeContent,
27}
28
29pub enum ProcInodeContent {
30 Static(Vec<u8>),
32
33 Dynamic(Arc<dyn ContentGenerator>),
35
36 Directory(Mutex<BTreeMap<String, Arc<ProcInode>>>),
38
39 Symlink(String),
41
42 DynamicSymlink(Arc<dyn Fn() -> String + Send + Sync>),
44}
45
46static NEXT_INODE_NO: AtomicUsize = AtomicUsize::new(1);
48
49impl ProcInode {
50 pub fn new_static_file(_name: &str, content: Vec<u8>, mode: FileMode) -> Arc<Self> {
52 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
53 let now = TimeSpec::now();
54
55 Arc::new(Self {
56 metadata: SpinLock::new(InodeMetadata {
57 inode_no,
58 inode_type: InodeType::File,
59 mode,
60 uid: 0,
61 gid: 0,
62 size: 0, atime: now,
64 mtime: now,
65 ctime: now,
66 nlinks: 1,
67 blocks: 0,
68 rdev: 0,
69 }),
70 content: ProcInodeContent::Static(content),
71 })
72 }
73
74 pub fn new_dynamic_file(
76 _name: &str,
77 generator: Arc<dyn ContentGenerator>,
78 mode: FileMode,
79 ) -> Arc<Self> {
80 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
81 let now = TimeSpec::now();
82
83 Arc::new(Self {
84 metadata: SpinLock::new(InodeMetadata {
85 inode_no,
86 inode_type: InodeType::File,
87 mode,
88 uid: 0,
89 gid: 0,
90 size: 0, atime: now,
92 mtime: now,
93 ctime: now,
94 nlinks: 1,
95 blocks: 0,
96 rdev: 0,
97 }),
98 content: ProcInodeContent::Dynamic(generator),
99 })
100 }
101
102 pub fn new_directory(mode: FileMode) -> Arc<Self> {
104 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
105 let now = TimeSpec::now();
106
107 Arc::new(Self {
108 metadata: SpinLock::new(InodeMetadata {
109 inode_no,
110 inode_type: InodeType::Directory,
111 mode,
112 uid: 0,
113 gid: 0,
114 size: 0,
115 atime: now,
116 mtime: now,
117 ctime: now,
118 nlinks: 2, blocks: 0,
120 rdev: 0,
121 }),
122 content: ProcInodeContent::Directory(Mutex::new(BTreeMap::new())),
123 })
124 }
125
126 pub fn new_symlink(_name: &str, target: String) -> Arc<Self> {
128 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
129 let now = TimeSpec::now();
130
131 Arc::new(Self {
132 metadata: SpinLock::new(InodeMetadata {
133 inode_no,
134 inode_type: InodeType::Symlink,
135 mode: FileMode::from_bits_truncate(0o777),
136 uid: 0,
137 gid: 0,
138 size: target.len(),
139 atime: now,
140 mtime: now,
141 ctime: now,
142 nlinks: 1,
143 blocks: 0,
144 rdev: 0,
145 }),
146 content: ProcInodeContent::Symlink(target),
147 })
148 }
149
150 pub fn new_dynamic_symlink<F>(_name: &str, generator: F) -> Arc<Self>
152 where
153 F: Fn() -> String + Send + Sync + 'static,
154 {
155 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
156 let now = TimeSpec::now();
157
158 Arc::new(Self {
159 metadata: SpinLock::new(InodeMetadata {
160 inode_no,
161 inode_type: InodeType::Symlink,
162 mode: FileMode::from_bits_truncate(0o777),
163 uid: 0,
164 gid: 0,
165 size: 0, atime: now,
167 mtime: now,
168 ctime: now,
169 nlinks: 1,
170 blocks: 0,
171 rdev: 0,
172 }),
173 content: ProcInodeContent::DynamicSymlink(Arc::new(generator)),
174 })
175 }
176
177 pub fn add_child(&self, name: &str, child: Arc<ProcInode>) -> Result<(), FsError> {
179 match &self.content {
180 ProcInodeContent::Directory(children) => {
181 children.lock().insert(name.to_string(), child);
182 Ok(())
183 }
184 _ => Err(FsError::NotDirectory),
185 }
186 }
187
188 fn create_process_dir(&self, pid: u32) -> Option<Arc<ProcInode>> {
190 use crate::fs::proc::generators::{CmdlineGenerator, StatGenerator, StatusGenerator};
191 use crate::kernel::{TASK_MANAGER, TaskManagerTrait};
192
193 let task = TASK_MANAGER.lock().get_task(pid)?;
195
196 let proc_dir = Self::new_directory(FileMode::from_bits_truncate(
198 0o555 | FileMode::S_IFDIR.bits(),
199 ));
200
201 let status = Self::new_dynamic_file(
203 "status",
204 Arc::new(StatusGenerator::new(Arc::downgrade(&task))),
205 FileMode::from_bits_truncate(0o444),
206 );
207 let _ = proc_dir.add_child("status", status);
208
209 let stat = Self::new_dynamic_file(
211 "stat",
212 Arc::new(StatGenerator::new(Arc::downgrade(&task))),
213 FileMode::from_bits_truncate(0o444),
214 );
215 let _ = proc_dir.add_child("stat", stat);
216
217 let cmdline = Self::new_dynamic_file(
219 "cmdline",
220 Arc::new(CmdlineGenerator::new(Arc::downgrade(&task))),
221 FileMode::from_bits_truncate(0o444),
222 );
223 let _ = proc_dir.add_child("cmdline", cmdline);
224
225 Some(proc_dir)
226 }
227}
228
229impl Inode for ProcInode {
230 fn metadata(&self) -> Result<InodeMetadata, FsError> {
231 Ok(self.metadata.lock().clone())
232 }
233
234 fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize, FsError> {
235 match &self.content {
236 ProcInodeContent::Static(data) => {
237 if offset >= data.len() {
238 return Ok(0);
239 }
240 let to_read = (data.len() - offset).min(buf.len());
241 buf[..to_read].copy_from_slice(&data[offset..offset + to_read]);
242 Ok(to_read)
243 }
244 ProcInodeContent::Dynamic(generator) => {
245 let data = generator.generate()?;
246 if offset >= data.len() {
247 return Ok(0);
248 }
249 let to_read = (data.len() - offset).min(buf.len());
250 buf[..to_read].copy_from_slice(&data[offset..offset + to_read]);
251 Ok(to_read)
252 }
253 _ => Err(FsError::IsDirectory),
254 }
255 }
256
257 fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize, FsError> {
258 Err(FsError::PermissionDenied)
259 }
260
261 fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>, FsError> {
262 match &self.content {
263 ProcInodeContent::Directory(children) => {
264 if let Some(child) = children.lock().get(name).cloned() {
266 return Ok(child as Arc<dyn Inode>);
267 }
268
269 if let Ok(pid) = name.parse::<u32>() {
271 if let Some(proc_dir) = self.create_process_dir(pid) {
273 children.lock().insert(name.to_string(), proc_dir.clone());
275 return Ok(proc_dir as Arc<dyn Inode>);
276 }
277 }
278
279 Err(FsError::NotFound)
280 }
281 _ => Err(FsError::NotDirectory),
282 }
283 }
284
285 fn create(&self, _name: &str, _mode: FileMode) -> Result<Arc<dyn Inode>, FsError> {
286 Err(FsError::PermissionDenied)
287 }
288
289 fn mkdir(&self, _name: &str, _mode: FileMode) -> Result<Arc<dyn Inode>, FsError> {
290 Err(FsError::PermissionDenied)
291 }
292
293 fn symlink(&self, _name: &str, _target: &str) -> Result<Arc<dyn Inode>, FsError> {
294 Err(FsError::PermissionDenied)
295 }
296
297 fn link(&self, _name: &str, _target: &Arc<dyn Inode>) -> Result<(), FsError> {
298 Err(FsError::PermissionDenied)
299 }
300
301 fn unlink(&self, _name: &str) -> Result<(), FsError> {
302 Err(FsError::PermissionDenied)
303 }
304
305 fn rmdir(&self, _name: &str) -> Result<(), FsError> {
306 Err(FsError::PermissionDenied)
307 }
308
309 fn rename(
310 &self,
311 _old_name: &str,
312 _new_parent: Arc<dyn Inode>,
313 _new_name: &str,
314 ) -> Result<(), FsError> {
315 Err(FsError::PermissionDenied)
316 }
317
318 fn readdir(&self) -> Result<Vec<DirEntry>, FsError> {
319 match &self.content {
320 ProcInodeContent::Directory(children) => {
321 let metadata = self.metadata.lock();
322 let mut entries = Vec::new();
323
324 entries.push(DirEntry {
325 name: ".".to_string(),
326 inode_no: metadata.inode_no,
327 inode_type: InodeType::Directory,
328 });
329 entries.push(DirEntry {
330 name: "..".to_string(),
331 inode_no: metadata.inode_no,
332 inode_type: InodeType::Directory,
333 });
334
335 for (name, child) in children.lock().iter() {
336 let child_meta = child.metadata.lock();
337 entries.push(DirEntry {
338 name: name.clone(),
339 inode_no: child_meta.inode_no,
340 inode_type: child_meta.inode_type,
341 });
342 }
343
344 Ok(entries)
345 }
346 _ => Err(FsError::NotDirectory),
347 }
348 }
349
350 fn truncate(&self, _size: usize) -> Result<(), FsError> {
351 Err(FsError::PermissionDenied)
352 }
353
354 fn sync(&self) -> Result<(), FsError> {
355 Ok(())
356 }
357
358 fn as_any(&self) -> &dyn core::any::Any {
359 self
360 }
361
362 fn set_times(&self, _atime: Option<TimeSpec>, _mtime: Option<TimeSpec>) -> Result<(), FsError> {
363 Err(FsError::PermissionDenied)
364 }
365
366 fn readlink(&self) -> Result<String, FsError> {
367 match &self.content {
368 ProcInodeContent::Symlink(target) => Ok(target.clone()),
369 ProcInodeContent::DynamicSymlink(generator) => Ok(generator()),
370 _ => Err(FsError::InvalidArgument),
371 }
372 }
373
374 fn mknod(&self, _name: &str, _mode: FileMode, _dev: u64) -> Result<Arc<dyn Inode>, FsError> {
375 Err(FsError::NotSupported)
376 }
377
378 fn chmod(&self, _mode: FileMode) -> Result<(), FsError> {
379 Err(FsError::NotSupported)
380 }
381
382 fn chown(&self, _uid: u32, _gid: u32) -> Result<(), FsError> {
383 Err(FsError::NotSupported)
384 }
385}