kernel/fs/vfs_v2/drivers/tmpfs/
mod.rs

1//! TmpFS v2 - Memory-based filesystem implementation
2//!
3//! This is a complete rewrite of TmpFS using the new VFS v2 architecture.
4//! It implements FileSystemOperations directly and uses VfsNode for internal
5//! structure representation.
6
7use 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
37/// TmpFS v2 - New memory-based filesystem implementation
38///
39/// This struct implements an in-memory filesystem for VFS v2.
40/// It supports regular files, directories, and device nodes, with optional memory usage limits.
41/// The internal structure is based on `TmpNode` and uses locking for thread safety.
42///
43pub struct TmpFS {
44    /// Unique filesystem identifier
45    fs_id: FileSystemId,
46    /// Root directory node
47    root: RwLock<Arc<TmpNode>>,
48    /// Memory limit (0 = unlimited)
49    memory_limit: usize,
50    /// Current memory usage
51    current_memory: Mutex<usize>,
52    /// Next file ID generator
53    next_file_id: Mutex<u64>,
54    /// Filesystem name
55    name: String,
56}
57
58impl TmpFS {
59    /// Create a new TmpFS instance (two-phase initialization)
60    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), // Start from 2, root is 1
68            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    /// VFS v2 driver registration API: create from option string
80    /// Example option: "mem=1048576" etc.
81    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    /// Generate next unique file ID
97    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    /// Check memory limit
105    fn check_memory_limit(&self, additional_bytes: usize) -> Result<(), FileSystemError> {
106        if self.memory_limit == 0 {
107            return Ok(()); // Unlimited
108        }
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    /// Add to memory usage
122    fn add_memory_usage(&self, bytes: usize) {
123        if self.memory_limit > 0 {
124            *self.current_memory.lock() += bytes;
125        }
126    }
127
128    /// Subtract from memory usage
129    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        // Downcast to TmpNode
148        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        // Check if parent is a directory
159        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        // Handle special directory entries
167        match name.as_str() {
168            "." => {
169                // Current directory - return self
170                return Ok(Arc::clone(&parent_node));
171            }
172            ".." => {
173                // Parent directory - try to handle within filesystem
174                if let Some(parent_weak) = &tmp_node.parent() {
175                    if let Some(parent) = parent_weak.upgrade() {
176                        // crate::println!("TmpFS lookup: found parent node {:?}", parent);
177                        // Return parent node within this filesystem
178                        return Ok(parent as Arc<dyn VfsNode>);
179                    }
180                }
181            }
182            _ => {
183                // Regular lookup
184            }
185        }
186
187        // Look up child in directory
188        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        // Check if parent is a directory
244        if tmp_parent.file_type() != FileType::Directory {
245            return Err(FileSystemError::new(
246                FileSystemErrorKind::NotADirectory,
247                "Parent is not a directory",
248            ));
249        }
250        // Check if file already exists
251        {
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        // Generate file ID
261        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                // Account for memory usage (target path length)
269                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        // After creation, set the filesystem reference (always check if upgrade is possible)
287        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        // Add to parent directory
303        {
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    /// Create a hard link to an existing file
313    ///
314    /// This function creates a new directory entry (hard link) for an existing file.
315    /// Both the link and the target must be in the same filesystem.
316    /// The link count of the target file is incremented.
317    /// Returns the target node (the same inode as the original file).
318    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        // Check that both parent and target are TmpNodes
325        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        // Check that parent is a directory
346        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        // Check that target is a regular file (no directory hard links)
354        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        // Check if link name already exists
362        {
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        // For TmpFS, hard links are just additional references to the same TmpNode
373        // Update the link count in metadata
374        {
375            let mut metadata = tmp_target.metadata.write();
376            metadata.link_count += 1;
377        }
378
379        // Add the target node to the parent directory under the new name
380        {
381            let mut children = tmp_parent.children.write();
382            children.insert(link_name.clone(), Arc::clone(target_node));
383        }
384
385        // Return the same target node (hard link shares the same inode)
386        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        // Check if parent is a directory
401        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        // Remove from parent directory
409        let mut children = tmp_parent.children.write();
410        if let Some(removed_node) = children.get(name) {
411            // If it's a directory, check if it's empty first
412            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                // Update memory usage for regular files and symbolic links
424                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        // Now remove the node
443        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        // Check if it's a directory
462        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
499/// TmpNode represents a file, directory, or device node in TmpFS.
500///
501/// Each node contains metadata, content (for symlinks), children (for directories),
502/// and references to its parent and filesystem. All fields are protected by locks for thread safety.
503pub struct TmpNode {
504    /// File name
505    name: RwLock<String>,
506    /// File type
507    file_type: RwLock<FileType>,
508    /// File metadata
509    metadata: RwLock<FileMetadata>,
510    /// File content (for symlinks)
511    content: RwLock<Vec<u8>>,
512    /// Child nodes (for directories)
513    children: RwLock<BTreeMap<String, Arc<dyn VfsNode>>>,
514    /// Parent node (weak reference to avoid cycles)
515    parent: RwLock<Option<Weak<TmpNode>>>,
516    /// Reference to filesystem (Weak<dyn FileSystemOperations>)
517    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    /// Create a new regular file node
536    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, // TODO: actual timestamp
549                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), // No parent initially
557            filesystem: RwLock::new(None),
558        }
559    }
560
561    /// Create a new directory node
562    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), // No parent initially
583            filesystem: RwLock::new(None),
584        }
585    }
586
587    /// Create a new device file node
588    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), // No parent initially
609            filesystem: RwLock::new(None),
610        }
611    }
612
613    /// Create a new symbolic link node
614    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, // TODO: actual timestamp
627                modified_time: 0,
628                accessed_time: 0,
629                file_id,
630                link_count: 1,
631            }),
632            // Store symlink target in content as UTF-8 bytes
633            content: RwLock::new(target.into_bytes()),
634            children: RwLock::new(BTreeMap::new()),
635            parent: RwLock::new(None), // No parent initially
636            filesystem: RwLock::new(None),
637        }
638    }
639
640    /// Set the filesystem reference for this node
641    pub fn set_filesystem(&self, fs: Weak<dyn FileSystemOperations>) {
642        *self.filesystem.write() = Some(fs);
643    }
644
645    /// Update file size in metadata
646    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; // TODO: actual timestamp
650    }
651
652    /// Set parent reference for this node
653    pub fn set_parent(&self, parent: Weak<TmpNode>) {
654        self.parent.write().replace(parent);
655    }
656
657    /// Check if this node is the root of the filesystem
658    pub fn is_filesystem_root(&self) -> bool {
659        self.parent.read().is_none()
660    }
661
662    /// Get the file name
663    pub fn name(&self) -> String {
664        self.name.read().clone()
665    }
666
667    /// Get the file type
668    pub fn file_type(&self) -> FileType {
669        self.file_type.read().clone()
670    }
671
672    /// Get the filesystem reference
673    pub fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>> {
674        self.filesystem.read().clone()
675    }
676
677    /// Get the parent node
678    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        // Check if this is actually a symbolic link and return target
702        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
712/// File object for TmpFS operations
713///
714/// TmpFileObject represents an open file or directory in TmpFS.
715///
716/// It maintains the current file position and, for device files, an optional device guard.
717/// For socket files, it maintains a reference to the socket object.
718pub struct TmpFileObject {
719    /// Reference to the TmpNode
720    node: Arc<TmpNode>,
721
722    /// Current file position
723    position: RwLock<u64>,
724
725    /// Optional device guard for device files
726    device_guard: Option<Arc<dyn Device>>,
727
728    /// Optional socket reference for socket files
729    socket_ref: Option<Arc<dyn crate::network::SocketObject>>,
730    /// Page-aligned backing for private mmap operations
731    mmap_backing: RwLock<Option<Box<[crate::mem::page::Page]>>>,
732    /// Byte length of the mmap backing (file size snapshot)
733    mmap_backing_len: Mutex<usize>,
734    /// Active mmap ranges keyed by starting virtual address
735    mmap_ranges: RwLock<BTreeMap<usize, MmapRange>>,
736}
737
738impl TmpFileObject {
739    /// Create a new file object for regular files
740    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    /// Create a new file object for directories
753    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    /// Create a new file object for device files
766    pub fn new_device(node: Arc<TmpNode>, info: DeviceFileInfo) -> Self {
767        // Try to borrow the device from DeviceManager
768        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                // If borrowing fails, return an error
780                panic!("Failed to borrow device {}", info.device_id);
781            }
782        }
783    }
784
785    /// Create a new file object for socket files
786    pub fn new_socket(node: Arc<TmpNode>, info: SocketFileInfo) -> Self {
787        // Try to get the socket from NetworkManager
788        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                // Socket not found in NetworkManager. This can happen in legitimate
800                // scenarios (e.g. stale socket file after reboot or the socket being
801                // unregistered before the file is opened). We avoid panicking here and
802                // instead return a TmpFile without an associated socket so that later
803                // operations can fail gracefully with a FileSystemError.
804                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                        // For block devices, we can read a single sector
890                        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            // Delegate read operation to the socket object
1095            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            // Delegate write operation to the socket object
1104            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                // For directories, return entries in struct format
1117                let node = self.node.clone();
1118                // We need to reconstruct the path from the node structure
1119                // Since we don't have path stored, use the readdir logic directly
1120
1121                // Create a vector to store all entries including "." and ".."
1122                // Add "." entry (current directory)
1123                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                // Add ".." entry (parent directory) - simplified to point to self for now
1133                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                // Add regular directory entries and sort by file_id
1142                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                // Sort regular entries by file_id (ascending order)
1156                regular_entries.sort_by_key(|entry| entry.file_id);
1157
1158                // Append sorted regular entries to the result
1159                all_entries.extend(regular_entries);
1160
1161                // position is the entry index
1162                let position = *self.position.read() as usize;
1163
1164                if position >= all_entries.len() {
1165                    return Ok(0); // EOF
1166                }
1167
1168                // Get current entry (already sorted)
1169                let internal_entry = &all_entries[position];
1170
1171                // Convert to binary format
1172                let dir_entry = crate::fs::DirectoryEntry::from_internal(internal_entry);
1173
1174                // Calculate actual entry size
1175                let entry_size = dir_entry.entry_size();
1176
1177                // Check buffer size
1178                if buffer.len() < entry_size {
1179                    return Err(StreamError::InvalidArgument); // Buffer too small
1180                }
1181
1182                // Treat struct as byte array
1183                let entry_bytes = unsafe {
1184                    core::slice::from_raw_parts(&dir_entry as *const _ as *const u8, entry_size)
1185                };
1186
1187                // Copy to buffer
1188                buffer[..entry_size].copy_from_slice(entry_bytes);
1189
1190                // Move to next entry
1191                *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    // Temporary files don't support control operations by default
1221    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        // Parse tmpfs options (e.g., "size=64M")
1596        let memory_limit = parse_tmpfs_size_option(options).unwrap_or(64 * 1024 * 1024); // Default 64MB
1597        Ok(TmpFS::new(memory_limit))
1598    }
1599
1600    fn name(&self) -> &'static str {
1601        "tmpfs"
1602    }
1603}
1604
1605/// Parse tmpfs size option from option string
1606///
1607/// Parses size option in the format "size=64M", "size=1G", etc.
1608/// Returns the size in bytes, or None if no valid size option is found.
1609fn 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            // Parse size with suffix (K, M, G)
1613            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;