os/fs/proc/
inode.rs

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
15/// 动态内容生成器 trait
16pub trait ContentGenerator: Send + Sync {
17    /// 生成文件内容(每次调用时重新生成)
18    fn generate(&self) -> Result<Vec<u8>, FsError>;
19}
20
21pub struct ProcInode {
22    /// 元数据
23    metadata: SpinLock<InodeMetadata>,
24
25    /// 内容
26    content: ProcInodeContent,
27}
28
29pub enum ProcInodeContent {
30    /// 静态文件(内容固定)
31    Static(Vec<u8>),
32
33    /// 动态文件(每次读取时生成)
34    Dynamic(Arc<dyn ContentGenerator>),
35
36    /// 目录(包含子节点)
37    Directory(Mutex<BTreeMap<String, Arc<ProcInode>>>),
38
39    /// 符号链接
40    Symlink(String),
41
42    /// 动态符号链接(每次读取时生成目标)
43    DynamicSymlink(Arc<dyn Fn() -> String + Send + Sync>),
44}
45
46/// 全局 Inode 编号分配器
47static NEXT_INODE_NO: AtomicUsize = AtomicUsize::new(1);
48
49impl ProcInode {
50    /// 创建静态文件 inode
51    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, // proc 文件总是返回 size = 0
63                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    /// 创建动态文件 inode
75    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, // proc 文件总是返回 size = 0
91                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    /// 创建目录 inode
103    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, // . 和 ..
119                blocks: 0,
120                rdev: 0,
121            }),
122            content: ProcInodeContent::Directory(Mutex::new(BTreeMap::new())),
123        })
124    }
125
126    /// 创建符号链接 inode
127    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    /// 创建动态符号链接 inode
151    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, // 动态链接的大小未知
166                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    /// 向目录添加子节点
178    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    /// 为指定 PID 创建进程目录
189    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        // 获取任务
194        let task = TASK_MANAGER.lock().get_task(pid)?;
195
196        // 创建进程目录
197        let proc_dir = Self::new_directory(FileMode::from_bits_truncate(
198            0o555 | FileMode::S_IFDIR.bits(),
199        ));
200
201        // 创建 status 文件
202        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        // 创建 stat 文件
210        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        // 创建 cmdline 文件
218        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                // 先从已有的子节点中查找
265                if let Some(child) = children.lock().get(name).cloned() {
266                    return Ok(child as Arc<dyn Inode>);
267                }
268
269                // 检查是否为进程目录(数字命名)
270                if let Ok(pid) = name.parse::<u32>() {
271                    // 动态创建进程目录
272                    if let Some(proc_dir) = self.create_process_dir(pid) {
273                        // 缓存到children中
274                        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}