1use alloc::collections::BTreeMap;
9use alloc::string::{String, ToString};
10use alloc::sync::Arc;
11use alloc::vec::Vec;
12use core::any::Any;
13use core::sync::atomic::{AtomicUsize, Ordering};
14use spin::Mutex;
15
16use crate::uapi::time::TimeSpec;
17use crate::vfs::{DirEntry, FileMode, FsError, Inode, InodeMetadata, InodeType};
18
19pub type AttrShowFn = dyn Fn() -> Result<String, FsError> + Send + Sync;
23pub type AttrStoreFn = dyn Fn(&str) -> Result<(), FsError> + Send + Sync;
24
25pub struct SysfsAttr {
27 pub name: String,
28 pub mode: FileMode,
29 pub show: Arc<AttrShowFn>,
30 pub store: Option<Arc<AttrStoreFn>>,
31}
32
33pub enum SysfsInodeContent {
35 Directory(Mutex<BTreeMap<String, Arc<SysfsInode>>>),
37
38 Attribute(SysfsAttr),
40
41 Symlink(String),
43}
44
45pub struct SysfsInode {
47 inode_no: usize,
48 inode_type: InodeType,
49 metadata: Mutex<InodeMetadata>,
50 content: SysfsInodeContent,
51}
52
53static NEXT_INODE_NO: AtomicUsize = AtomicUsize::new(1);
54
55impl SysfsInode {
56 pub fn new_directory(mode: FileMode) -> Arc<Self> {
58 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
59 let now = TimeSpec::now();
60 Arc::new(Self {
61 inode_no,
62 inode_type: InodeType::Directory,
63 metadata: Mutex::new(InodeMetadata {
64 inode_no,
65 inode_type: InodeType::Directory,
66 size: 0,
67 mode,
68 nlinks: 2,
69 uid: 0,
70 gid: 0,
71 atime: now,
72 mtime: now,
73 ctime: now,
74 blocks: 0,
75 rdev: 0,
76 }),
77 content: SysfsInodeContent::Directory(Mutex::new(BTreeMap::new())),
78 })
79 }
80
81 pub fn new_attribute(attr: SysfsAttr) -> Arc<Self> {
83 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
84 let mode = attr.mode.clone();
85 let now = TimeSpec::now();
86 Arc::new(Self {
87 inode_no,
88 inode_type: InodeType::File,
89 metadata: Mutex::new(InodeMetadata {
90 inode_no,
91 inode_type: InodeType::File,
92 size: 0, mode,
94 nlinks: 1,
95 uid: 0,
96 gid: 0,
97 atime: now,
98 mtime: now,
99 ctime: now,
100 blocks: 0,
101 rdev: 0,
102 }),
103 content: SysfsInodeContent::Attribute(attr),
104 })
105 }
106
107 pub fn new_symlink(target: String) -> Arc<Self> {
109 let inode_no = NEXT_INODE_NO.fetch_add(1, Ordering::Relaxed);
110 let now = TimeSpec::now();
111 Arc::new(Self {
112 inode_no,
113 inode_type: InodeType::Symlink,
114 metadata: Mutex::new(InodeMetadata {
115 inode_no,
116 inode_type: InodeType::Symlink,
117 size: target.len(),
118 mode: FileMode::from_bits_truncate(0o777),
119 nlinks: 1,
120 uid: 0,
121 gid: 0,
122 atime: now,
123 mtime: now,
124 ctime: now,
125 blocks: 0,
126 rdev: 0,
127 }),
128 content: SysfsInodeContent::Symlink(target),
129 })
130 }
131
132 pub fn add_child(&self, name: &str, child: Arc<SysfsInode>) -> Result<(), FsError> {
134 match &self.content {
135 SysfsInodeContent::Directory(children) => {
136 children.lock().insert(name.to_string(), child);
137 Ok(())
138 }
139 _ => Err(FsError::NotDirectory),
140 }
141 }
142
143 pub fn readlink(&self) -> Result<String, FsError> {
145 match &self.content {
146 SysfsInodeContent::Symlink(target) => Ok(target.clone()),
147 _ => Err(FsError::InvalidArgument),
148 }
149 }
150}
151
152impl Inode for SysfsInode {
153 fn metadata(&self) -> Result<InodeMetadata, FsError> {
154 Ok(self.metadata.lock().clone())
155 }
156
157 fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize, FsError> {
158 match &self.content {
159 SysfsInodeContent::Attribute(attr) => {
160 let content = (attr.show)()?;
162 let data = content.as_bytes();
163
164 if offset >= data.len() {
165 return Ok(0);
166 }
167
168 let to_read = (data.len() - offset).min(buf.len());
169 buf[..to_read].copy_from_slice(&data[offset..offset + to_read]);
170 Ok(to_read)
171 }
172 _ => Err(FsError::IsDirectory),
173 }
174 }
175
176 fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize, FsError> {
177 match &self.content {
178 SysfsInodeContent::Attribute(attr) => {
179 if let Some(store) = &attr.store {
181 if offset != 0 {
182 return Err(FsError::InvalidArgument);
183 }
184
185 let content =
187 core::str::from_utf8(buf).map_err(|_| FsError::InvalidArgument)?;
188
189 (store)(content)?;
190 Ok(buf.len())
191 } else {
192 Err(FsError::PermissionDenied)
193 }
194 }
195 _ => Err(FsError::IsDirectory),
196 }
197 }
198
199 fn readdir(&self) -> Result<Vec<DirEntry>, FsError> {
200 match &self.content {
201 SysfsInodeContent::Directory(children) => {
202 let mut entries = Vec::new();
203
204 entries.push(DirEntry {
205 name: ".".to_string(),
206 inode_no: self.inode_no,
207 inode_type: InodeType::Directory,
208 });
209
210 entries.push(DirEntry {
211 name: "..".to_string(),
212 inode_no: self.inode_no,
213 inode_type: InodeType::Directory,
214 });
215
216 for (name, child) in children.lock().iter() {
217 entries.push(DirEntry {
218 name: name.clone(),
219 inode_no: child.inode_no,
220 inode_type: child.inode_type,
221 });
222 }
223
224 Ok(entries)
225 }
226 _ => Err(FsError::NotDirectory),
227 }
228 }
229
230 fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>, FsError> {
231 match &self.content {
232 SysfsInodeContent::Directory(children) => children
233 .lock()
234 .get(name)
235 .cloned()
236 .map(|inode| inode as Arc<dyn Inode>)
237 .ok_or(FsError::NotFound),
238 _ => Err(FsError::NotDirectory),
239 }
240 }
241
242 fn create(&self, _name: &str, _mode: FileMode) -> Result<Arc<dyn Inode>, FsError> {
243 Err(FsError::ReadOnlyFs)
245 }
246
247 fn mkdir(&self, _name: &str, _mode: FileMode) -> Result<Arc<dyn Inode>, FsError> {
248 Err(FsError::ReadOnlyFs)
250 }
251
252 fn truncate(&self, _size: usize) -> Result<(), FsError> {
253 Err(FsError::ReadOnlyFs)
255 }
256
257 fn sync(&self) -> Result<(), FsError> {
258 Ok(())
260 }
261
262 fn symlink(&self, _name: &str, _target: &str) -> Result<Arc<dyn Inode>, FsError> {
263 Err(FsError::PermissionDenied)
264 }
265
266 fn link(&self, _name: &str, _target: &Arc<dyn Inode>) -> Result<(), FsError> {
267 Err(FsError::PermissionDenied)
268 }
269
270 fn unlink(&self, _name: &str) -> Result<(), FsError> {
271 Err(FsError::PermissionDenied)
272 }
273
274 fn rmdir(&self, _name: &str) -> Result<(), FsError> {
275 Err(FsError::PermissionDenied)
276 }
277
278 fn rename(
279 &self,
280 _old_name: &str,
281 _new_parent: Arc<dyn Inode>,
282 _new_name: &str,
283 ) -> Result<(), FsError> {
284 Err(FsError::PermissionDenied)
285 }
286
287 fn set_times(&self, _atime: Option<TimeSpec>, _mtime: Option<TimeSpec>) -> Result<(), FsError> {
288 Err(FsError::PermissionDenied)
289 }
290
291 fn readlink(&self) -> Result<String, FsError> {
292 match &self.content {
293 SysfsInodeContent::Symlink(target) => Ok(target.clone()),
294 _ => Err(FsError::InvalidArgument),
295 }
296 }
297
298 fn as_any(&self) -> &dyn Any {
299 self
300 }
301
302 fn mknod(&self, _name: &str, _mode: FileMode, _dev: u64) -> Result<Arc<dyn Inode>, FsError> {
303 Err(FsError::NotSupported)
304 }
305
306 fn chmod(&self, _mode: FileMode) -> Result<(), FsError> {
307 Err(FsError::NotSupported)
308 }
309
310 fn chown(&self, _uid: u32, _gid: u32) -> Result<(), FsError> {
311 Err(FsError::NotSupported)
312 }
313}