kernel/fs/vfs_v2/drivers/
devfs.rs

1//! DevFS - Device filesystem implementation
2//!
3//! DevFS is a virtual filesystem that automatically exposes all devices
4//! registered in the global DeviceManager. When mounted (typically at /dev),
5//! it provides device files for all character and block devices that are
6//! currently registered with the kernel.
7//!
8//! ## Features
9//!
10//! - **Automatic Device Discovery**: Shows all devices from DeviceManager
11//! - **Dynamic Updates**: Reflects changes when devices are added/removed
12//! - **Device File Support**: Exposes character and block devices as device files
13//! - **Read-only Filesystem**: Device files cannot be created/deleted through VFS
14//!
15//! ## Usage
16//!
17//! ```rust
18//! // Mount devfs at /dev
19//! let vfs = VfsManager::new();
20//! let devfs = DevFS::new();
21//! vfs.mount(devfs, "/dev", 0)?;
22//!
23//! // Access device files
24//! let tty_file = vfs.open("/dev/tty0", O_RDWR)?;
25//! ```
26
27use alloc::{
28    boxed::Box,
29    collections::BTreeMap,
30    format,
31    string::{String, ToString},
32    sync::{Arc, Weak},
33    vec::Vec,
34};
35use core::any::Any;
36use spin::RwLock;
37
38use crate::device::{Device, DeviceType, manager::DeviceManager};
39use crate::object::capability::{ControlOps, StreamError, StreamOps};
40use crate::{
41    driver_initcall,
42    fs::{
43        DeviceFileInfo, FileMetadata, FileObject, FilePermission, FileSystemDriver,
44        FileSystemError, FileSystemErrorKind, FileSystemType, FileType, SeekFrom,
45        get_fs_driver_manager,
46    },
47    object::capability::MemoryMappingOps,
48};
49
50use super::super::core::{DirectoryEntryInternal, FileSystemId, FileSystemOperations, VfsNode};
51
52/// DevFS - Device filesystem implementation
53///
54/// This filesystem automatically exposes all devices registered in the global
55/// DeviceManager as device files. It provides a virtual view of the system's
56/// devices, similar to /dev in Unix-like systems.
57pub struct DevFS {
58    /// Unique filesystem identifier
59    fs_id: FileSystemId,
60    /// Root directory node
61    root: RwLock<Arc<DevNode>>,
62    /// Filesystem name
63    name: String,
64}
65
66impl DevFS {
67    /// Create a new DevFS instance
68    pub fn new() -> Arc<Self> {
69        let root = Arc::new(DevNode::new_directory("/".to_string()));
70        let fs = Arc::new(Self {
71            fs_id: FileSystemId::new(),
72            root: RwLock::new(Arc::clone(&root)),
73            name: "devfs".to_string(),
74        });
75        let fs_weak = Arc::downgrade(&(fs.clone() as Arc<dyn FileSystemOperations>));
76        root.set_filesystem(fs_weak);
77        fs
78    }
79
80    /// Populate the filesystem with current devices from DeviceManager
81    fn populate_devices(&self) -> Result<(), FileSystemError> {
82        let device_manager = DeviceManager::get_manager();
83        let root = self.root.read();
84
85        // Clear existing devices (for dynamic updates)
86        root.clear_children();
87
88        // Get all devices that were registered with explicit names
89        let named_devices = device_manager.get_named_devices();
90
91        for (device_name, device) in named_devices {
92            let device_type = device.device_type();
93
94            // Only add char and block devices to devfs
95            match device_type {
96                DeviceType::Char | DeviceType::Block => {
97                    // Get the actual device ID from the name
98                    let device_id = device_manager
99                        .get_device_id_by_name(&device_name)
100                        .unwrap_or(0); // fallback to 0 if not found
101
102                    let device_file_info = DeviceFileInfo {
103                        device_id,
104                        device_type,
105                    };
106
107                    let file_type = match device_type {
108                        DeviceType::Char => FileType::CharDevice(device_file_info),
109                        DeviceType::Block => FileType::BlockDevice(device_file_info),
110                        _ => continue, // Skip other device types
111                    };
112
113                    let device_node = Arc::new(DevNode::new_device_file(
114                        device_name.clone(),
115                        file_type,
116                        device_id as u64, // Use the device ID as file ID too
117                    ));
118
119                    // Set filesystem reference for the device node
120                    if let Some(fs_ref) = root.filesystem() {
121                        device_node.set_filesystem(fs_ref);
122                    }
123
124                    root.add_child(device_name, device_node)?;
125                }
126                _ => {} // Skip non-device files
127            }
128        }
129
130        Ok(())
131    }
132}
133
134impl FileSystemOperations for DevFS {
135    fn fs_id(&self) -> FileSystemId {
136        self.fs_id
137    }
138
139    fn name(&self) -> &str {
140        &self.name
141    }
142
143    fn root_node(&self) -> Arc<dyn VfsNode> {
144        // Refresh devices on each root access to ensure up-to-date view
145        let _ = self.populate_devices();
146        Arc::clone(&*self.root.read()) as Arc<dyn VfsNode>
147    }
148
149    fn lookup(
150        &self,
151        parent: &Arc<dyn VfsNode>,
152        name: &String,
153    ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
154        // Refresh devices before lookup to ensure up-to-date view
155        let _ = self.populate_devices();
156
157        let dev_node = Arc::downcast::<DevNode>(parent.clone()).map_err(|_| {
158            FileSystemError::new(
159                FileSystemErrorKind::NotSupported,
160                "Invalid node type for DevFS",
161            )
162        })?;
163
164        if let Some(child) = dev_node.get_child(name) {
165            Ok(child as Arc<dyn VfsNode>)
166        } else {
167            Err(FileSystemError::new(
168                FileSystemErrorKind::NotFound,
169                format!("Device '{}' not found in devfs", name),
170            ))
171        }
172    }
173
174    fn readdir(
175        &self,
176        node: &Arc<dyn VfsNode>,
177    ) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
178        // Refresh devices before readdir to ensure up-to-date view
179        let _ = self.populate_devices();
180
181        let dev_node = Arc::downcast::<DevNode>(node.clone()).map_err(|_| {
182            FileSystemError::new(
183                FileSystemErrorKind::NotSupported,
184                "Invalid node type for DevFS",
185            )
186        })?;
187
188        dev_node.readdir()
189    }
190
191    fn open(
192        &self,
193        node: &Arc<dyn VfsNode>,
194        _flags: u32,
195    ) -> Result<Arc<dyn FileObject>, FileSystemError> {
196        let dev_node = Arc::downcast::<DevNode>(node.clone()).map_err(|_| {
197            FileSystemError::new(
198                FileSystemErrorKind::NotSupported,
199                "Invalid node type for DevFS",
200            )
201        })?;
202
203        dev_node.open()
204    }
205
206    fn is_read_only(&self) -> bool {
207        true
208    }
209
210    // DevFS is read-only - these operations are not supported
211    fn create(
212        &self,
213        _parent: &Arc<dyn VfsNode>,
214        _name: &String,
215        _file_type: FileType,
216        _mode: u32,
217    ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
218        Err(FileSystemError::new(
219            FileSystemErrorKind::ReadOnly,
220            "DevFS is read-only: cannot create files",
221        ))
222    }
223
224    fn remove(&self, _parent: &Arc<dyn VfsNode>, _name: &String) -> Result<(), FileSystemError> {
225        Err(FileSystemError::new(
226            FileSystemErrorKind::ReadOnly,
227            "DevFS is read-only: cannot remove files",
228        ))
229    }
230
231    fn as_any(&self) -> &dyn Any {
232        self
233    }
234}
235
236/// A node in the DevFS filesystem
237pub struct DevNode {
238    /// Node name
239    name: String,
240    /// File type
241    file_type: FileType,
242    /// File ID
243    file_id: u64,
244    /// Child nodes (for directories)
245    children: RwLock<BTreeMap<String, Arc<DevNode>>>,
246    /// Reference to filesystem
247    filesystem: RwLock<Option<Weak<dyn FileSystemOperations>>>,
248}
249
250impl Clone for DevNode {
251    fn clone(&self) -> Self {
252        Self {
253            name: self.name.clone(),
254            file_type: self.file_type.clone(),
255            file_id: self.file_id,
256            children: RwLock::new(self.children.read().clone()),
257            filesystem: RwLock::new(self.filesystem.read().clone()),
258        }
259    }
260}
261
262impl DevNode {
263    /// Create a new directory node
264    pub fn new_directory(name: String) -> Self {
265        Self {
266            name,
267            file_type: FileType::Directory,
268            file_id: 0, // Root directory ID
269            children: RwLock::new(BTreeMap::new()),
270            filesystem: RwLock::new(None),
271        }
272    }
273
274    /// Create a new device file node
275    pub fn new_device_file(name: String, file_type: FileType, file_id: u64) -> Self {
276        Self {
277            name,
278            file_type,
279            file_id,
280            children: RwLock::new(BTreeMap::new()),
281            filesystem: RwLock::new(None),
282        }
283    }
284
285    /// Set filesystem reference
286    pub fn set_filesystem(&self, fs: Weak<dyn FileSystemOperations>) {
287        *self.filesystem.write() = Some(fs);
288    }
289
290    /// Add a child node
291    pub fn add_child(&self, name: String, child: Arc<DevNode>) -> Result<(), FileSystemError> {
292        if self.file_type != FileType::Directory {
293            return Err(FileSystemError::new(
294                FileSystemErrorKind::NotADirectory,
295                "Cannot add child to non-directory node",
296            ));
297        }
298
299        let mut children = self.children.write();
300        children.insert(name, child);
301        Ok(())
302    }
303
304    /// Get a child by name
305    pub fn get_child(&self, name: &str) -> Option<Arc<DevNode>> {
306        let children = self.children.read();
307        children.get(name).cloned()
308    }
309
310    /// Clear all children (for dynamic updates)
311    pub fn clear_children(&self) {
312        let mut children = self.children.write();
313        children.clear();
314    }
315
316    /// Read directory contents
317    pub fn readdir(&self) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
318        if self.file_type != FileType::Directory {
319            return Err(FileSystemError::new(
320                FileSystemErrorKind::NotADirectory,
321                "Cannot read directory of non-directory node",
322            ));
323        }
324
325        let children = self.children.read();
326        let mut entries = Vec::new();
327
328        // Add "." entry (current directory)
329        entries.push(DirectoryEntryInternal {
330            name: ".".to_string(),
331            file_type: FileType::Directory,
332            file_id: self.file_id,
333        });
334
335        // Add ".." entry (parent directory)
336        // For DevFS root, parent is itself
337        entries.push(DirectoryEntryInternal {
338            name: "..".to_string(),
339            file_type: FileType::Directory,
340            file_id: self.file_id, // DevFS is always at root level, so parent is self
341        });
342
343        // Add actual child entries
344        for (name, child) in children.iter() {
345            entries.push(DirectoryEntryInternal {
346                name: name.clone(),
347                file_type: child.file_type.clone(),
348                file_id: child.file_id,
349            });
350        }
351
352        Ok(entries)
353    }
354
355    /// Open the node as a file object
356    pub fn open(&self) -> Result<Arc<dyn FileObject>, FileSystemError> {
357        match self.file_type {
358            FileType::CharDevice(device_info) | FileType::BlockDevice(device_info) => {
359                // Create a device file object that can handle device operations
360                Ok(Arc::new(DevFileObject::new(
361                    Arc::new(self.clone()),
362                    device_info.device_id,
363                    device_info.device_type,
364                )?))
365            }
366            FileType::Directory => {
367                // Create a directory file object that can handle directory operations
368                Ok(Arc::new(DevDirectoryObject::new(Arc::new(self.clone()))))
369            }
370            _ => Err(FileSystemError::new(
371                FileSystemErrorKind::NotSupported,
372                "Unsupported file type in devfs",
373            )),
374        }
375    }
376}
377
378impl VfsNode for DevNode {
379    fn id(&self) -> u64 {
380        self.file_id
381    }
382
383    fn metadata(&self) -> Result<FileMetadata, FileSystemError> {
384        Ok(FileMetadata {
385            file_type: self.file_type.clone(),
386            size: 0, // Device files have no size
387            permissions: FilePermission {
388                read: true,
389                write: true,
390                execute: false,
391            },
392            created_time: 0,
393            modified_time: 0,
394            accessed_time: 0,
395            file_id: self.file_id,
396            link_count: 1,
397        })
398    }
399
400    fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>> {
401        self.filesystem.read().clone()
402    }
403
404    fn as_any(&self) -> &dyn Any {
405        self
406    }
407}
408
409/// DevFS filesystem driver
410pub struct DevFSDriver;
411
412/// A file object for device files in DevFS
413///
414/// This struct provides a FileObject implementation that delegates
415/// device operations to the underlying device registered in DeviceManager.
416pub struct DevFileObject {
417    /// Reference to the DevNode
418    node: Arc<DevNode>,
419    /// Current file position (for seekable devices)
420    position: RwLock<u64>,
421    /// Device ID for lookup in DeviceManager
422    #[allow(dead_code)]
423    device_id: usize,
424    /// Device type
425    #[allow(dead_code)]
426    device_type: DeviceType,
427    /// Optional device guard for device files
428    device_guard: Option<Arc<dyn Device>>,
429}
430
431impl DevFileObject {
432    /// Create a new file object for device files
433    pub fn new(
434        node: Arc<DevNode>,
435        device_id: usize,
436        device_type: DeviceType,
437    ) -> Result<Self, FileSystemError> {
438        // Try to get the device from DeviceManager by ID
439        match DeviceManager::get_manager().get_device(device_id) {
440            Some(device_guard) => Ok(Self {
441                node,
442                position: RwLock::new(0),
443                device_id,
444                device_type,
445                device_guard: Some(device_guard),
446            }),
447            None => Err(FileSystemError::new(
448                FileSystemErrorKind::DeviceError,
449                format!("Device with ID {} not found in DeviceManager", device_id),
450            )),
451        }
452    }
453
454    /// Read from the underlying device at current position
455    fn read_device(&self, buffer: &mut [u8]) -> Result<usize, FileSystemError> {
456        if let Some(ref device_guard) = self.device_guard {
457            let device_guard_ref = device_guard.as_ref();
458            let position = *self.position.read();
459
460            match device_guard_ref.device_type() {
461                DeviceType::Char => {
462                    if let Some(char_device) = device_guard_ref.as_char_device() {
463                        // Use read_at for position-based read
464                        match char_device.read_at(position, buffer) {
465                            Ok(bytes_read) => {
466                                // Update position after successful read
467                                *self.position.write() += bytes_read as u64;
468                                Ok(bytes_read)
469                            }
470                            Err(e) => Err(FileSystemError::new(
471                                FileSystemErrorKind::IoError,
472                                format!("Character device read failed: {}", e),
473                            )),
474                        }
475                    } else {
476                        return Err(FileSystemError::new(
477                            FileSystemErrorKind::DeviceError,
478                            "Device does not support character operations",
479                        ));
480                    }
481                }
482                DeviceType::Block => {
483                    if let Some(block_device) = device_guard_ref.as_block_device() {
484                        // For block devices, we read sectors using the request system
485                        let request = Box::new(crate::device::block::request::BlockIORequest {
486                            request_type: crate::device::block::request::BlockIORequestType::Read,
487                            sector: 0,
488                            sector_count: 1,
489                            head: 0,
490                            cylinder: 0,
491                            buffer: buffer.to_vec(),
492                        });
493
494                        block_device.enqueue_request(request);
495                        let results = block_device.process_requests();
496
497                        if let Some(result) = results.first() {
498                            match &result.result {
499                                Ok(_) => {
500                                    // Copy the data back to the buffer
501                                    let bytes_to_copy =
502                                        core::cmp::min(buffer.len(), result.request.buffer.len());
503                                    buffer[..bytes_to_copy]
504                                        .copy_from_slice(&result.request.buffer[..bytes_to_copy]);
505                                    return Ok(bytes_to_copy);
506                                }
507                                Err(e) => {
508                                    return Err(FileSystemError::new(
509                                        FileSystemErrorKind::IoError,
510                                        format!("Block device read failed: {}", e),
511                                    ));
512                                }
513                            }
514                        }
515                        return Ok(0);
516                    } else {
517                        return Err(FileSystemError::new(
518                            FileSystemErrorKind::DeviceError,
519                            "Device does not support block operations",
520                        ));
521                    }
522                }
523                _ => {
524                    return Err(FileSystemError::new(
525                        FileSystemErrorKind::DeviceError,
526                        "Unsupported device type",
527                    ));
528                }
529            }
530        } else {
531            Err(FileSystemError::new(
532                FileSystemErrorKind::DeviceError,
533                "No device guard available",
534            ))
535        }
536    }
537
538    /// Write to the underlying device at current position
539    fn write_device(&self, buffer: &[u8]) -> Result<usize, FileSystemError> {
540        if let Some(ref device_guard) = self.device_guard {
541            let device_guard_ref = device_guard.as_ref();
542            let position = *self.position.read();
543
544            match device_guard_ref.device_type() {
545                DeviceType::Char => {
546                    if let Some(char_device) = device_guard_ref.as_char_device() {
547                        // Use write_at for position-based write
548                        match char_device.write_at(position, buffer) {
549                            Ok(bytes_written) => {
550                                // Update position after successful write
551                                *self.position.write() += bytes_written as u64;
552                                Ok(bytes_written)
553                            }
554                            Err(e) => Err(FileSystemError::new(
555                                FileSystemErrorKind::IoError,
556                                format!("Character device write failed: {}", e),
557                            )),
558                        }
559                    } else {
560                        return Err(FileSystemError::new(
561                            FileSystemErrorKind::DeviceError,
562                            "Device does not support character operations",
563                        ));
564                    }
565                }
566                DeviceType::Block => {
567                    if let Some(block_device) = device_guard_ref.as_block_device() {
568                        let request = Box::new(crate::device::block::request::BlockIORequest {
569                            request_type: crate::device::block::request::BlockIORequestType::Write,
570                            sector: 0,
571                            sector_count: 1,
572                            head: 0,
573                            cylinder: 0,
574                            buffer: buffer.to_vec(),
575                        });
576
577                        block_device.enqueue_request(request);
578                        let results = block_device.process_requests();
579
580                        if let Some(result) = results.first() {
581                            match &result.result {
582                                Ok(_) => return Ok(buffer.len()),
583                                Err(e) => {
584                                    return Err(FileSystemError::new(
585                                        FileSystemErrorKind::IoError,
586                                        format!("Block device write failed: {}", e),
587                                    ));
588                                }
589                            }
590                        }
591                        return Ok(0);
592                    } else {
593                        return Err(FileSystemError::new(
594                            FileSystemErrorKind::DeviceError,
595                            "Device does not support block operations",
596                        ));
597                    }
598                }
599                _ => {
600                    return Err(FileSystemError::new(
601                        FileSystemErrorKind::DeviceError,
602                        "Unsupported device type",
603                    ));
604                }
605            }
606        } else {
607            Err(FileSystemError::new(
608                FileSystemErrorKind::DeviceError,
609                "No device guard available",
610            ))
611        }
612    }
613}
614
615impl StreamOps for DevFileObject {
616    fn read(&self, buffer: &mut [u8]) -> Result<usize, StreamError> {
617        self.read_device(buffer).map_err(StreamError::from)
618    }
619
620    fn write(&self, buffer: &[u8]) -> Result<usize, StreamError> {
621        self.write_device(buffer).map_err(StreamError::from)
622    }
623}
624
625impl ControlOps for DevFileObject {
626    fn control(&self, command: u32, arg: usize) -> Result<i32, &'static str> {
627        // For device files, delegate control operations to the underlying device
628        if let Some(ref device_guard) = self.device_guard {
629            let device_guard_ref = device_guard.as_ref();
630            // Device trait now inherits from ControlOps, so we can delegate directly
631            device_guard_ref.control(command, arg)
632        } else {
633            Err("No device available for control operations")
634        }
635    }
636
637    fn supported_control_commands(&self) -> alloc::vec::Vec<(u32, &'static str)> {
638        // For device files, delegate to the underlying device
639        if let Some(ref device_guard) = self.device_guard {
640            let device_guard_ref = device_guard.as_ref();
641            device_guard_ref.supported_control_commands()
642        } else {
643            alloc::vec![]
644        }
645    }
646}
647
648impl MemoryMappingOps for DevFileObject {
649    fn get_mapping_info(
650        &self,
651        offset: usize,
652        length: usize,
653    ) -> Result<(usize, usize, bool), &'static str> {
654        // For device files, delegate to the underlying device if it supports memory mapping
655        if let Some(ref device_guard) = self.device_guard {
656            let device_guard_ref = device_guard.as_ref();
657            device_guard_ref.get_mapping_info(offset, length)
658        } else {
659            Err("No device associated with this DevFileObject")
660        }
661    }
662
663    fn on_mapped(&self, vaddr: usize, paddr: usize, length: usize, offset: usize) {
664        if let Some(ref device_guard) = self.device_guard {
665            let device_guard_ref = device_guard.as_ref();
666            device_guard_ref.on_mapped(vaddr, paddr, length, offset);
667        }
668    }
669
670    fn on_unmapped(&self, vaddr: usize, length: usize) {
671        if let Some(ref device_guard) = self.device_guard {
672            let device_guard_ref = device_guard.as_ref();
673            device_guard_ref.on_unmapped(vaddr, length);
674        }
675    }
676
677    fn supports_mmap(&self) -> bool {
678        if let Some(ref device_guard) = self.device_guard {
679            let device_guard_ref = device_guard.as_ref();
680            device_guard_ref.supports_mmap()
681        } else {
682            false
683        }
684    }
685}
686
687impl FileObject for DevFileObject {
688    fn seek(&self, whence: SeekFrom) -> Result<u64, StreamError> {
689        let mut position = self.position.write();
690
691        let new_pos = match whence {
692            SeekFrom::Start(offset) => offset,
693            SeekFrom::Current(offset) => {
694                if offset >= 0 {
695                    *position + offset as u64
696                } else {
697                    position.saturating_sub((-offset) as u64)
698                }
699            }
700            SeekFrom::End(offset) => {
701                // For devices, we can't easily determine the "end" position
702                // Most devices don't have a fixed size, so seeking from end is not meaningful
703                // We'll treat this as an error for now
704                return Err(StreamError::from(FileSystemError::new(
705                    FileSystemErrorKind::NotSupported,
706                    "Seek from end not supported for device files",
707                )));
708            }
709        };
710
711        *position = new_pos;
712        Ok(new_pos)
713    }
714
715    fn metadata(&self) -> Result<FileMetadata, StreamError> {
716        self.node.metadata().map_err(StreamError::from)
717    }
718
719    fn truncate(&self, _size: u64) -> Result<(), StreamError> {
720        // Device files cannot be truncated
721        Err(StreamError::from(FileSystemError::new(
722            FileSystemErrorKind::NotSupported,
723            "Cannot truncate device files",
724        )))
725    }
726
727    fn as_any(&self) -> &dyn Any {
728        self
729    }
730}
731
732impl crate::object::capability::selectable::Selectable for DevFileObject {
733    fn current_ready(
734        &self,
735        interest: crate::object::capability::selectable::ReadyInterest,
736    ) -> crate::object::capability::selectable::ReadySet {
737        // Delegate to underlying Device's Selectable; fallback to default
738        if let Some(ref device_guard) = self.device_guard {
739            return device_guard.as_ref().current_ready(interest);
740        }
741        crate::object::capability::selectable::Selectable::current_ready(self, interest)
742    }
743
744    fn wait_until_ready(
745        &self,
746        interest: crate::object::capability::selectable::ReadyInterest,
747        trapframe: &mut crate::arch::Trapframe,
748        timeout_ticks: Option<u64>,
749    ) -> crate::object::capability::selectable::SelectWaitOutcome {
750        if let Some(ref device_guard) = self.device_guard {
751            return device_guard
752                .as_ref()
753                .wait_until_ready(interest, trapframe, timeout_ticks);
754        }
755        let _ = (interest, trapframe, timeout_ticks);
756        crate::object::capability::selectable::SelectWaitOutcome::Ready
757    }
758
759    fn set_nonblocking(&self, enabled: bool) {
760        if let Some(ref device_guard) = self.device_guard {
761            device_guard.as_ref().set_nonblocking(enabled);
762        }
763    }
764
765    fn is_nonblocking(&self) -> bool {
766        if let Some(ref device_guard) = self.device_guard {
767            return device_guard.as_ref().is_nonblocking();
768        }
769        crate::object::capability::selectable::Selectable::is_nonblocking(self)
770    }
771}
772
773/// A file object for directories in DevFS
774///
775/// This struct provides a FileObject implementation for directories
776/// that allows reading directory entries as binary DirectoryEntry data.
777pub struct DevDirectoryObject {
778    /// Reference to the DevNode
779    node: Arc<DevNode>,
780    /// Current position in directory entries (entry index)
781    position: RwLock<usize>,
782}
783
784impl DevDirectoryObject {
785    /// Create a new directory file object
786    pub fn new(node: Arc<DevNode>) -> Self {
787        Self {
788            node,
789            position: RwLock::new(0),
790        }
791    }
792}
793
794impl StreamOps for DevDirectoryObject {
795    fn read(&self, buffer: &mut [u8]) -> Result<usize, StreamError> {
796        // Get all directory entries
797        let entries = self.node.readdir().map_err(StreamError::from)?;
798        let position = *self.position.read();
799
800        if position >= entries.len() {
801            return Ok(0); // EOF
802        }
803
804        // Convert the entry at current position to DirectoryEntry format
805        let internal_entry = &entries[position];
806
807        // Create DirectoryEntryInternal with size field for DirectoryEntry::from_internal
808        let internal_with_size = crate::fs::DirectoryEntryInternal {
809            name: internal_entry.name.clone(),
810            file_type: internal_entry.file_type.clone(),
811            size: 0, // Device files have no meaningful size
812            file_id: internal_entry.file_id,
813            metadata: None,
814        };
815
816        let dir_entry = crate::fs::DirectoryEntry::from_internal(&internal_with_size);
817        let entry_size = dir_entry.entry_size();
818
819        if buffer.len() < entry_size {
820            return Err(StreamError::InvalidArgument); // Buffer too small
821        }
822
823        // Copy entry as bytes
824        let entry_bytes =
825            unsafe { core::slice::from_raw_parts(&dir_entry as *const _ as *const u8, entry_size) };
826
827        buffer[..entry_size].copy_from_slice(entry_bytes);
828
829        // Move to next entry
830        *self.position.write() += 1;
831
832        Ok(entry_size)
833    }
834
835    fn write(&self, _buffer: &[u8]) -> Result<usize, StreamError> {
836        Err(StreamError::from(FileSystemError::new(
837            FileSystemErrorKind::ReadOnly,
838            "Cannot write to directory in devfs",
839        )))
840    }
841}
842
843impl ControlOps for DevDirectoryObject {
844    // Directory objects don't support control operations by default
845    fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
846        Err("Control operations not supported on directories")
847    }
848}
849
850impl MemoryMappingOps for DevDirectoryObject {
851    fn get_mapping_info(
852        &self,
853        _offset: usize,
854        _length: usize,
855    ) -> Result<(usize, usize, bool), &'static str> {
856        Err("Memory mapping not supported for directories")
857    }
858
859    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
860        // Directories don't support memory mapping
861    }
862
863    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
864        // Directories don't support memory mapping
865    }
866
867    fn supports_mmap(&self) -> bool {
868        false
869    }
870}
871
872impl FileObject for DevDirectoryObject {
873    fn seek(&self, whence: SeekFrom) -> Result<u64, StreamError> {
874        // Get directory entries to know the count
875        let entries = self.node.readdir().map_err(StreamError::from)?;
876        let entry_count = entries.len() as u64;
877
878        let mut position = self.position.write();
879
880        let new_pos = match whence {
881            SeekFrom::Start(offset) => offset,
882            SeekFrom::Current(offset) => {
883                if offset >= 0 {
884                    *position as u64 + offset as u64
885                } else {
886                    (*position as u64).saturating_sub((-offset) as u64)
887                }
888            }
889            SeekFrom::End(offset) => {
890                if offset >= 0 {
891                    entry_count + offset as u64
892                } else {
893                    entry_count.saturating_sub((-offset) as u64)
894                }
895            }
896        };
897
898        *position = new_pos as usize;
899        Ok(new_pos)
900    }
901
902    fn metadata(&self) -> Result<FileMetadata, StreamError> {
903        self.node.metadata().map_err(StreamError::from)
904    }
905
906    fn truncate(&self, _size: u64) -> Result<(), StreamError> {
907        Err(StreamError::from(FileSystemError::new(
908            FileSystemErrorKind::ReadOnly,
909            "Cannot truncate directory in devfs",
910        )))
911    }
912
913    fn as_any(&self) -> &dyn Any {
914        self
915    }
916}
917
918impl crate::object::capability::selectable::Selectable for DevDirectoryObject {
919    fn current_ready(
920        &self,
921        interest: crate::object::capability::selectable::ReadyInterest,
922    ) -> crate::object::capability::selectable::ReadySet {
923        let mut set = crate::object::capability::selectable::ReadySet::none();
924        if interest.read {
925            set.read = true;
926        }
927        if interest.write {
928            set.write = true;
929        }
930        if interest.except {
931            set.except = false;
932        }
933        set
934    }
935
936    fn wait_until_ready(
937        &self,
938        _interest: crate::object::capability::selectable::ReadyInterest,
939        _trapframe: &mut crate::arch::Trapframe,
940        _timeout_ticks: Option<u64>,
941    ) -> crate::object::capability::selectable::SelectWaitOutcome {
942        crate::object::capability::selectable::SelectWaitOutcome::Ready
943    }
944
945    fn is_nonblocking(&self) -> bool {
946        true
947    }
948}
949
950impl FileSystemDriver for DevFSDriver {
951    fn name(&self) -> &'static str {
952        "devfs"
953    }
954
955    fn filesystem_type(&self) -> FileSystemType {
956        FileSystemType::Device
957    }
958
959    fn create(&self) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
960        Ok(DevFS::new() as Arc<dyn FileSystemOperations>)
961    }
962
963    fn create_from_option_string(
964        &self,
965        _options: &str,
966    ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
967        // DevFS doesn't use options, just create a new instance
968        self.create()
969    }
970}
971
972/// Register the DevFS driver with the filesystem driver manager
973fn register_driver() {
974    let fs_driver_manager = get_fs_driver_manager();
975    fs_driver_manager.register_driver(Box::new(DevFSDriver));
976}
977
978driver_initcall!(register_driver);
979
980#[cfg(test)]
981mod tests {
982    use super::*;
983    use crate::device::{GenericDevice, manager::DeviceManager};
984    use alloc::sync::Arc;
985
986    #[test_case]
987    fn test_devfs_creation() {
988        let devfs = DevFS::new();
989        assert_eq!(devfs.name(), "devfs");
990    }
991
992    #[test_case]
993    fn test_devfs_root_access() {
994        let devfs = DevFS::new();
995        let root = devfs.root_node();
996        assert_eq!(root.id(), 0);
997
998        let metadata = root.metadata().unwrap();
999        assert_eq!(metadata.file_type, FileType::Directory);
1000    }
1001
1002    #[test_case]
1003    fn test_devfs_device_discovery() {
1004        // Register a test device
1005        let device_manager = DeviceManager::get_manager();
1006        let test_device = Arc::new(GenericDevice::new("test_devfs_device"));
1007        let _device_id =
1008            device_manager.register_device_with_name("test_devfs_device".to_string(), test_device);
1009
1010        let devfs = DevFS::new();
1011        let root = devfs.root_node();
1012
1013        // Check if we can read the directory (this should trigger device population)
1014        let entries = devfs.readdir(&root).unwrap();
1015
1016        // Note: Generic devices are not currently exposed in devfs (only Char/Block devices)
1017        // So this test checks that the readdir operation works without error
1018        assert!(
1019            entries.len() == 0 || entries.len() > 0,
1020            "DevFS readdir should work without error"
1021        );
1022    }
1023
1024    #[test_case]
1025    fn test_devfs_lookup() {
1026        let devfs = DevFS::new();
1027        let root = devfs.root_node();
1028
1029        // Try to lookup a non-existent device
1030        let result = devfs.lookup(&root, &"nonexistent_device".to_string());
1031        assert!(result.is_err(), "Lookup of non-existent device should fail");
1032
1033        let error = result.unwrap_err();
1034        assert_eq!(error.kind, FileSystemErrorKind::NotFound);
1035    }
1036
1037    #[test_case]
1038    fn test_devfs_with_real_devices() {
1039        use crate::device::block::mockblk::MockBlockDevice;
1040        use crate::device::char::mockchar::MockCharDevice;
1041
1042        // Register actual character and block devices
1043        let device_manager = DeviceManager::get_manager();
1044
1045        // Register a character device (TTY-like)
1046        let char_device = Arc::new(MockCharDevice::new("tty0"));
1047        let _char_device_id =
1048            device_manager.register_device_with_name("tty0".to_string(), char_device.clone());
1049
1050        // Register a block device (disk-like)
1051        let block_device = Arc::new(MockBlockDevice::new("sda", 512, 1000));
1052        let _block_device_id =
1053            device_manager.register_device_with_name("sda".to_string(), block_device.clone());
1054
1055        // Create devfs and verify devices appear
1056        let devfs = DevFS::new();
1057        let root = devfs.root_node();
1058
1059        // Read directory contents
1060        let entries = devfs.readdir(&root).unwrap();
1061
1062        // Debug: print all entries to see what we actually have
1063        let _unused_entries = &entries;
1064
1065        // Should contain both our devices
1066        let has_tty0 = entries.iter().any(|entry| {
1067            entry.name == "tty0" && matches!(entry.file_type, FileType::CharDevice(_))
1068        });
1069        let has_sda = entries.iter().any(|entry| {
1070            entry.name == "sda" && matches!(entry.file_type, FileType::BlockDevice(_))
1071        });
1072
1073        assert!(has_tty0, "DevFS should contain tty0 character device");
1074        assert!(has_sda, "DevFS should contain sda block device");
1075
1076        // Test lookup functionality for character device
1077        let tty0_result = devfs.lookup(&root, &"tty0".to_string());
1078        assert!(tty0_result.is_ok(), "Should be able to lookup tty0");
1079
1080        // Test lookup functionality for block device
1081        let sda_result = devfs.lookup(&root, &"sda".to_string());
1082        assert!(sda_result.is_ok(), "Should be able to lookup sda");
1083    }
1084
1085    #[test_case]
1086    fn test_devfs_driver_registration() {
1087        // Test that the DevFS driver is properly registered
1088        let fs_driver_manager = get_fs_driver_manager();
1089
1090        // Check if devfs driver is registered
1091        assert!(
1092            fs_driver_manager.has_driver("devfs"),
1093            "DevFS driver should be registered"
1094        );
1095
1096        // Check driver type
1097        let driver_type = fs_driver_manager.get_driver_type("devfs");
1098        assert_eq!(driver_type, Some(FileSystemType::Device));
1099
1100        // Test creating a devfs instance through the driver manager
1101        let devfs_result = fs_driver_manager.create_from_option_string("devfs", "");
1102        assert!(
1103            devfs_result.is_ok(),
1104            "Should be able to create DevFS through driver manager"
1105        );
1106
1107        let devfs_instance = devfs_result.unwrap();
1108        assert_eq!(devfs_instance.name(), "devfs");
1109        assert!(devfs_instance.is_read_only());
1110    }
1111
1112    #[test_case]
1113    fn test_devfs_readonly_operations() {
1114        let devfs = DevFS::new();
1115        let root = devfs.root_node();
1116
1117        // Test that create operations fail
1118        let create_result = devfs.create(&root, &"test_file".to_string(), FileType::RegularFile, 0);
1119        assert!(create_result.is_err());
1120        assert_eq!(
1121            create_result.unwrap_err().kind,
1122            FileSystemErrorKind::ReadOnly
1123        );
1124
1125        // Test that remove operations fail
1126        let remove_result = devfs.remove(&root, &"test_file".to_string());
1127        assert!(remove_result.is_err());
1128        assert_eq!(
1129            remove_result.unwrap_err().kind,
1130            FileSystemErrorKind::ReadOnly
1131        );
1132    }
1133
1134    #[test_case]
1135    fn test_devfs_device_file_operations() {
1136        use crate::device::char::mockchar::MockCharDevice;
1137
1138        // Register a character device for testing
1139        let device_manager = DeviceManager::get_manager();
1140        let char_device = Arc::new(MockCharDevice::new("test_char_dev"));
1141        let _device_id = device_manager
1142            .register_device_with_name("test_char_dev".to_string(), char_device.clone());
1143
1144        // Create devfs and lookup the device
1145        let devfs = DevFS::new();
1146        let root = devfs.root_node();
1147
1148        let device_node_result = devfs.lookup(&root, &"test_char_dev".to_string());
1149        assert!(
1150            device_node_result.is_ok(),
1151            "Should be able to lookup character device"
1152        );
1153
1154        let device_node = device_node_result.unwrap();
1155
1156        // Test opening the device file
1157        let file_result = devfs.open(&device_node, 0);
1158        assert!(
1159            file_result.is_ok(),
1160            "Should be able to open character device file"
1161        );
1162
1163        let file_obj = file_result.unwrap();
1164
1165        // Test basic FileObject operations
1166        let metadata_result = file_obj.metadata();
1167        assert!(
1168            metadata_result.is_ok(),
1169            "Should be able to get device file metadata"
1170        );
1171
1172        // Test read operation (should work even if device returns no data)
1173        let mut read_buffer = [0u8; 10];
1174        let read_result = file_obj.read(&mut read_buffer);
1175        assert!(read_result.is_ok(), "Read operation should succeed");
1176
1177        // Test write operation
1178        let write_data = b"test";
1179        let write_result = file_obj.write(write_data);
1180        assert!(write_result.is_ok(), "Write operation should succeed");
1181
1182        // Test seek operation
1183        let seek_result = file_obj.seek(crate::fs::SeekFrom::Start(0));
1184        assert!(seek_result.is_ok(), "Seek operation should succeed");
1185
1186        // Test truncate operation (should fail for device files)
1187        let truncate_result = file_obj.truncate(100);
1188        assert!(
1189            truncate_result.is_err(),
1190            "Truncate should fail for device files"
1191        );
1192    }
1193
1194    #[test_case]
1195    fn test_devfs_directory_operations() {
1196        use crate::device::char::mockchar::MockCharDevice;
1197
1198        // Register a character device for testing
1199        let device_manager = DeviceManager::get_manager();
1200        let char_device = Arc::new(MockCharDevice::new("test_dir_ops"));
1201        let _device_id = device_manager
1202            .register_device_with_name("test_dir_ops".to_string(), char_device.clone());
1203
1204        // Create devfs
1205        let devfs = DevFS::new();
1206        let root = devfs.root_node();
1207
1208        // Test opening directory
1209        let dir_file_result = devfs.open(&root, 0);
1210        assert!(
1211            dir_file_result.is_ok(),
1212            "Should be able to open directory in devfs"
1213        );
1214
1215        let dir_file = dir_file_result.unwrap();
1216
1217        // Test reading directory contents (should return DirectoryEntry structs)
1218        let mut read_buffer = [0u8; 512]; // Buffer for one directory entry
1219        let read_result = dir_file.read(&mut read_buffer);
1220        assert!(
1221            read_result.is_ok(),
1222            "Should be able to read directory contents"
1223        );
1224
1225        let bytes_read = read_result.unwrap();
1226        assert!(bytes_read > 0, "Should read some directory data");
1227
1228        // Parse the directory entry
1229        let dir_entry = crate::fs::DirectoryEntry::parse(&read_buffer[..bytes_read]);
1230        assert!(
1231            dir_entry.is_some(),
1232            "Should be able to parse directory entry"
1233        );
1234
1235        let entry = dir_entry.unwrap();
1236        let name = entry.name_str().unwrap();
1237
1238        // Should be either "." or ".." or our test device
1239        assert!(
1240            name == "." || name == ".." || name == "test_dir_ops",
1241            "Entry name should be '.', '..' or 'test_dir_ops', got: {}",
1242            name
1243        );
1244
1245        // Test seek operations
1246        let seek_result = dir_file.seek(SeekFrom::Start(0));
1247        assert!(seek_result.is_ok(), "Should be able to seek in directory");
1248
1249        // Test metadata
1250        let metadata_result = dir_file.metadata();
1251        assert!(
1252            metadata_result.is_ok(),
1253            "Should be able to get directory metadata"
1254        );
1255
1256        let metadata = metadata_result.unwrap();
1257        assert_eq!(
1258            metadata.file_type,
1259            FileType::Directory,
1260            "Should be directory type"
1261        );
1262    }
1263}