1use alloc::{
8 boxed::Box,
9 collections::BTreeMap,
10 format,
11 string::{String, ToString},
12 sync::{Arc, Weak},
13 vec,
14 vec::Vec,
15};
16use core::{any::Any, fmt::Debug};
17use spin::{Mutex, rwlock::RwLock};
18
19use crate::device::manager::DeviceManager;
20use crate::environment::PAGE_SIZE;
21use crate::network::NetworkManager;
22use crate::object::capability::{ControlOps, StreamError, StreamOps};
23use crate::{
24 device::{Device, DeviceType},
25 driver_initcall,
26 fs::{
27 DeviceFileInfo, FileMetadata, FileObject, FilePermission, FileSystemDriver,
28 FileSystemError, FileSystemErrorKind, FileType, SocketFileInfo, get_fs_driver_manager,
29 vfs_v2::cache::PageCacheCapable,
30 },
31 mem::{page::allocate_boxed_pages, page_cache::PageCacheManager},
32 object::capability::MemoryMappingOps,
33};
34
35use super::super::core::{DirectoryEntryInternal, FileSystemId, FileSystemOperations, VfsNode};
36
37pub struct TmpFS {
44 fs_id: FileSystemId,
46 root: RwLock<Arc<TmpNode>>,
48 memory_limit: usize,
50 current_memory: Mutex<usize>,
52 next_file_id: Mutex<u64>,
54 name: String,
56}
57
58impl TmpFS {
59 pub fn new(memory_limit: usize) -> Arc<Self> {
61 let root = Arc::new(TmpNode::new_directory("/".to_string(), 1));
62 let fs = Arc::new(Self {
63 fs_id: FileSystemId::new(),
64 root: RwLock::new(Arc::clone(&root)),
65 memory_limit,
66 current_memory: Mutex::new(0),
67 next_file_id: Mutex::new(2), name: "tmpfs_v2".to_string(),
69 });
70 let fs_weak = Arc::downgrade(&(fs.clone() as Arc<dyn FileSystemOperations>));
71 root.set_filesystem(fs_weak);
72 debug_assert!(
73 root.filesystem().is_some(),
74 "TmpFS root node's filesystem() is None after set_filesystem"
75 );
76 fs
77 }
78
79 pub fn create_from_option_string(option: Option<&str>) -> Arc<dyn FileSystemOperations> {
82 let mut memory_limit = 0;
83 if let Some(opt) = option {
84 for part in opt.split(',') {
85 let part = part.trim();
86 if let Some(mem_str) = part.strip_prefix("mem=") {
87 if let Ok(val) = mem_str.parse::<usize>() {
88 memory_limit = val;
89 }
90 }
91 }
92 }
93 TmpFS::new(memory_limit) as Arc<dyn FileSystemOperations>
94 }
95
96 fn generate_file_id(&self) -> u64 {
98 let mut next_id = self.next_file_id.lock();
99 let id = *next_id;
100 *next_id += 1;
101 id
102 }
103
104 fn check_memory_limit(&self, additional_bytes: usize) -> Result<(), FileSystemError> {
106 if self.memory_limit == 0 {
107 return Ok(()); }
109
110 let current = *self.current_memory.lock();
111 if current + additional_bytes > self.memory_limit {
112 return Err(FileSystemError::new(
113 FileSystemErrorKind::NoSpace,
114 "TmpFS memory limit exceeded",
115 ));
116 }
117
118 Ok(())
119 }
120
121 fn add_memory_usage(&self, bytes: usize) {
123 if self.memory_limit > 0 {
124 *self.current_memory.lock() += bytes;
125 }
126 }
127
128 fn subtract_memory_usage(&self, bytes: usize) {
130 if self.memory_limit > 0 {
131 let mut current = self.current_memory.lock();
132 *current = current.saturating_sub(bytes);
133 }
134 }
135}
136
137impl FileSystemOperations for TmpFS {
138 fn fs_id(&self) -> FileSystemId {
139 self.fs_id
140 }
141
142 fn lookup(
143 &self,
144 parent_node: &Arc<dyn VfsNode>,
145 name: &String,
146 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
147 let tmp_node = parent_node
149 .as_any()
150 .downcast_ref::<TmpNode>()
151 .ok_or_else(|| {
152 FileSystemError::new(
153 FileSystemErrorKind::NotSupported,
154 "Invalid node type for TmpFS",
155 )
156 })?;
157
158 if tmp_node.file_type() != FileType::Directory {
160 return Err(FileSystemError::new(
161 FileSystemErrorKind::NotADirectory,
162 "Parent is not a directory",
163 ));
164 }
165
166 match name.as_str() {
168 "." => {
169 return Ok(Arc::clone(&parent_node));
171 }
172 ".." => {
173 if let Some(parent_weak) = &tmp_node.parent() {
175 if let Some(parent) = parent_weak.upgrade() {
176 return Ok(parent as Arc<dyn VfsNode>);
179 }
180 }
181 }
182 _ => {
183 }
185 }
186
187 let children = tmp_node.children.read();
189 if let Some(child_node) = children.get(name) {
190 Ok(Arc::clone(child_node) as Arc<dyn VfsNode>)
191 } else {
192 Err(FileSystemError::new(
193 FileSystemErrorKind::NotFound,
194 "File not found",
195 ))
196 }
197 }
198
199 fn open(
200 &self,
201 node: &Arc<dyn VfsNode>,
202 _flags: u32,
203 ) -> Result<Arc<dyn FileObject>, FileSystemError> {
204 let tmp_node = Arc::downcast::<TmpNode>(node.clone()).map_err(|_| {
205 FileSystemError::new(
206 FileSystemErrorKind::NotSupported,
207 "Invalid node type for TmpFS",
208 )
209 })?;
210
211 let file_object = match tmp_node.file_type() {
212 FileType::RegularFile => TmpFileObject::new_regular(tmp_node),
213 FileType::Directory => TmpFileObject::new_directory(tmp_node),
214 FileType::CharDevice(info) | FileType::BlockDevice(info) => {
215 TmpFileObject::new_device(tmp_node, info)
216 }
217 FileType::Socket(info) => TmpFileObject::new_socket(tmp_node, info),
218 _ => {
219 return Err(FileSystemError::new(
220 FileSystemErrorKind::NotSupported,
221 "Unsupported file type for open",
222 ));
223 }
224 };
225
226 Ok(Arc::new(file_object))
227 }
228
229 fn create(
230 &self,
231 parent_node: &Arc<dyn VfsNode>,
232 name: &String,
233 file_type: FileType,
234 _mode: u32,
235 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
236 let tmp_parent = Arc::downcast::<TmpNode>(parent_node.clone()).map_err(|_| {
237 FileSystemError::new(
238 FileSystemErrorKind::NotSupported,
239 "Invalid node type for TmpFS",
240 )
241 })?;
242
243 if tmp_parent.file_type() != FileType::Directory {
245 return Err(FileSystemError::new(
246 FileSystemErrorKind::NotADirectory,
247 "Parent is not a directory",
248 ));
249 }
250 {
252 let children = tmp_parent.children.read();
253 if children.contains_key(name) {
254 return Err(FileSystemError::new(
255 FileSystemErrorKind::AlreadyExists,
256 "File already exists",
257 ));
258 }
259 }
260 let file_id = self.generate_file_id();
262 let new_node = match file_type {
263 FileType::RegularFile => Arc::new(TmpNode::new_file(name.clone().to_string(), file_id)),
264 FileType::Directory => {
265 Arc::new(TmpNode::new_directory(name.clone().to_string(), file_id))
266 }
267 FileType::SymbolicLink(target_path) => {
268 self.add_memory_usage(target_path.len());
270 Arc::new(TmpNode::new_symlink(
271 name.clone().to_string(),
272 target_path,
273 file_id,
274 ))
275 }
276 FileType::CharDevice(_) | FileType::BlockDevice(_) | FileType::Socket(_) => Arc::new(
277 TmpNode::new_device(name.clone().to_string(), file_type, file_id),
278 ),
279 _ => {
280 return Err(FileSystemError::new(
281 FileSystemErrorKind::NotSupported,
282 "Unsupported file type for creation",
283 ));
284 }
285 };
286 let fs_ref = parent_node.filesystem().ok_or_else(|| {
288 FileSystemError::new(
289 FileSystemErrorKind::NotSupported,
290 "Parent node does not have a filesystem reference",
291 )
292 })?;
293 if fs_ref.upgrade().is_none() {
294 return Err(FileSystemError::new(
295 FileSystemErrorKind::NotSupported,
296 "Parent node's filesystem reference is dead (cannot upgrade)",
297 ));
298 }
299 if let Some(tmp_node) = new_node.as_any().downcast_ref::<TmpNode>() {
300 tmp_node.set_filesystem(fs_ref);
301 }
302 {
304 let mut children = tmp_parent.children.write();
305 new_node.set_parent(Arc::downgrade(&tmp_parent));
306 children.insert(name.clone(), Arc::clone(&new_node) as Arc<dyn VfsNode>);
307 }
308
309 Ok(new_node)
310 }
311
312 fn create_hardlink(
319 &self,
320 link_parent: &Arc<dyn VfsNode>,
321 link_name: &String,
322 target_node: &Arc<dyn VfsNode>,
323 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
324 let tmp_parent = link_parent
326 .as_any()
327 .downcast_ref::<TmpNode>()
328 .ok_or_else(|| {
329 FileSystemError::new(
330 FileSystemErrorKind::NotSupported,
331 "Invalid parent node type for TmpFS",
332 )
333 })?;
334
335 let tmp_target = target_node
336 .as_any()
337 .downcast_ref::<TmpNode>()
338 .ok_or_else(|| {
339 FileSystemError::new(
340 FileSystemErrorKind::NotSupported,
341 "Invalid target node type for TmpFS",
342 )
343 })?;
344
345 if tmp_parent.file_type() != FileType::Directory {
347 return Err(FileSystemError::new(
348 FileSystemErrorKind::NotADirectory,
349 "Parent is not a directory",
350 ));
351 }
352
353 if tmp_target.file_type() != FileType::RegularFile {
355 return Err(FileSystemError::new(
356 FileSystemErrorKind::InvalidOperation,
357 "Cannot create hard link to non-regular file",
358 ));
359 }
360
361 {
363 let children = tmp_parent.children.read();
364 if children.contains_key(link_name) {
365 return Err(FileSystemError::new(
366 FileSystemErrorKind::FileExists,
367 "Link name already exists",
368 ));
369 }
370 }
371
372 {
375 let mut metadata = tmp_target.metadata.write();
376 metadata.link_count += 1;
377 }
378
379 {
381 let mut children = tmp_parent.children.write();
382 children.insert(link_name.clone(), Arc::clone(target_node));
383 }
384
385 Ok(Arc::clone(target_node))
387 }
388
389 fn remove(&self, parent_node: &Arc<dyn VfsNode>, name: &String) -> Result<(), FileSystemError> {
390 let tmp_parent = parent_node
391 .as_any()
392 .downcast_ref::<TmpNode>()
393 .ok_or_else(|| {
394 FileSystemError::new(
395 FileSystemErrorKind::NotSupported,
396 "Invalid node type for TmpFS",
397 )
398 })?;
399
400 if tmp_parent.file_type() != FileType::Directory {
402 return Err(FileSystemError::new(
403 FileSystemErrorKind::NotADirectory,
404 "Parent is not a directory",
405 ));
406 }
407
408 let mut children = tmp_parent.children.write();
410 if let Some(removed_node) = children.get(name) {
411 if let Some(tmp_node) = removed_node.as_any().downcast_ref::<TmpNode>() {
413 if tmp_node.file_type() == FileType::Directory {
414 let child_children = tmp_node.children.read();
415 if !child_children.is_empty() {
416 return Err(FileSystemError::new(
417 FileSystemErrorKind::DirectoryNotEmpty,
418 "Directory not empty",
419 ));
420 }
421 }
422
423 match tmp_node.file_type() {
425 FileType::RegularFile => {
426 let size = tmp_node.metadata.read().size;
427 self.subtract_memory_usage(size);
428 let fs_id = self.fs_id().get();
429 let file_id = tmp_node.metadata.read().file_id;
430 let cache_key = (fs_id << 32) | (file_id & 0xFFFF_FFFF);
431 PageCacheManager::global()
432 .invalidate(crate::fs::vfs_v2::cache::CacheId::new(cache_key));
433 }
434 FileType::SymbolicLink(target) => {
435 self.subtract_memory_usage(target.len());
436 }
437 _ => {}
438 }
439 }
440 }
441
442 children
444 .remove(name)
445 .ok_or_else(|| FileSystemError::new(FileSystemErrorKind::NotFound, "File not found"))?;
446
447 Ok(())
448 }
449
450 fn readdir(
451 &self,
452 node: &Arc<dyn VfsNode>,
453 ) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
454 let tmp_node = node.as_any().downcast_ref::<TmpNode>().ok_or_else(|| {
455 FileSystemError::new(
456 FileSystemErrorKind::NotSupported,
457 "Invalid node type for TmpFS",
458 )
459 })?;
460
461 if tmp_node.file_type() != FileType::Directory {
463 return Err(FileSystemError::new(
464 FileSystemErrorKind::NotADirectory,
465 "Not a directory",
466 ));
467 }
468
469 let mut entries = Vec::new();
470 let children = tmp_node.children.read();
471
472 for (name, child_node) in children.iter() {
473 if let Some(child_tmp_node) = child_node.as_any().downcast_ref::<TmpNode>() {
474 let metadata = child_tmp_node.metadata.read();
475 entries.push(DirectoryEntryInternal {
476 name: name.clone(),
477 file_type: child_tmp_node.file_type.read().clone(),
478 file_id: metadata.file_id,
479 });
480 }
481 }
482
483 Ok(entries)
484 }
485
486 fn root_node(&self) -> Arc<dyn VfsNode> {
487 Arc::clone(&*self.root.read()) as Arc<dyn VfsNode>
488 }
489
490 fn name(&self) -> &str {
491 &self.name
492 }
493
494 fn as_any(&self) -> &dyn Any {
495 self
496 }
497}
498
499pub struct TmpNode {
504 name: RwLock<String>,
506 file_type: RwLock<FileType>,
508 metadata: RwLock<FileMetadata>,
510 content: RwLock<Vec<u8>>,
512 children: RwLock<BTreeMap<String, Arc<dyn VfsNode>>>,
514 parent: RwLock<Option<Weak<TmpNode>>>,
516 filesystem: RwLock<Option<Weak<dyn FileSystemOperations>>>,
518}
519
520impl Debug for TmpNode {
521 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
522 f.debug_struct("TmpNode")
523 .field("name", &self.name.read())
524 .field("file_type", &self.file_type.read())
525 .field("metadata", &self.metadata.read())
526 .field(
527 "parent",
528 &self.parent.read().as_ref().map(|p| p.strong_count()),
529 )
530 .finish()
531 }
532}
533
534impl TmpNode {
535 pub fn new_file(name: String, file_id: u64) -> Self {
537 Self {
538 name: RwLock::new(name),
539 file_type: RwLock::new(FileType::RegularFile),
540 metadata: RwLock::new(FileMetadata {
541 file_type: FileType::RegularFile,
542 size: 0,
543 permissions: FilePermission {
544 read: true,
545 write: true,
546 execute: false,
547 },
548 created_time: 0, modified_time: 0,
550 accessed_time: 0,
551 file_id,
552 link_count: 1,
553 }),
554 content: RwLock::new(Vec::new()),
555 children: RwLock::new(BTreeMap::new()),
556 parent: RwLock::new(None), filesystem: RwLock::new(None),
558 }
559 }
560
561 pub fn new_directory(name: String, file_id: u64) -> Self {
563 Self {
564 name: RwLock::new(name),
565 file_type: RwLock::new(FileType::Directory),
566 metadata: RwLock::new(FileMetadata {
567 file_type: FileType::Directory,
568 size: 0,
569 permissions: FilePermission {
570 read: true,
571 write: true,
572 execute: true,
573 },
574 created_time: 0,
575 modified_time: 0,
576 accessed_time: 0,
577 file_id,
578 link_count: 1,
579 }),
580 content: RwLock::new(Vec::new()),
581 children: RwLock::new(BTreeMap::new()),
582 parent: RwLock::new(None), filesystem: RwLock::new(None),
584 }
585 }
586
587 pub fn new_device(name: String, file_type: FileType, file_id: u64) -> Self {
589 Self {
590 name: RwLock::new(name),
591 file_type: RwLock::new(file_type.clone()),
592 metadata: RwLock::new(FileMetadata {
593 file_type,
594 size: 0,
595 permissions: FilePermission {
596 read: true,
597 write: true,
598 execute: false,
599 },
600 created_time: 0,
601 modified_time: 0,
602 accessed_time: 0,
603 file_id,
604 link_count: 1,
605 }),
606 content: RwLock::new(Vec::new()),
607 children: RwLock::new(BTreeMap::new()),
608 parent: RwLock::new(None), filesystem: RwLock::new(None),
610 }
611 }
612
613 pub fn new_symlink(name: String, target: String, file_id: u64) -> Self {
615 Self {
616 name: RwLock::new(name),
617 file_type: RwLock::new(FileType::SymbolicLink(target.clone())),
618 metadata: RwLock::new(FileMetadata {
619 file_type: FileType::SymbolicLink(target.clone()),
620 size: target.len(),
621 permissions: FilePermission {
622 read: true,
623 write: true,
624 execute: false,
625 },
626 created_time: 0, modified_time: 0,
628 accessed_time: 0,
629 file_id,
630 link_count: 1,
631 }),
632 content: RwLock::new(target.into_bytes()),
634 children: RwLock::new(BTreeMap::new()),
635 parent: RwLock::new(None), filesystem: RwLock::new(None),
637 }
638 }
639
640 pub fn set_filesystem(&self, fs: Weak<dyn FileSystemOperations>) {
642 *self.filesystem.write() = Some(fs);
643 }
644
645 pub fn update_size(&self, new_size: u64) {
647 let mut metadata = self.metadata.write();
648 metadata.size = new_size as usize;
649 metadata.modified_time = 0; }
651
652 pub fn set_parent(&self, parent: Weak<TmpNode>) {
654 self.parent.write().replace(parent);
655 }
656
657 pub fn is_filesystem_root(&self) -> bool {
659 self.parent.read().is_none()
660 }
661
662 pub fn name(&self) -> String {
664 self.name.read().clone()
665 }
666
667 pub fn file_type(&self) -> FileType {
669 self.file_type.read().clone()
670 }
671
672 pub fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>> {
674 self.filesystem.read().clone()
675 }
676
677 pub fn parent(&self) -> Option<Weak<TmpNode>> {
679 self.parent.read().clone()
680 }
681}
682
683impl VfsNode for TmpNode {
684 fn id(&self) -> u64 {
685 self.metadata.read().file_id
686 }
687
688 fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>> {
689 self.filesystem.read().clone()
690 }
691
692 fn metadata(&self) -> Result<FileMetadata, FileSystemError> {
693 Ok(self.metadata.read().clone())
694 }
695
696 fn as_any(&self) -> &dyn Any {
697 self
698 }
699
700 fn read_link(&self) -> Result<String, FileSystemError> {
701 match &self.file_type() {
703 FileType::SymbolicLink(target) => Ok(target.clone()),
704 _ => Err(FileSystemError::new(
705 FileSystemErrorKind::NotSupported,
706 "Not a symbolic link",
707 )),
708 }
709 }
710}
711
712pub struct TmpFileObject {
719 node: Arc<TmpNode>,
721
722 position: RwLock<u64>,
724
725 device_guard: Option<Arc<dyn Device>>,
727
728 socket_ref: Option<Arc<dyn crate::network::SocketObject>>,
730 mmap_backing: RwLock<Option<Box<[crate::mem::page::Page]>>>,
732 mmap_backing_len: Mutex<usize>,
734 mmap_ranges: RwLock<BTreeMap<usize, MmapRange>>,
736}
737
738impl TmpFileObject {
739 pub fn new_regular(node: Arc<TmpNode>) -> Self {
741 Self {
742 node,
743 position: RwLock::new(0),
744 device_guard: None,
745 socket_ref: None,
746 mmap_backing: RwLock::new(None),
747 mmap_backing_len: Mutex::new(0),
748 mmap_ranges: RwLock::new(BTreeMap::new()),
749 }
750 }
751
752 pub fn new_directory(node: Arc<TmpNode>) -> Self {
754 Self {
755 node,
756 position: RwLock::new(0),
757 device_guard: None,
758 socket_ref: None,
759 mmap_backing: RwLock::new(None),
760 mmap_backing_len: Mutex::new(0),
761 mmap_ranges: RwLock::new(BTreeMap::new()),
762 }
763 }
764
765 pub fn new_device(node: Arc<TmpNode>, info: DeviceFileInfo) -> Self {
767 match DeviceManager::get_manager().get_device(info.device_id) {
769 Some(device_guard) => Self {
770 node,
771 position: RwLock::new(0),
772 device_guard: Some(device_guard),
773 socket_ref: None,
774 mmap_backing: RwLock::new(None),
775 mmap_backing_len: Mutex::new(0),
776 mmap_ranges: RwLock::new(BTreeMap::new()),
777 },
778 None => {
779 panic!("Failed to borrow device {}", info.device_id);
781 }
782 }
783 }
784
785 pub fn new_socket(node: Arc<TmpNode>, info: SocketFileInfo) -> Self {
787 match NetworkManager::get_manager().get_socket(info.socket_id) {
789 Some(socket) => Self {
790 node,
791 position: RwLock::new(0),
792 device_guard: None,
793 socket_ref: Some(socket),
794 mmap_backing: RwLock::new(None),
795 mmap_backing_len: Mutex::new(0),
796 mmap_ranges: RwLock::new(BTreeMap::new()),
797 },
798 None => {
799 Self {
805 node,
806 position: RwLock::new(0),
807 device_guard: None,
808 socket_ref: None,
809 mmap_backing: RwLock::new(None),
810 mmap_backing_len: Mutex::new(0),
811 mmap_ranges: RwLock::new(BTreeMap::new()),
812 }
813 }
814 }
815 }
816
817 fn ensure_mmap_backing(
818 &self,
819 file_size: usize,
820 required_size: usize,
821 ) -> Result<(), StreamError> {
822 if file_size == 0 || required_size == 0 {
823 return Err(StreamError::InvalidArgument);
824 }
825
826 let num_pages = (required_size + PAGE_SIZE - 1) / PAGE_SIZE;
827 let mut backing_guard = self.mmap_backing.write();
828 let needs_alloc = backing_guard
829 .as_ref()
830 .map(|buf| buf.len() < num_pages)
831 .unwrap_or(true);
832 if needs_alloc {
833 *backing_guard = Some(allocate_boxed_pages(num_pages));
834 }
835
836 let backing = backing_guard.as_mut().expect("mmap backing missing");
837 *self.mmap_backing_len.lock() = file_size;
838
839 let cache_id = self.cache_id();
840 let backing_ptr = backing.as_mut_ptr() as *mut u8;
841 for page_index in 0..num_pages {
842 let pinned = PageCacheManager::global()
843 .pin_or_load(cache_id, page_index as u64, |paddr| {
844 unsafe {
845 core::ptr::write_bytes(paddr as *mut u8, 0, PAGE_SIZE);
846 }
847 Ok(())
848 })
849 .map_err(|_| StreamError::IoError)?;
850 unsafe {
851 core::ptr::copy_nonoverlapping(
852 pinned.paddr() as *const u8,
853 backing_ptr.add(page_index * PAGE_SIZE),
854 PAGE_SIZE,
855 );
856 }
857 }
858
859 Ok(())
860 }
861
862 fn read_device(&self, buffer: &mut [u8]) -> Result<usize, FileSystemError> {
863 if let Some(ref device_guard) = self.device_guard {
864 let device_guard_ref = device_guard.as_ref();
865
866 match device_guard_ref.device_type() {
867 DeviceType::Char => {
868 if let Some(char_device) = device_guard_ref.as_char_device() {
869 let mut bytes_read = 0;
870 for byte in buffer.iter_mut() {
871 match char_device.read_byte() {
872 Some(b) => {
873 *byte = b;
874 bytes_read += 1;
875 }
876 None => break,
877 }
878 }
879 return Ok(bytes_read);
880 } else {
881 return Err(FileSystemError {
882 kind: FileSystemErrorKind::NotSupported,
883 message: "Device is not a character device".to_string(),
884 });
885 }
886 }
887 DeviceType::Block => {
888 if let Some(block_device) = device_guard_ref.as_block_device() {
889 let request = Box::new(crate::device::block::request::BlockIORequest {
891 request_type: crate::device::block::request::BlockIORequestType::Read,
892 sector: 0,
893 sector_count: 1,
894 head: 0,
895 cylinder: 0,
896 buffer: buffer.to_vec(),
897 });
898
899 block_device.enqueue_request(request);
900 let results = block_device.process_requests();
901
902 if let Some(result) = results.first() {
903 match &result.result {
904 Ok(_) => return Ok(buffer.len()),
905 Err(e) => {
906 return Err(FileSystemError {
907 kind: FileSystemErrorKind::IoError,
908 message: format!("Block device read failed: {}", e),
909 });
910 }
911 }
912 }
913 return Ok(0);
914 } else {
915 return Err(FileSystemError {
916 kind: FileSystemErrorKind::NotSupported,
917 message: "Device is not a block device".to_string(),
918 });
919 }
920 }
921 _ => {
922 return Err(FileSystemError {
923 kind: FileSystemErrorKind::NotSupported,
924 message: "Unsupported device type".to_string(),
925 });
926 }
927 }
928 }
929
930 Err(FileSystemError {
931 kind: FileSystemErrorKind::NotSupported,
932 message: "No device guard available".to_string(),
933 })
934 }
935
936 fn read_regular_file(&self, buffer: &mut [u8]) -> Result<usize, FileSystemError> {
937 let mut pos = *self.position.read();
938 let file_size = self.node.metadata.read().size as u64;
939 if pos >= file_size {
940 return Ok(0);
941 }
942
943 let mut total_read = 0usize;
944 let cache_id = self.cache_id();
945 while total_read < buffer.len() && pos < file_size {
946 let page_index = (pos as usize / PAGE_SIZE) as u64;
947 let offset_in_page = (pos as usize) % PAGE_SIZE;
948 let pinned = PageCacheManager::global()
949 .pin_or_load(cache_id, page_index, |paddr| {
950 unsafe {
951 core::ptr::write_bytes(paddr as *mut u8, 0, PAGE_SIZE);
952 }
953 Ok(())
954 })
955 .map_err(|_| {
956 FileSystemError::new(FileSystemErrorKind::IoError, "tmpfs page load error")
957 })?;
958
959 unsafe {
960 let src = (pinned.paddr() as *const u8).add(offset_in_page);
961 let remaining_in_page = PAGE_SIZE - offset_in_page;
962 let remaining_file = (file_size - pos) as usize;
963 let remaining_buf = buffer.len() - total_read;
964 let chunk = core::cmp::min(
965 remaining_in_page,
966 core::cmp::min(remaining_file, remaining_buf),
967 );
968 core::ptr::copy_nonoverlapping(src, buffer.as_mut_ptr().add(total_read), chunk);
969 total_read += chunk;
970 pos += chunk as u64;
971 }
972 }
973
974 *self.position.write() = pos;
975 Ok(total_read)
976 }
977
978 fn write_device(&self, buffer: &[u8]) -> Result<usize, FileSystemError> {
979 if let Some(ref device_guard) = self.device_guard {
980 let device_guard_ref = device_guard.as_ref();
981
982 match device_guard_ref.device_type() {
983 DeviceType::Char => {
984 if let Some(char_device) = device_guard_ref.as_char_device() {
985 let mut bytes_written = 0;
986 for &byte in buffer {
987 match char_device.write_byte(byte) {
988 Ok(_) => bytes_written += 1,
989 Err(_) => break,
990 }
991 }
992 return Ok(bytes_written);
993 } else {
994 return Err(FileSystemError {
995 kind: FileSystemErrorKind::NotSupported,
996 message: "Device is not a character device".to_string(),
997 });
998 }
999 }
1000 DeviceType::Block => {
1001 if let Some(block_device) = device_guard_ref.as_block_device() {
1002 let request = Box::new(crate::device::block::request::BlockIORequest {
1003 request_type: crate::device::block::request::BlockIORequestType::Write,
1004 sector: 0,
1005 sector_count: 1,
1006 head: 0,
1007 cylinder: 0,
1008 buffer: buffer.to_vec(),
1009 });
1010
1011 block_device.enqueue_request(request);
1012 let results = block_device.process_requests();
1013
1014 if let Some(result) = results.first() {
1015 match &result.result {
1016 Ok(_) => return Ok(buffer.len()),
1017 Err(e) => {
1018 return Err(FileSystemError {
1019 kind: FileSystemErrorKind::IoError,
1020 message: format!("Block device write failed: {}", e),
1021 });
1022 }
1023 }
1024 }
1025 return Ok(0);
1026 } else {
1027 return Err(FileSystemError {
1028 kind: FileSystemErrorKind::NotSupported,
1029 message: "Device is not a block device".to_string(),
1030 });
1031 }
1032 }
1033 _ => {
1034 return Err(FileSystemError {
1035 kind: FileSystemErrorKind::NotSupported,
1036 message: "Unsupported device type".to_string(),
1037 });
1038 }
1039 }
1040 } else {
1041 Err(FileSystemError {
1042 kind: FileSystemErrorKind::NotSupported,
1043 message: "No device guard available".to_string(),
1044 })
1045 }
1046 }
1047
1048 fn write_regular_file(&self, buffer: &[u8]) -> Result<usize, FileSystemError> {
1049 let mut pos = *self.position.read() as usize;
1050 let mut written = 0usize;
1051 let cache_id = self.cache_id();
1052
1053 while written < buffer.len() {
1054 let page_index = (pos / PAGE_SIZE) as u64;
1055 let page_off = pos % PAGE_SIZE;
1056 let remain_in_page = PAGE_SIZE - page_off;
1057 let chunk = core::cmp::min(buffer.len() - written, remain_in_page);
1058
1059 let pinned = PageCacheManager::global()
1060 .pin_or_load(cache_id, page_index, |paddr| {
1061 unsafe {
1062 core::ptr::write_bytes(paddr as *mut u8, 0, PAGE_SIZE);
1063 }
1064 Ok(())
1065 })
1066 .map_err(|_| {
1067 FileSystemError::new(FileSystemErrorKind::IoError, "tmpfs page load error")
1068 })?;
1069
1070 unsafe {
1071 let dst = (pinned.paddr() as *mut u8).add(page_off);
1072 let src = buffer.as_ptr().add(written);
1073 core::ptr::copy_nonoverlapping(src, dst, chunk);
1074 }
1075 pinned.mark_dirty();
1076
1077 written += chunk;
1078 pos += chunk;
1079 }
1080
1081 {
1082 *self.position.write() = pos as u64;
1083 let mut meta = self.node.metadata.write();
1084 if pos > meta.size {
1085 meta.size = pos;
1086 }
1087 }
1088
1089 Ok(written)
1090 }
1091
1092 fn read_socket(&self, buffer: &mut [u8]) -> Result<usize, StreamError> {
1093 if let Some(ref socket) = self.socket_ref {
1094 socket.read(buffer)
1096 } else {
1097 Err(StreamError::NotSupported)
1098 }
1099 }
1100
1101 fn write_socket(&self, buffer: &[u8]) -> Result<usize, StreamError> {
1102 if let Some(ref socket) = self.socket_ref {
1103 socket.write(buffer)
1105 } else {
1106 Err(StreamError::NotSupported)
1107 }
1108 }
1109}
1110
1111impl StreamOps for TmpFileObject {
1112 fn read(&self, buffer: &mut [u8]) -> Result<usize, StreamError> {
1113 match self.node.file_type() {
1114 FileType::RegularFile => self.read_regular_file(buffer).map_err(StreamError::from),
1115 FileType::Directory => {
1116 let node = self.node.clone();
1118 let current_metadata = node.metadata.read();
1124 let mut all_entries = vec![crate::fs::DirectoryEntryInternal {
1125 name: ".".to_string(),
1126 file_type: FileType::Directory,
1127 size: current_metadata.size,
1128 file_id: current_metadata.file_id,
1129 metadata: Some(current_metadata.clone()),
1130 }];
1131
1132 all_entries.push(crate::fs::DirectoryEntryInternal {
1134 name: "..".to_string(),
1135 file_type: FileType::Directory,
1136 size: current_metadata.size,
1137 file_id: current_metadata.file_id,
1138 metadata: Some(current_metadata.clone()),
1139 });
1140
1141 let children = node.children.read();
1143 let mut regular_entries = Vec::new();
1144 for (name, child) in children.iter() {
1145 let metadata = child.metadata().unwrap();
1146 regular_entries.push(crate::fs::DirectoryEntryInternal {
1147 name: name.clone(),
1148 file_type: child.file_type().unwrap().clone(),
1149 size: metadata.size,
1150 file_id: metadata.file_id,
1151 metadata: Some(metadata.clone()),
1152 });
1153 }
1154
1155 regular_entries.sort_by_key(|entry| entry.file_id);
1157
1158 all_entries.extend(regular_entries);
1160
1161 let position = *self.position.read() as usize;
1163
1164 if position >= all_entries.len() {
1165 return Ok(0); }
1167
1168 let internal_entry = &all_entries[position];
1170
1171 let dir_entry = crate::fs::DirectoryEntry::from_internal(internal_entry);
1173
1174 let entry_size = dir_entry.entry_size();
1176
1177 if buffer.len() < entry_size {
1179 return Err(StreamError::InvalidArgument); }
1181
1182 let entry_bytes = unsafe {
1184 core::slice::from_raw_parts(&dir_entry as *const _ as *const u8, entry_size)
1185 };
1186
1187 buffer[..entry_size].copy_from_slice(entry_bytes);
1189
1190 *self.position.write() += 1;
1192
1193 Ok(entry_size)
1194 }
1195 FileType::CharDevice(_) | FileType::BlockDevice(_) => {
1196 self.read_device(buffer).map_err(StreamError::from)
1197 }
1198 FileType::Socket(_) => self.read_socket(buffer),
1199 _ => Err(StreamError::NotSupported),
1200 }
1201 }
1202
1203 fn write(&self, buffer: &[u8]) -> Result<usize, StreamError> {
1204 match self.node.file_type() {
1205 FileType::RegularFile => self.write_regular_file(buffer).map_err(StreamError::from),
1206 FileType::Directory => Err(StreamError::from(FileSystemError::new(
1207 FileSystemErrorKind::IsADirectory,
1208 "Cannot write to directory",
1209 ))),
1210 FileType::CharDevice(_) | FileType::BlockDevice(_) => {
1211 self.write_device(buffer).map_err(StreamError::from)
1212 }
1213 FileType::Socket(_) => self.write_socket(buffer),
1214 _ => Err(StreamError::NotSupported),
1215 }
1216 }
1217}
1218
1219impl ControlOps for TmpFileObject {
1220 fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
1222 Err("Control operations not supported on temporary files")
1223 }
1224}
1225
1226#[derive(Clone, Copy, Debug)]
1227struct MmapRange {
1228 vaddr_start: usize,
1229 vaddr_end: usize,
1230 offset: usize,
1231}
1232
1233impl MemoryMappingOps for TmpFileObject {
1234 fn get_mapping_info(
1235 &self,
1236 offset: usize,
1237 length: usize,
1238 ) -> Result<(usize, usize, bool), &'static str> {
1239 if offset % PAGE_SIZE != 0 {
1240 return Err("Offset not page aligned");
1241 }
1242
1243 let file_size = self.node.metadata.read().size;
1244 if file_size == 0 || offset >= file_size {
1245 return Err("Offset beyond file size");
1246 }
1247
1248 let required_size = offset.saturating_add(length).max(file_size);
1249 self.ensure_mmap_backing(file_size, required_size)
1250 .map_err(|_| "Failed to prepare mmap backing")?;
1251
1252 let backing_guard = self.mmap_backing.read();
1253 let backing = backing_guard.as_ref().ok_or("mmap backing missing")?;
1254 let base = backing.as_ptr() as usize;
1255 let paddr = base + offset;
1256 if paddr % PAGE_SIZE != 0 {
1257 return Err("Backing address not aligned");
1258 }
1259
1260 Ok((paddr, 0x3, false))
1261 }
1262
1263 fn get_mapping_info_with(
1264 &self,
1265 offset: usize,
1266 length: usize,
1267 is_shared: bool,
1268 ) -> Result<(usize, usize, bool), &'static str> {
1269 if is_shared {
1270 if offset % PAGE_SIZE != 0 {
1271 return Err("Offset not page aligned");
1272 }
1273
1274 let file_size = self.node.metadata.read().size;
1275 if file_size == 0 || offset >= file_size {
1276 return Err("Offset beyond file size");
1277 }
1278
1279 let _ = length;
1280 return Ok((0, 0x3, true));
1281 }
1282
1283 self.get_mapping_info(offset, length)
1284 }
1285
1286 fn on_mapped(&self, vaddr: usize, _paddr: usize, length: usize, offset: usize) {
1287 if length == 0 {
1288 return;
1289 }
1290 let vaddr_end = vaddr.saturating_add(length - 1);
1291 let range = MmapRange {
1292 vaddr_start: vaddr,
1293 vaddr_end,
1294 offset,
1295 };
1296 self.mmap_ranges.write().insert(vaddr, range);
1297 }
1298
1299 fn on_unmapped(&self, vaddr: usize, _length: usize) {
1300 self.mmap_ranges.write().remove(&vaddr);
1301 }
1302
1303 fn supports_mmap(&self) -> bool {
1304 true
1305 }
1306
1307 fn resolve_fault(
1308 &self,
1309 access: &crate::object::capability::memory_mapping::AccessKind,
1310 map: &crate::vm::vmem::VirtualMemoryMap,
1311 ) -> core::result::Result<
1312 crate::object::capability::memory_mapping::ResolveFaultResult,
1313 crate::object::capability::memory_mapping::ResolveFaultError,
1314 > {
1315 let range = self
1316 .mmap_ranges
1317 .read()
1318 .get(&map.vmarea.start)
1319 .copied()
1320 .ok_or(crate::object::capability::memory_mapping::ResolveFaultError::Invalid)?;
1321 if access.vaddr < range.vaddr_start || access.vaddr > range.vaddr_end {
1322 return Err(crate::object::capability::memory_mapping::ResolveFaultError::Invalid);
1323 }
1324
1325 let file_size = self.node.metadata.read().size;
1326 let file_offset = range
1327 .offset
1328 .saturating_add(access.vaddr.saturating_sub(range.vaddr_start));
1329 if file_size == 0 || file_offset >= file_size {
1330 return Err(crate::object::capability::memory_mapping::ResolveFaultError::Invalid);
1331 }
1332
1333 let page_index = (file_offset / PAGE_SIZE) as u64;
1334 let pinned = PageCacheManager::global()
1335 .pin_or_load(self.cache_id(), page_index, |paddr| {
1336 unsafe {
1337 core::ptr::write_bytes(paddr as *mut u8, 0, PAGE_SIZE);
1338 }
1339 Ok(())
1340 })
1341 .map_err(|_| crate::object::capability::memory_mapping::ResolveFaultError::Invalid)?;
1342
1343 if matches!(
1344 access.op,
1345 crate::object::capability::memory_mapping::AccessOp::Store
1346 ) {
1347 pinned.mark_dirty();
1348 }
1349
1350 Ok(
1351 crate::object::capability::memory_mapping::ResolveFaultResult {
1352 paddr_page_base: pinned.paddr(),
1353 is_tail: false,
1354 },
1355 )
1356 }
1357}
1358
1359impl FileObject for TmpFileObject {
1360 fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<usize, StreamError> {
1361 if self.node.file_type() != FileType::RegularFile {
1362 return Err(StreamError::NotSupported);
1363 }
1364
1365 let offset = usize::try_from(offset).map_err(|_| StreamError::InvalidArgument)?;
1366 let file_size = self.node.metadata.read().size;
1367 if offset >= file_size {
1368 return Ok(0);
1369 }
1370
1371 let mut total_read = 0usize;
1372 let cache_id = self.cache_id();
1373 while total_read < buffer.len() && offset + total_read < file_size {
1374 let absolute = offset + total_read;
1375 let page_index = (absolute / PAGE_SIZE) as u64;
1376 let offset_in_page = absolute % PAGE_SIZE;
1377
1378 let pinned = PageCacheManager::global()
1379 .pin_or_load(cache_id, page_index, |paddr| {
1380 unsafe {
1381 core::ptr::write_bytes(paddr as *mut u8, 0, PAGE_SIZE);
1382 }
1383 Ok(())
1384 })
1385 .map_err(|_| StreamError::IoError)?;
1386
1387 unsafe {
1388 let src = (pinned.paddr() as *const u8).add(offset_in_page);
1389 let remaining_in_page = PAGE_SIZE - offset_in_page;
1390 let remaining_file = file_size - (offset + total_read);
1391 let remaining_buf = buffer.len() - total_read;
1392 let chunk = core::cmp::min(
1393 remaining_in_page,
1394 core::cmp::min(remaining_file, remaining_buf),
1395 );
1396 core::ptr::copy_nonoverlapping(src, buffer.as_mut_ptr().add(total_read), chunk);
1397 total_read += chunk;
1398 }
1399 }
1400
1401 Ok(total_read)
1402 }
1403
1404 fn write_at(&self, offset: u64, buffer: &[u8]) -> Result<usize, StreamError> {
1405 if self.node.file_type() != FileType::RegularFile {
1406 return Err(StreamError::NotSupported);
1407 }
1408
1409 let offset = usize::try_from(offset).map_err(|_| StreamError::InvalidArgument)?;
1410 let mut written = 0usize;
1411 let cache_id = self.cache_id();
1412
1413 while written < buffer.len() {
1414 let absolute = offset + written;
1415 let page_index = (absolute / PAGE_SIZE) as u64;
1416 let page_off = absolute % PAGE_SIZE;
1417 let remain_in_page = PAGE_SIZE - page_off;
1418 let chunk = core::cmp::min(buffer.len() - written, remain_in_page);
1419
1420 let pinned = PageCacheManager::global()
1421 .pin_or_load(cache_id, page_index, |paddr| {
1422 unsafe {
1423 core::ptr::write_bytes(paddr as *mut u8, 0, PAGE_SIZE);
1424 }
1425 Ok(())
1426 })
1427 .map_err(|_| StreamError::IoError)?;
1428
1429 unsafe {
1430 let dst = (pinned.paddr() as *mut u8).add(page_off);
1431 let src = buffer.as_ptr().add(written);
1432 core::ptr::copy_nonoverlapping(src, dst, chunk);
1433 }
1434 pinned.mark_dirty();
1435 written += chunk;
1436 }
1437
1438 let new_end = offset + written;
1439 {
1440 let mut meta = self.node.metadata.write();
1441 if new_end > meta.size {
1442 meta.size = new_end;
1443 }
1444 }
1445
1446 Ok(written)
1447 }
1448
1449 fn seek(&self, pos: crate::fs::SeekFrom) -> Result<u64, StreamError> {
1450 use crate::fs::SeekFrom;
1451
1452 let mut position = self.position.write();
1453 let file_size = self.node.metadata.read().size as u64;
1454
1455 let new_pos = match pos {
1456 SeekFrom::Start(offset) => {
1457 if offset <= file_size {
1458 offset
1459 } else {
1460 return Err(StreamError::from(FileSystemError::new(
1461 FileSystemErrorKind::NotSupported,
1462 "Seek offset beyond EOF",
1463 )));
1464 }
1465 }
1466 SeekFrom::End(offset) => {
1467 if offset >= 0 {
1468 file_size + offset as u64
1469 } else {
1470 file_size.saturating_sub((-offset) as u64)
1471 }
1472 }
1473 SeekFrom::Current(offset) => {
1474 if offset >= 0 {
1475 *position + offset as u64
1476 } else {
1477 position.saturating_sub((-offset) as u64)
1478 }
1479 }
1480 };
1481
1482 *position = new_pos;
1483 Ok(new_pos)
1484 }
1485
1486 fn metadata(&self) -> Result<FileMetadata, StreamError> {
1487 self.node.metadata().map_err(StreamError::from)
1488 }
1489
1490 fn truncate(&self, size: u64) -> Result<(), StreamError> {
1491 if self.node.file_type() != FileType::RegularFile {
1492 return Err(StreamError::from(FileSystemError::new(
1493 FileSystemErrorKind::IsADirectory,
1494 "Cannot truncate non-regular file",
1495 )));
1496 }
1497
1498 let new_size = usize::try_from(size).map_err(|_| StreamError::InvalidArgument)?;
1499 let old_size = self.node.metadata.read().size;
1500 if new_size == old_size {
1501 return Ok(());
1502 }
1503
1504 let cache_id = self.cache_id();
1505 if new_size == 0 {
1506 PageCacheManager::global().invalidate(cache_id);
1507 } else if new_size < old_size {
1508 let start_page = new_size / PAGE_SIZE;
1509 let end_page = (old_size - 1) / PAGE_SIZE;
1510 let tail_offset = new_size % PAGE_SIZE;
1511 for page_index in start_page..=end_page {
1512 let pinned = PageCacheManager::global()
1513 .pin_or_load(cache_id, page_index as u64, |paddr| {
1514 unsafe {
1515 core::ptr::write_bytes(paddr as *mut u8, 0, PAGE_SIZE);
1516 }
1517 Ok(())
1518 })
1519 .map_err(|_| StreamError::IoError)?;
1520
1521 unsafe {
1522 let base = pinned.paddr() as *mut u8;
1523 if page_index == start_page && tail_offset != 0 {
1524 core::ptr::write_bytes(base.add(tail_offset), 0, PAGE_SIZE - tail_offset);
1525 } else {
1526 core::ptr::write_bytes(base, 0, PAGE_SIZE);
1527 }
1528 }
1529 pinned.mark_dirty();
1530 }
1531 }
1532
1533 {
1534 let mut meta = self.node.metadata.write();
1535 meta.size = new_size;
1536 }
1537 Ok(())
1538 }
1539
1540 fn as_any(&self) -> &dyn Any {
1541 self
1542 }
1543}
1544
1545impl PageCacheCapable for TmpFileObject {
1546 fn cache_id(&self) -> crate::fs::vfs_v2::cache::CacheId {
1547 let fs = self
1548 .node
1549 .filesystem()
1550 .and_then(|weak| weak.upgrade())
1551 .expect("TmpFileObject: filesystem gone");
1552 let fs_id = fs.fs_id().get();
1553 let file_id = self.node.metadata.read().file_id & 0xFFFF_FFFF;
1554 let id = (fs_id << 32) | file_id;
1555 crate::fs::vfs_v2::cache::CacheId::new(id)
1556 }
1557}
1558
1559impl crate::object::capability::selectable::Selectable for TmpFileObject {
1560 fn wait_until_ready(
1561 &self,
1562 _interest: crate::object::capability::selectable::ReadyInterest,
1563 _trapframe: &mut crate::arch::Trapframe,
1564 _timeout_ticks: Option<u64>,
1565 ) -> crate::object::capability::selectable::SelectWaitOutcome {
1566 crate::object::capability::selectable::SelectWaitOutcome::Ready
1567 }
1568}
1569
1570pub struct TmpFSDriver;
1571
1572impl FileSystemDriver for TmpFSDriver {
1573 fn filesystem_type(&self) -> crate::fs::FileSystemType {
1574 crate::fs::FileSystemType::Virtual
1575 }
1576
1577 fn create_from_memory(
1578 &self,
1579 _memory_area: &crate::vm::vmem::MemoryArea,
1580 ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1581 Ok(TmpFS::new(0) as Arc<dyn FileSystemOperations>)
1582 }
1583
1584 fn create_from_params(
1585 &self,
1586 _params: &dyn crate::fs::params::FileSystemParams,
1587 ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1588 Ok(TmpFS::create_from_option_string(None))
1589 }
1590
1591 fn create_from_option_string(
1592 &self,
1593 options: &str,
1594 ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1595 let memory_limit = parse_tmpfs_size_option(options).unwrap_or(64 * 1024 * 1024); Ok(TmpFS::new(memory_limit))
1598 }
1599
1600 fn name(&self) -> &'static str {
1601 "tmpfs"
1602 }
1603}
1604
1605fn parse_tmpfs_size_option(options: &str) -> Option<usize> {
1610 for option in options.split(',') {
1611 if let Some(size_str) = option.strip_prefix("size=") {
1612 let size_str = size_str.trim();
1614 if size_str.is_empty() {
1615 continue;
1616 }
1617
1618 let (number_part, multiplier) = if size_str.ends_with('K') || size_str.ends_with('k') {
1619 (&size_str[..size_str.len() - 1], 1024)
1620 } else if size_str.ends_with('M') || size_str.ends_with('m') {
1621 (&size_str[..size_str.len() - 1], 1024 * 1024)
1622 } else if size_str.ends_with('G') || size_str.ends_with('g') {
1623 (&size_str[..size_str.len() - 1], 1024 * 1024 * 1024)
1624 } else {
1625 (size_str, 1)
1626 };
1627
1628 if let Ok(number) = number_part.parse::<usize>() {
1629 return Some(number * multiplier);
1630 }
1631 }
1632 }
1633 None
1634}
1635
1636fn register_driver() {
1637 let fs_driver_manager = get_fs_driver_manager();
1638 fs_driver_manager.register_driver(Box::new(TmpFSDriver));
1639}
1640
1641driver_initcall!(register_driver);
1642
1643#[cfg(test)]
1644mod tests;