1use alloc::collections::BTreeMap;
6use alloc::string::{String, ToString};
7use alloc::sync::{Arc, Weak};
8use alloc::vec::Vec;
9
10use crate::config::PAGE_SIZE;
11use crate::mm::address::{ConvertablePaddr, PageNum, UsizeConvert};
12use crate::mm::frame_allocator::{FrameTracker, alloc_frame};
13use crate::sync::{Mutex, SpinLock};
14use crate::uapi::time::TimeSpec;
15use crate::vfs::{DirEntry, FileMode, FsError, Inode, InodeMetadata, InodeType};
16
17pub struct TmpfsInode {
21 metadata: SpinLock<InodeMetadata>,
23
24 data: Mutex<Vec<Option<Arc<FrameTracker>>>>,
28
29 parent: Mutex<Weak<TmpfsInode>>,
31
32 children: Mutex<BTreeMap<String, Arc<TmpfsInode>>>,
34
35 stats: Arc<Mutex<TmpfsStats>>,
37
38 self_ref: Mutex<Weak<TmpfsInode>>,
40}
41
42#[derive(Debug, Clone)]
44pub struct TmpfsStats {
45 pub allocated_pages: usize,
47
48 pub max_pages: usize,
50
51 pub next_inode_no: usize,
53}
54
55impl TmpfsInode {
56 pub fn new(
58 inode_no: usize,
59 inode_type: InodeType,
60 mode: FileMode,
61 parent: Weak<TmpfsInode>,
62 stats: Arc<Mutex<TmpfsStats>>,
63 ) -> Arc<Self> {
64 let now = TimeSpec::now();
65
66 let mode = mode & !FileMode::S_IFMT;
68
69 let mode = match inode_type {
71 InodeType::Directory => mode | FileMode::S_IFDIR,
72 InodeType::File => mode | FileMode::S_IFREG,
73 InodeType::Symlink => mode | FileMode::S_IFLNK,
74 InodeType::CharDevice => mode | FileMode::S_IFCHR,
75 InodeType::BlockDevice => mode | FileMode::S_IFBLK,
76 InodeType::Fifo => mode | FileMode::S_IFIFO,
77 InodeType::Socket => mode | FileMode::S_IFSOCK,
78 };
79
80 let metadata = InodeMetadata {
81 inode_no,
82 inode_type,
83 mode,
84 uid: 0,
85 gid: 0,
86 size: 0,
87 atime: now,
88 mtime: now,
89 ctime: now,
90 nlinks: if inode_type == InodeType::Directory {
91 2
92 } else {
93 1
94 }, blocks: 0,
96 rdev: 0, };
98
99 Arc::new(Self {
100 metadata: SpinLock::new(metadata),
101 data: Mutex::new(Vec::new()),
102 parent: Mutex::new(parent),
103 children: Mutex::new(BTreeMap::new()),
104 stats,
105 self_ref: Mutex::new(Weak::new()),
106 })
107 }
108
109 pub fn new_root(stats: Arc<Mutex<TmpfsStats>>) -> Arc<Self> {
111 let stats_guard = stats.lock();
112 let inode_no = stats_guard.next_inode_no;
113 drop(stats_guard);
114
115 let mut stats_guard = stats.lock();
116 stats_guard.next_inode_no += 1;
117 drop(stats_guard);
118
119 let root = Self::new(
120 inode_no,
121 InodeType::Directory,
122 FileMode::S_IRUSR
123 | FileMode::S_IWUSR
124 | FileMode::S_IXUSR
125 | FileMode::S_IRGRP
126 | FileMode::S_IXGRP
127 | FileMode::S_IROTH
128 | FileMode::S_IXOTH,
129 Weak::new(),
130 stats,
131 );
132
133 *root.self_ref.lock() = Arc::downgrade(&root);
135
136 root
137 }
138
139 fn alloc_inode_no(&self) -> usize {
141 let mut stats = self.stats.lock();
142 let inode_no = stats.next_inode_no;
143 stats.next_inode_no += 1;
144 inode_no
145 }
146
147 fn can_alloc_pages(&self, num_pages: usize) -> bool {
149 let stats = self.stats.lock();
150 if stats.max_pages == 0 {
151 return true; }
153 stats.allocated_pages + num_pages <= stats.max_pages
154 }
155
156 fn inc_allocated_pages(&self, num: usize) {
158 let mut stats = self.stats.lock();
159 stats.allocated_pages += num;
160 }
161
162 fn dec_allocated_pages(&self, num: usize) {
164 let mut stats = self.stats.lock();
165 stats.allocated_pages = stats.allocated_pages.saturating_sub(num);
166 }
167
168 fn update_atime(&self) {
170 let mut meta = self.metadata.lock();
171 meta.atime = TimeSpec::now();
172 }
173
174 fn update_mtime(&self) {
176 let mut meta = self.metadata.lock();
177 let now = TimeSpec::now();
178 meta.mtime = now;
179 meta.ctime = now;
180 }
181
182 fn reserve_page(&self) -> Result<(), FsError> {
183 let mut stats = self.stats.lock();
184 if stats.max_pages != 0 && stats.allocated_pages >= stats.max_pages {
185 return Err(FsError::NoSpace);
186 }
187 stats.allocated_pages += 1;
188 Ok(())
189 }
190
191 fn cancel_page_reservation(&self) {
192 self.dec_allocated_pages(1);
193 }
194}
195
196impl Inode for TmpfsInode {
197 fn metadata(&self) -> Result<InodeMetadata, FsError> {
198 Ok(self.metadata.lock().clone())
199 }
200
201 fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize, FsError> {
202 let meta = self.metadata.lock();
203
204 if meta.inode_type != InodeType::File {
205 return Err(FsError::IsDirectory);
206 }
207
208 if offset >= meta.size {
209 return Ok(0);
210 }
211
212 let read_size = buf.len().min(meta.size - offset);
213 drop(meta);
214
215 let mut bytes_read = 0;
216 let data = self.data.lock();
217
218 while bytes_read < read_size {
219 let page_index = (offset + bytes_read) / PAGE_SIZE;
220 let page_offset = (offset + bytes_read) % PAGE_SIZE;
221 let read_len = (PAGE_SIZE - page_offset).min(read_size - bytes_read);
222
223 if page_index >= data.len() || data[page_index].is_none() {
225 buf[bytes_read..bytes_read + read_len].fill(0);
226 } else {
227 let frame = data[page_index].as_ref().unwrap();
229 let kernel_vaddr = frame.ppn().start_addr().to_vaddr();
230
231 unsafe {
232 core::ptr::copy_nonoverlapping(
233 (kernel_vaddr.as_usize() + page_offset) as *const u8,
234 buf[bytes_read..].as_mut_ptr(),
235 read_len,
236 );
237 }
238 }
239
240 bytes_read += read_len;
241 }
242
243 self.update_atime();
244 Ok(bytes_read)
245 }
246
247 fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize, FsError> {
248 let meta = self.metadata.lock();
249
250 if meta.inode_type != InodeType::File {
251 return Err(FsError::IsDirectory);
252 }
253
254 drop(meta);
255
256 let mut data = self.data.lock();
257 let mut bytes_written = 0;
258
259 while bytes_written < buf.len() {
260 let page_index = (offset + bytes_written) / PAGE_SIZE;
261 let page_offset = (offset + bytes_written) % PAGE_SIZE;
262 let write_len = (PAGE_SIZE - page_offset).min(buf.len() - bytes_written);
263
264 if page_index >= data.len() {
266 data.resize(page_index + 1, None);
267 }
268
269 if data[page_index].is_none() {
271 if self.reserve_page().is_err() {
272 return Err(FsError::NoSpace);
273 }
274
275 match alloc_frame() {
276 Some(frame) => {
277 data[page_index] = Some(Arc::new(frame));
278 }
279 None => {
280 self.cancel_page_reservation();
282 return Err(FsError::NoSpace);
283 }
284 }
285 }
286
287 let frame = data[page_index].as_ref().unwrap();
289 let kernel_vaddr = frame.ppn().start_addr().to_vaddr();
290
291 unsafe {
292 core::ptr::copy_nonoverlapping(
293 buf[bytes_written..].as_ptr(),
294 (kernel_vaddr.as_usize() + page_offset) as *mut u8,
295 write_len,
296 );
297 }
298
299 bytes_written += write_len;
300 }
301
302 drop(data);
303
304 let mut meta = self.metadata.lock();
306 meta.size = meta.size.max(offset + bytes_written);
307 meta.blocks = (meta.size + 511) / 512; drop(meta);
309
310 self.update_mtime();
311 Ok(bytes_written)
312 }
313
314 fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>, FsError> {
315 let meta = self.metadata.lock();
316 if meta.inode_type != InodeType::Directory {
317 return Err(FsError::NotDirectory);
318 }
319 drop(meta);
320
321 let children = self.children.lock();
322
323 if name == "." {
325 let self_weak = self.self_ref.lock();
327 if let Some(self_arc) = self_weak.upgrade() {
328 return Ok(self_arc as Arc<dyn Inode>);
329 }
330 return Err(FsError::IoError); } else if name == ".." {
332 let parent = self.parent.lock();
333 if let Some(parent_arc) = parent.upgrade() {
334 return Ok(parent_arc as Arc<dyn Inode>);
335 }
336 let self_weak = self.self_ref.lock();
338 if let Some(self_arc) = self_weak.upgrade() {
339 return Ok(self_arc as Arc<dyn Inode>);
340 }
341 return Err(FsError::IoError); }
343
344 children
345 .get(name)
346 .cloned()
347 .map(|inode| inode as Arc<dyn Inode>)
348 .ok_or(FsError::NotFound)
349 }
350
351 fn create(&self, name: &str, mode: FileMode) -> Result<Arc<dyn Inode>, FsError> {
352 let meta = self.metadata.lock();
353 if meta.inode_type != InodeType::Directory {
354 return Err(FsError::NotDirectory);
355 }
356 drop(meta);
357
358 let mut children = self.children.lock();
359
360 if children.contains_key(name) {
362 return Err(FsError::AlreadyExists);
363 }
364
365 let inode_no = self.alloc_inode_no();
367
368 let parent_weak = self.self_ref.lock().clone();
370
371 let new_inode = TmpfsInode::new(
372 inode_no,
373 InodeType::File,
374 mode,
375 parent_weak,
376 self.stats.clone(),
377 );
378
379 *new_inode.self_ref.lock() = Arc::downgrade(&new_inode);
381
382 children.insert(String::from(name), new_inode.clone());
383 drop(children);
384
385 self.update_mtime();
386
387 Ok(new_inode as Arc<dyn Inode>)
388 }
389
390 fn mkdir(&self, name: &str, mode: FileMode) -> Result<Arc<dyn Inode>, FsError> {
391 let meta = self.metadata.lock();
392 if meta.inode_type != InodeType::Directory {
393 return Err(FsError::NotDirectory);
394 }
395 drop(meta);
396
397 let mut children = self.children.lock();
398
399 if children.contains_key(name) {
401 return Err(FsError::AlreadyExists);
402 }
403
404 let inode_no = self.alloc_inode_no();
406
407 let parent_weak = self.self_ref.lock().clone();
409
410 let new_inode = TmpfsInode::new(
411 inode_no,
412 InodeType::Directory,
413 mode,
414 parent_weak,
415 self.stats.clone(),
416 );
417
418 *new_inode.self_ref.lock() = Arc::downgrade(&new_inode);
420
421 children.insert(String::from(name), new_inode.clone());
422 drop(children);
423
424 let mut meta = self.metadata.lock();
426 meta.nlinks += 1;
427 drop(meta);
428
429 self.update_mtime();
430
431 Ok(new_inode as Arc<dyn Inode>)
432 }
433
434 fn unlink(&self, name: &str) -> Result<(), FsError> {
435 let meta = self.metadata.lock();
436 if meta.inode_type != InodeType::Directory {
437 return Err(FsError::NotDirectory);
438 }
439 drop(meta);
440
441 let mut children = self.children.lock();
442
443 let child = children.get(name).ok_or(FsError::NotFound)?;
445
446 let child_meta = child.metadata.lock();
448 if child_meta.inode_type == InodeType::Directory {
449 return Err(FsError::IsDirectory);
450 }
451 drop(child_meta);
452
453 let child_data = child.data.lock();
455 let allocated = child_data.iter().filter(|f| f.is_some()).count();
456 drop(child_data);
457
458 children.remove(name);
460 self.dec_allocated_pages(allocated);
461 self.update_mtime();
462
463 Ok(())
464 }
465
466 fn rmdir(&self, name: &str) -> Result<(), FsError> {
467 let meta = self.metadata.lock();
468 if meta.inode_type != InodeType::Directory {
469 return Err(FsError::NotDirectory);
470 }
471 drop(meta);
472
473 let mut children = self.children.lock();
474
475 let child = children.get(name).ok_or(FsError::NotFound)?;
477
478 let child_meta = child.metadata.lock();
480 if child_meta.inode_type != InodeType::Directory {
481 return Err(FsError::NotDirectory);
482 }
483 drop(child_meta);
484
485 let child_children = child.children.lock();
487 if !child_children.is_empty() {
488 return Err(FsError::DirectoryNotEmpty);
489 }
490 drop(child_children);
491
492 children.remove(name);
494
495 let mut meta = self.metadata.lock();
497 meta.nlinks = meta.nlinks.saturating_sub(1);
498 drop(meta);
499
500 self.update_mtime();
501
502 Ok(())
503 }
504
505 fn readdir(&self) -> Result<Vec<DirEntry>, FsError> {
506 let meta = self.metadata.lock();
507 if meta.inode_type != InodeType::Directory {
508 return Err(FsError::NotDirectory);
509 }
510 let inode_no = meta.inode_no;
511 drop(meta);
512
513 let children = self.children.lock();
514 let mut entries = Vec::new();
515
516 entries.push(DirEntry {
518 name: String::from("."),
519 inode_no,
520 inode_type: InodeType::Directory,
521 });
522
523 let parent = self.parent.lock();
524 let parent_inode_no = if let Some(parent_arc) = parent.upgrade() {
525 parent_arc.metadata.lock().inode_no
526 } else {
527 inode_no };
529 drop(parent);
530
531 entries.push(DirEntry {
532 name: String::from(".."),
533 inode_no: parent_inode_no,
534 inode_type: InodeType::Directory,
535 });
536
537 for (name, child) in children.iter() {
539 let child_meta = child.metadata.lock();
540 entries.push(DirEntry {
541 name: name.clone(),
542 inode_no: child_meta.inode_no,
543 inode_type: child_meta.inode_type,
544 });
545 }
546
547 Ok(entries)
548 }
549
550 fn truncate(&self, new_size: usize) -> Result<(), FsError> {
551 let mut meta = self.metadata.lock();
552
553 if meta.inode_type != InodeType::File {
554 return Err(FsError::IsDirectory);
555 }
556
557 let old_size = meta.size;
558
559 if new_size < old_size {
560 let new_page_count = (new_size + PAGE_SIZE - 1) / PAGE_SIZE;
562 let old_page_count = (old_size + PAGE_SIZE - 1) / PAGE_SIZE;
563
564 let mut data = self.data.lock();
565
566 let pages_to_free = data[new_page_count..old_page_count.min(data.len())]
568 .iter()
569 .filter(|f| f.is_some())
570 .count();
571
572 data.truncate(new_page_count);
573 drop(data);
574
575 self.dec_allocated_pages(pages_to_free);
576 }
577
578 meta.size = new_size;
579 meta.blocks = (new_size + 511) / 512;
580 drop(meta);
581
582 self.update_mtime();
583 Ok(())
584 }
585
586 fn sync(&self) -> Result<(), FsError> {
587 Ok(())
589 }
590
591 fn as_any(&self) -> &dyn core::any::Any {
592 self
593 }
594
595 fn symlink(&self, name: &str, target: &str) -> Result<Arc<dyn Inode>, FsError> {
596 let meta = self.metadata.lock();
598 if meta.inode_type != InodeType::Directory {
599 return Err(FsError::NotDirectory);
600 }
601 drop(meta);
602
603 let children = self.children.lock();
605 if children.contains_key(name) {
606 return Err(FsError::AlreadyExists);
607 }
608 drop(children);
609
610 let mut stats = self.stats.lock();
612 let inode_no = stats.next_inode_no;
613 stats.next_inode_no += 1;
614 drop(stats);
615
616 let symlink_inode = TmpfsInode::new(
618 inode_no,
619 InodeType::Symlink,
620 FileMode::from_bits_truncate(0o777),
621 Arc::downgrade(&self.self_ref.lock().upgrade().unwrap()),
622 self.stats.clone(),
623 );
624
625 let target_bytes = target.as_bytes();
627 if let Err(e) = symlink_inode.write_at(0, target_bytes) {
628 return Err(e);
629 }
630
631 self.children
633 .lock()
634 .insert(name.to_string(), symlink_inode.clone());
635
636 self.metadata.lock().mtime = TimeSpec::now();
638
639 Ok(symlink_inode as Arc<dyn Inode>)
640 }
641
642 fn link(&self, _name: &str, _target: &Arc<dyn Inode>) -> Result<(), FsError> {
643 Err(FsError::NotSupported)
645 }
646
647 fn rename(
648 &self,
649 _old_name: &str,
650 _new_parent: Arc<dyn Inode>,
651 _new_name: &str,
652 ) -> Result<(), FsError> {
653 Err(FsError::NotSupported)
655 }
656
657 fn set_times(&self, atime: Option<TimeSpec>, mtime: Option<TimeSpec>) -> Result<(), FsError> {
658 let mut metadata = self.metadata.lock();
659 if let Some(atime) = atime {
660 metadata.atime = atime;
661 }
662 if let Some(mtime) = mtime {
663 metadata.mtime = mtime;
664 }
665 Ok(())
666 }
667
668 fn readlink(&self) -> Result<String, FsError> {
669 let meta = self.metadata.lock();
671 if meta.inode_type != InodeType::Symlink {
672 return Err(FsError::InvalidArgument);
673 }
674 let size = meta.size;
675 drop(meta);
676
677 if size == 0 {
679 return Ok(String::new());
680 }
681
682 let mut buf = alloc::vec![0u8; size];
683 let bytes_read = self.read_at(0, &mut buf)?;
684
685 String::from_utf8(buf[..bytes_read].to_vec()).map_err(|_| FsError::InvalidArgument)
687 }
688
689 fn mknod(&self, name: &str, mode: FileMode, dev: u64) -> Result<Arc<dyn Inode>, FsError> {
690 let meta = self.metadata.lock();
692 if meta.inode_type != InodeType::Directory {
693 return Err(FsError::NotDirectory);
694 }
695 drop(meta);
696
697 let mut children = self.children.lock();
699 if children.contains_key(name) {
700 return Err(FsError::AlreadyExists);
701 }
702
703 let inode_type = if mode.contains(FileMode::S_IFCHR) {
705 InodeType::CharDevice
706 } else if mode.contains(FileMode::S_IFBLK) {
707 InodeType::BlockDevice
708 } else if mode.contains(FileMode::S_IFIFO) {
709 InodeType::Fifo
710 } else {
711 return Err(FsError::InvalidArgument);
713 };
714
715 let inode_no = self.alloc_inode_no();
717
718 let parent_weak = self.self_ref.lock().clone();
720
721 let new_inode =
723 TmpfsInode::new(inode_no, inode_type, mode, parent_weak, self.stats.clone());
724
725 new_inode.metadata.lock().rdev = dev;
727 *new_inode.self_ref.lock() = Arc::downgrade(&new_inode);
728
729 children.insert(String::from(name), new_inode.clone());
731 drop(children);
732
733 self.update_mtime();
734
735 Ok(new_inode as Arc<dyn Inode>)
736 }
737
738 fn chmod(&self, _mode: FileMode) -> Result<(), FsError> {
739 Err(FsError::NotSupported)
740 }
741
742 fn chown(&self, _uid: u32, _gid: u32) -> Result<(), FsError> {
743 Err(FsError::NotSupported)
744 }
745}