kernel/fs/vfs_v2/drivers/
cpiofs.rs

1//! CpioFS v2 - CPIO filesystem implementation for initramfs
2//!
3//! This is a simplified read-only filesystem for handling CPIO archives
4//! used as initramfs. It implements the VFS v2 architecture.
5
6use alloc::sync::Weak;
7use alloc::{
8    boxed::Box,
9    collections::BTreeMap,
10    format,
11    string::{String, ToString},
12    sync::Arc,
13    vec::Vec,
14};
15use core::any::Any;
16use spin::RwLock;
17
18use crate::fs::{
19    FileMetadata, FileObject, FilePermission, FileSystemError, FileSystemErrorKind, FileType,
20};
21use crate::object::capability::{ControlOps, StreamError, StreamOps};
22use crate::{
23    driver_initcall,
24    fs::{
25        FileSystemDriver, FileSystemType,
26        core::DirectoryEntryInternal,
27        get_fs_driver_manager,
28        vfs_v2::core::{FileSystemId, FileSystemOperations, VfsNode},
29    },
30    object::capability::MemoryMappingOps,
31    vm::vmem::MemoryArea,
32};
33
34/// CPIO filesystem implementation
35pub struct CpioFS {
36    /// Unique filesystem identifier
37    fs_id: FileSystemId,
38    /// Root node of the filesystem
39    root_node: Arc<CpioNode>,
40
41    /// Filesystem name
42    name: String,
43}
44
45/// A single node in the CPIO filesystem
46pub struct CpioNode {
47    /// File name
48    name: String,
49
50    /// File type
51    file_type: FileType,
52
53    /// File content (for regular files)
54    content: Vec<u8>,
55
56    /// Child nodes (for directories)
57    children: RwLock<BTreeMap<String, Arc<CpioNode>>>,
58
59    /// Reference to filesystem
60    filesystem: RwLock<Option<Arc<CpioFS>>>,
61
62    /// File ID
63    file_id: usize,
64
65    /// Parent node (weak reference)
66    parent: RwLock<Option<Weak<CpioNode>>>,
67}
68
69impl CpioNode {
70    /// Create a new CPIO node
71    pub fn new(name: String, file_type: FileType, content: Vec<u8>, file_id: usize) -> Arc<Self> {
72        Arc::new(Self {
73            name,
74            file_type,
75            content,
76            children: RwLock::new(BTreeMap::new()),
77            filesystem: RwLock::new(None),
78            file_id,
79            parent: RwLock::new(None),
80        })
81    }
82
83    /// Add a child to this directory node
84    pub fn add_child(
85        self: &Arc<Self>,
86        name: String,
87        child: Arc<CpioNode>,
88    ) -> Result<(), FileSystemError> {
89        if self.file_type != FileType::Directory {
90            return Err(FileSystemError::new(
91                FileSystemErrorKind::NotADirectory,
92                "Cannot add child to non-directory node",
93            ));
94        }
95        // Set parent pointer
96        *child.parent.write() = Some(Arc::downgrade(self));
97        let mut children = self.children.write();
98        children.insert(name, child);
99        Ok(())
100    }
101
102    /// Get a child by name
103    pub fn get_child(&self, name: &str) -> Option<Arc<CpioNode>> {
104        let children = self.children.read();
105        children.get(name).cloned()
106    }
107
108    pub fn parent_file_id(&self) -> Option<u64> {
109        self.parent
110            .read()
111            .as_ref()?
112            .upgrade()
113            .map(|p| p.file_id as u64)
114    }
115
116    /// Helper to convert from Arc<dyn VfsNode> to Arc<CpioNode>
117    pub fn from_vfsnode_arc(node: &Arc<dyn VfsNode>) -> Option<Arc<CpioNode>> {
118        match Arc::downcast::<CpioNode>(node.clone()) {
119            Ok(cpio_node) => Some(cpio_node),
120            Err(_) => None,
121        }
122    }
123}
124
125impl VfsNode for CpioNode {
126    fn id(&self) -> u64 {
127        self.file_id as u64
128    }
129
130    fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>> {
131        let fs_guard = self.filesystem.read();
132        fs_guard
133            .as_ref()
134            .map(|fs| Arc::downgrade(fs) as Weak<dyn FileSystemOperations>)
135    }
136
137    fn metadata(&self) -> Result<FileMetadata, FileSystemError> {
138        Ok(FileMetadata {
139            file_type: self.file_type.clone(),
140            size: self.content.len(),
141            created_time: 0,
142            modified_time: 0,
143            accessed_time: 0,
144            permissions: FilePermission {
145                read: true,
146                write: false,
147                execute: true,
148            },
149            file_id: self.file_id as u64,
150            link_count: 1,
151        })
152    }
153
154    fn as_any(&self) -> &dyn Any {
155        self
156    }
157
158    fn read_link(&self) -> Result<String, FileSystemError> {
159        // Check if this is actually a symbolic link and return target
160        match &self.file_type {
161            FileType::SymbolicLink(target) => Ok(target.clone()),
162            _ => Err(FileSystemError::new(
163                FileSystemErrorKind::NotSupported,
164                "Not a symbolic link",
165            )),
166        }
167    }
168}
169
170impl CpioFS {
171    /// Create a new CpioFS from CPIO archive data
172    pub fn new(name: String, cpio_data: &[u8]) -> Result<Arc<Self>, FileSystemError> {
173        let root_node = CpioNode::new("/".to_string(), FileType::Directory, Vec::new(), 1);
174        let filesystem = Arc::new(Self {
175            fs_id: FileSystemId::new(),
176            root_node: Arc::clone(&root_node),
177            name,
178        });
179        {
180            let mut fs_guard = root_node.filesystem.write();
181            *fs_guard = Some(Arc::clone(&filesystem));
182        }
183        // Parse CPIO data and build directory tree
184        filesystem.parse_cpio_archive(cpio_data)?;
185        Ok(filesystem)
186    }
187
188    /// VFS v2 driver registration API: create from option string
189    /// Example: option = Some("initramfs_addr=0x80000000,size=65536")
190    pub fn create_from_option_string(
191        option: Option<&str>,
192        cpio_data: &[u8],
193    ) -> Arc<dyn FileSystemOperations> {
194        // Name is fixed, cpio_data is assumed to be provided externally
195        let name = "cpiofs".to_string();
196        // Extend option parsing as needed
197        CpioFS::new(name, cpio_data).expect("Failed to create CpioFS")
198            as Arc<dyn FileSystemOperations>
199    }
200
201    /// Parse CPIO archive and build directory tree
202    fn parse_cpio_archive(self: &Arc<Self>, data: &[u8]) -> Result<(), FileSystemError> {
203        // CPIO new ASCII format: magic "070701"
204        let mut offset = 0;
205        let mut file_id = 2;
206        while offset + 110 <= data.len() {
207            // Parse header
208            let magic = &data[offset..offset + 6];
209            if magic != b"070701" {
210                break;
211            }
212            let _inode = match core::str::from_utf8(&data[offset + 6..offset + 14]) {
213                Ok(s) => u32::from_str_radix(s, 16).map_err(|_| {
214                    FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid inode value")
215                })?,
216                Err(_) => {
217                    return Err(FileSystemError::new(
218                        FileSystemErrorKind::InvalidData,
219                        "Invalid UTF-8 in inode field",
220                    ));
221                }
222            };
223            let mode = match core::str::from_utf8(&data[offset + 14..offset + 22]) {
224                Ok(s) => u32::from_str_radix(s, 16).map_err(|_| {
225                    FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid mode value")
226                })?,
227                Err(_) => {
228                    return Err(FileSystemError::new(
229                        FileSystemErrorKind::InvalidData,
230                        "Invalid UTF-8 in mode field",
231                    ));
232                }
233            };
234            let namesize = match core::str::from_utf8(&data[offset + 94..offset + 102]) {
235                Ok(s) => usize::from_str_radix(s, 16).map_err(|_| {
236                    FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid namesize value")
237                })?,
238                Err(_) => {
239                    return Err(FileSystemError::new(
240                        FileSystemErrorKind::InvalidData,
241                        "Invalid UTF-8 in namesize field",
242                    ));
243                }
244            };
245            let filesize = match core::str::from_utf8(&data[offset + 54..offset + 62]) {
246                Ok(s) => usize::from_str_radix(s, 16).map_err(|_| {
247                    FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid filesize value")
248                })?,
249                Err(_) => {
250                    return Err(FileSystemError::new(
251                        FileSystemErrorKind::InvalidData,
252                        "Invalid UTF-8 in filesize field",
253                    ));
254                }
255            };
256            let name_start = offset + 110;
257            let name_end = name_start + namesize;
258            if name_end > data.len() {
259                break;
260            }
261            let name = &data[name_start..name_end - 1]; // remove trailing NUL
262            let name_str = core::str::from_utf8(name).unwrap_or("").to_string();
263            let file_start = (name_end + 3) & !3; // 4-byte align
264            let file_end = file_start + filesize;
265            if file_end > data.len() {
266                break;
267            }
268            if name_str == "TRAILER!!!" {
269                break;
270            }
271
272            // Determine file type
273            let (file_type, content) = match mode & 0o170000 {
274                0o040000 => (FileType::Directory, Vec::new()),
275                0o100000 => (FileType::RegularFile, data[file_start..file_end].to_vec()),
276                0o120000 => {
277                    // For symbolic links, extract target path from content
278                    let target_bytes = data[file_start..file_end].to_vec();
279                    let target_path =
280                        String::from_utf8(target_bytes.clone()).unwrap_or_else(|_| String::new());
281                    (FileType::SymbolicLink(target_path), target_bytes)
282                }
283                _ => (FileType::RegularFile, data[file_start..file_end].to_vec()),
284            };
285            // Build node and insert into tree
286            let base_name = if let Some(pos) = name_str.rfind('/') {
287                &name_str[pos + 1..]
288            } else {
289                &name_str[..]
290            };
291
292            // Skip "." and ".." entries as they are handled automatically by the VFS
293            if base_name == "." || base_name == ".." {
294                offset = (file_end + 3) & !3;
295                continue;
296            }
297
298            let node = CpioNode::new(base_name.to_string(), file_type, content, file_id);
299            {
300                let mut fs_guard = node.filesystem.write();
301                *fs_guard = Some(Arc::clone(self));
302            }
303            file_id += 1;
304            // Insert into parent
305            let parent_path = if let Some(pos) = name_str.rfind('/') {
306                &name_str[..pos]
307            } else {
308                ""
309            };
310            let parent = if parent_path.is_empty() {
311                Arc::clone(&self.root_node)
312            } else {
313                // Traverse from root to find parent
314                let mut cur = Arc::clone(&self.root_node);
315                for part in parent_path.split('/') {
316                    if part.is_empty() {
317                        continue;
318                    }
319                    if let Some(child) = cur.get_child(part) {
320                        cur = child;
321                    } else {
322                        // Create intermediate directory if missing
323                        let dir = CpioNode::new(
324                            part.to_string(),
325                            FileType::Directory,
326                            Vec::new(),
327                            file_id,
328                        );
329                        {
330                            let mut fs_guard = dir.filesystem.write();
331                            *fs_guard = Some(Arc::clone(self));
332                        }
333                        file_id += 1;
334                        cur.add_child(part.to_string(), Arc::clone(&dir)).ok();
335                        cur = dir;
336                    }
337                }
338                cur
339            };
340            parent
341                .add_child(base_name.to_string(), Arc::clone(&node))
342                .ok();
343            offset = (file_end + 3) & !3;
344        }
345        Ok(())
346    }
347}
348
349impl FileSystemOperations for CpioFS {
350    fn fs_id(&self) -> FileSystemId {
351        self.fs_id
352    }
353
354    fn lookup(
355        &self,
356        parent_node: &Arc<dyn VfsNode>,
357        name: &String,
358    ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
359        // Downcast to CpioNode
360        let cpio_parent = parent_node
361            .as_any()
362            .downcast_ref::<CpioNode>()
363            .ok_or_else(|| {
364                FileSystemError::new(
365                    FileSystemErrorKind::NotSupported,
366                    "Invalid node type for CpioFS",
367                )
368            })?;
369
370        // Look up child
371        cpio_parent
372            .get_child(name)
373            .map(|n| n as Arc<dyn VfsNode>)
374            .ok_or_else(|| {
375                FileSystemError::new(
376                    FileSystemErrorKind::NotFound,
377                    format!("File not found: {} in {}", name, cpio_parent.name),
378                )
379            })
380    }
381
382    fn open(
383        &self,
384        node: &Arc<dyn VfsNode>,
385        _flags: u32,
386    ) -> Result<Arc<dyn FileObject>, FileSystemError> {
387        let cpio_node = node.as_any().downcast_ref::<CpioNode>().ok_or_else(|| {
388            FileSystemError::new(
389                FileSystemErrorKind::NotSupported,
390                "Invalid node type for CpioFS",
391            )
392        })?;
393
394        match cpio_node.file_type {
395            FileType::RegularFile => Ok(Arc::new(CpioFileObject::new(Arc::clone(node)))),
396            FileType::Directory => Ok(Arc::new(CpioDirectoryObject::new(Arc::clone(node)))),
397            FileType::SymbolicLink(_) => Ok(Arc::new(CpioSymlinkObject::new(Arc::clone(node)))),
398            _ => Err(FileSystemError::new(
399                FileSystemErrorKind::NotSupported,
400                "Unsupported file type",
401            )),
402        }
403    }
404
405    fn create(
406        &self,
407        _parent_node: &Arc<dyn VfsNode>,
408        _name: &String,
409        _file_type: FileType,
410        _mode: u32,
411    ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
412        Err(FileSystemError::new(
413            FileSystemErrorKind::ReadOnly,
414            "CPIO filesystem is read-only",
415        ))
416    }
417
418    fn remove(
419        &self,
420        _parent_node: &Arc<dyn VfsNode>,
421        _name: &String,
422    ) -> Result<(), FileSystemError> {
423        Err(FileSystemError::new(
424            FileSystemErrorKind::ReadOnly,
425            "CPIO filesystem is read-only",
426        ))
427    }
428
429    fn root_node(&self) -> Arc<dyn VfsNode> {
430        Arc::clone(&self.root_node) as Arc<dyn VfsNode>
431    }
432
433    fn name(&self) -> &str {
434        &self.name
435    }
436
437    fn is_read_only(&self) -> bool {
438        true
439    }
440
441    fn readdir(
442        &self,
443        node: &Arc<dyn VfsNode>,
444    ) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
445        let cpio_node = node.as_any().downcast_ref::<CpioNode>().ok_or_else(|| {
446            FileSystemError::new(
447                FileSystemErrorKind::NotSupported,
448                "Invalid node type for CpioFS",
449            )
450        })?;
451        if cpio_node.file_type != FileType::Directory {
452            return Err(FileSystemError::new(
453                FileSystemErrorKind::NotADirectory,
454                "Not a directory",
455            ));
456        }
457        let mut entries = Vec::new();
458
459        // Add "." and ".." entries
460        entries.push(DirectoryEntryInternal {
461            name: ".".to_string(),
462            file_type: FileType::Directory,
463            file_id: cpio_node.file_id as u64,
464        });
465
466        // .. entry should have the parent directory's file_id
467        let parent_file_id = cpio_node.parent_file_id().unwrap_or(0);
468        entries.push(DirectoryEntryInternal {
469            name: "..".to_string(),
470            file_type: FileType::Directory,
471            file_id: parent_file_id,
472        });
473
474        // Add children
475        let children = cpio_node.children.read();
476        for child in children.values() {
477            entries.push(DirectoryEntryInternal {
478                name: child.name.clone(),
479                file_type: child.file_type.clone(),
480                file_id: child.file_id as u64,
481            });
482        }
483
484        Ok(entries)
485    }
486
487    fn as_any(&self) -> &dyn Any {
488        self
489    }
490}
491
492/// File object for CPIO regular files
493pub struct CpioFileObject {
494    node: Arc<dyn VfsNode>,
495    position: RwLock<u64>,
496}
497
498impl CpioFileObject {
499    pub fn new(node: Arc<dyn VfsNode>) -> Self {
500        Self {
501            node,
502            position: RwLock::new(0),
503        }
504    }
505}
506
507impl StreamOps for CpioFileObject {
508    fn read(&self, buf: &mut [u8]) -> Result<usize, StreamError> {
509        let cpio_node = self
510            .node
511            .as_any()
512            .downcast_ref::<CpioNode>()
513            .ok_or(StreamError::IoError)?;
514
515        let mut pos = self.position.write();
516        let start = *pos as usize;
517        let end = (start + buf.len()).min(cpio_node.content.len());
518
519        if start >= cpio_node.content.len() {
520            return Ok(0); // EOF
521        }
522
523        let bytes_to_read = end - start;
524        buf[..bytes_to_read].copy_from_slice(&cpio_node.content[start..end]);
525        *pos += bytes_to_read as u64;
526
527        Ok(bytes_to_read)
528    }
529
530    fn write(&self, _buf: &[u8]) -> Result<usize, StreamError> {
531        Err(StreamError::PermissionDenied)
532    }
533}
534
535impl ControlOps for CpioFileObject {
536    // CPIO files are read-only and don't support control operations
537    fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
538        Err("Control operations not supported on CPIO files")
539    }
540}
541
542impl MemoryMappingOps for CpioFileObject {
543    fn get_mapping_info(
544        &self,
545        _offset: usize,
546        _length: usize,
547    ) -> Result<(usize, usize, bool), &'static str> {
548        Err("Memory mapping not supported for CPIO files")
549    }
550
551    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
552        // CPIO files don't support memory mapping
553    }
554
555    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
556        // CPIO files don't support memory mapping
557    }
558
559    fn supports_mmap(&self) -> bool {
560        false
561    }
562}
563
564impl FileObject for CpioFileObject {
565    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<usize, StreamError> {
566        let cpio_node = self
567            .node
568            .as_any()
569            .downcast_ref::<CpioNode>()
570            .ok_or(StreamError::IoError)?;
571
572        let offset = usize::try_from(offset).map_err(|_| StreamError::InvalidArgument)?;
573        if offset >= cpio_node.content.len() {
574            return Ok(0);
575        }
576
577        let end = core::cmp::min(offset + buffer.len(), cpio_node.content.len());
578        let bytes_to_read = end - offset;
579        buffer[..bytes_to_read].copy_from_slice(&cpio_node.content[offset..end]);
580
581        Ok(bytes_to_read)
582    }
583
584    fn write_at(&self, _offset: u64, _buffer: &[u8]) -> Result<usize, StreamError> {
585        Err(StreamError::PermissionDenied)
586    }
587
588    fn seek(&self, whence: crate::fs::SeekFrom) -> Result<u64, StreamError> {
589        let cpio_node = self
590            .node
591            .as_any()
592            .downcast_ref::<CpioNode>()
593            .ok_or(StreamError::IoError)?;
594
595        let mut pos = self.position.write();
596        let file_size = cpio_node.content.len() as u64;
597
598        let new_pos = match whence {
599            crate::fs::SeekFrom::Start(offset) => offset,
600            crate::fs::SeekFrom::Current(offset) => {
601                if offset >= 0 {
602                    *pos + offset as u64
603                } else {
604                    pos.saturating_sub((-offset) as u64)
605                }
606            }
607            crate::fs::SeekFrom::End(offset) => {
608                if offset >= 0 {
609                    file_size + offset as u64
610                } else {
611                    file_size.saturating_sub((-offset) as u64)
612                }
613            }
614        };
615
616        *pos = new_pos.min(file_size);
617        Ok(*pos)
618    }
619
620    fn metadata(&self) -> Result<crate::fs::FileMetadata, StreamError> {
621        self.node.metadata().map_err(StreamError::from)
622    }
623
624    fn truncate(&self, _size: u64) -> Result<(), StreamError> {
625        Err(StreamError::PermissionDenied)
626    }
627
628    fn as_any(&self) -> &dyn Any {
629        self
630    }
631}
632
633impl crate::object::capability::selectable::Selectable for CpioFileObject {
634    fn current_ready(
635        &self,
636        interest: crate::object::capability::selectable::ReadyInterest,
637    ) -> crate::object::capability::selectable::ReadySet {
638        let mut set = crate::object::capability::selectable::ReadySet::none();
639        if interest.read {
640            set.read = true;
641        }
642        if interest.write {
643            set.write = true;
644        }
645        if interest.except {
646            set.except = false;
647        }
648        set
649    }
650
651    fn wait_until_ready(
652        &self,
653        _interest: crate::object::capability::selectable::ReadyInterest,
654        _trapframe: &mut crate::arch::Trapframe,
655        _timeout_ticks: Option<u64>,
656    ) -> crate::object::capability::selectable::SelectWaitOutcome {
657        crate::object::capability::selectable::SelectWaitOutcome::Ready
658    }
659
660    fn is_nonblocking(&self) -> bool {
661        true
662    }
663}
664
665/// Directory object for CPIO directories
666pub struct CpioDirectoryObject {
667    node: Arc<dyn VfsNode>,
668    position: RwLock<u64>,
669}
670
671impl CpioDirectoryObject {
672    pub fn new(node: Arc<dyn VfsNode>) -> Self {
673        Self {
674            node,
675            position: RwLock::new(0),
676        }
677    }
678}
679
680impl StreamOps for CpioDirectoryObject {
681    fn read(&self, buf: &mut [u8]) -> Result<usize, StreamError> {
682        let cpio_node = self
683            .node
684            .as_any()
685            .downcast_ref::<CpioNode>()
686            .ok_or(StreamError::NotSupported)?;
687        if cpio_node.file_type != FileType::Directory {
688            return Err(StreamError::NotSupported);
689        }
690        let mut all_entries = Vec::new();
691        // . entry
692        all_entries.push(crate::fs::DirectoryEntryInternal {
693            name: ".".to_string(),
694            file_type: FileType::Directory,
695            size: 0,
696            file_id: cpio_node.file_id as u64,
697            metadata: None,
698        });
699        // .. entry
700        let parent_file_id = cpio_node.parent_file_id().unwrap_or(0);
701        all_entries.push(crate::fs::DirectoryEntryInternal {
702            name: "..".to_string(),
703            file_type: FileType::Directory,
704            size: 0,
705            file_id: parent_file_id,
706            metadata: None,
707        });
708        // children entries
709        for child in cpio_node.children.read().values() {
710            all_entries.push(crate::fs::DirectoryEntryInternal {
711                name: child.name.clone(),
712                file_type: child.file_type.clone(),
713                size: child.content.len(),
714                file_id: child.file_id as u64,
715                metadata: None,
716            });
717        }
718
719        let position = *self.position.read() as usize;
720        if position >= all_entries.len() {
721            return Ok(0); // EOF
722        }
723        let internal_entry = &all_entries[position];
724        let dir_entry = crate::fs::DirectoryEntry::from_internal(internal_entry);
725        let entry_size = dir_entry.entry_size();
726        if buf.len() < entry_size {
727            return Err(StreamError::InvalidArgument);
728        }
729        let entry_bytes =
730            unsafe { core::slice::from_raw_parts(&dir_entry as *const _ as *const u8, entry_size) };
731        buf[..entry_size].copy_from_slice(entry_bytes);
732        *self.position.write() += 1;
733        Ok(entry_size)
734    }
735    fn write(&self, _buf: &[u8]) -> Result<usize, StreamError> {
736        Err(StreamError::FileSystemError(FileSystemError::new(
737            FileSystemErrorKind::ReadOnly,
738            "CPIO filesystem is read-only",
739        )))
740    }
741}
742
743impl ControlOps for CpioDirectoryObject {
744    // CPIO directories don't support control operations
745    fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
746        Err("Control operations not supported on CPIO directories")
747    }
748}
749
750impl MemoryMappingOps for CpioDirectoryObject {
751    fn get_mapping_info(
752        &self,
753        _offset: usize,
754        _length: usize,
755    ) -> Result<(usize, usize, bool), &'static str> {
756        Err("Memory mapping not supported for directories")
757    }
758
759    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
760        // Directories don't support memory mapping
761    }
762
763    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
764        // Directories don't support memory mapping
765    }
766
767    fn supports_mmap(&self) -> bool {
768        false
769    }
770}
771
772impl FileObject for CpioDirectoryObject {
773    fn seek(&self, _whence: crate::fs::SeekFrom) -> Result<u64, StreamError> {
774        // Seeking in directories not supported
775        Err(StreamError::NotSupported)
776    }
777
778    fn metadata(&self) -> Result<crate::fs::FileMetadata, StreamError> {
779        self.node.metadata().map_err(StreamError::from)
780    }
781
782    fn truncate(&self, _size: u64) -> Result<(), StreamError> {
783        Err(StreamError::FileSystemError(FileSystemError::new(
784            FileSystemErrorKind::ReadOnly,
785            "CPIO filesystem is read-only",
786        )))
787    }
788
789    fn as_any(&self) -> &dyn Any {
790        self
791    }
792}
793
794impl crate::object::capability::selectable::Selectable for CpioDirectoryObject {
795    fn current_ready(
796        &self,
797        interest: crate::object::capability::selectable::ReadyInterest,
798    ) -> crate::object::capability::selectable::ReadySet {
799        let mut set = crate::object::capability::selectable::ReadySet::none();
800        if interest.read {
801            set.read = true;
802        }
803        if interest.write {
804            set.write = true;
805        }
806        if interest.except {
807            set.except = false;
808        }
809        set
810    }
811
812    fn wait_until_ready(
813        &self,
814        _interest: crate::object::capability::selectable::ReadyInterest,
815        _trapframe: &mut crate::arch::Trapframe,
816        _timeout_ticks: Option<u64>,
817    ) -> crate::object::capability::selectable::SelectWaitOutcome {
818        crate::object::capability::selectable::SelectWaitOutcome::Ready
819    }
820
821    fn is_nonblocking(&self) -> bool {
822        true
823    }
824}
825
826/// Symbolic link object for CPIO symbolic links
827pub struct CpioSymlinkObject {
828    node: Arc<dyn VfsNode>,
829    position: RwLock<u64>,
830}
831
832impl CpioSymlinkObject {
833    pub fn new(node: Arc<dyn VfsNode>) -> Self {
834        Self {
835            node,
836            position: RwLock::new(0),
837        }
838    }
839}
840
841impl StreamOps for CpioSymlinkObject {
842    fn read(&self, buf: &mut [u8]) -> Result<usize, StreamError> {
843        // Reading a symlink returns the target path
844        let target = self
845            .node
846            .read_link()
847            .map_err(|e| StreamError::FileSystemError(e))?;
848
849        let target_bytes = target.as_bytes();
850        let mut pos = self.position.write();
851        let start = *pos as usize;
852
853        if start >= target_bytes.len() {
854            return Ok(0); // EOF
855        }
856
857        let end = (start + buf.len()).min(target_bytes.len());
858        let bytes_to_read = end - start;
859        buf[..bytes_to_read].copy_from_slice(&target_bytes[start..end]);
860        *pos += bytes_to_read as u64;
861
862        Ok(bytes_to_read)
863    }
864
865    fn write(&self, _buf: &[u8]) -> Result<usize, StreamError> {
866        Err(StreamError::FileSystemError(FileSystemError::new(
867            FileSystemErrorKind::ReadOnly,
868            "CPIO filesystem is read-only",
869        )))
870    }
871}
872
873impl ControlOps for CpioSymlinkObject {
874    fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
875        Err("Control operations not supported on CPIO symbolic links")
876    }
877}
878
879impl MemoryMappingOps for CpioSymlinkObject {
880    fn get_mapping_info(
881        &self,
882        _offset: usize,
883        _length: usize,
884    ) -> Result<(usize, usize, bool), &'static str> {
885        Err("Memory mapping not supported for symbolic links")
886    }
887
888    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
889        // Symbolic links don't support memory mapping
890    }
891
892    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
893        // Symbolic links don't support memory mapping
894    }
895
896    fn supports_mmap(&self) -> bool {
897        false
898    }
899}
900
901impl FileObject for CpioSymlinkObject {
902    fn seek(&self, whence: crate::fs::SeekFrom) -> Result<u64, StreamError> {
903        let target = self
904            .node
905            .read_link()
906            .map_err(|e| StreamError::FileSystemError(e))?;
907        let target_size = target.len() as u64;
908
909        let mut pos = self.position.write();
910
911        let new_pos = match whence {
912            crate::fs::SeekFrom::Start(offset) => offset,
913            crate::fs::SeekFrom::Current(offset) => {
914                if offset >= 0 {
915                    *pos + offset as u64
916                } else {
917                    pos.saturating_sub((-offset) as u64)
918                }
919            }
920            crate::fs::SeekFrom::End(offset) => {
921                if offset >= 0 {
922                    target_size + offset as u64
923                } else {
924                    target_size.saturating_sub((-offset) as u64)
925                }
926            }
927        };
928
929        *pos = new_pos.min(target_size);
930        Ok(*pos)
931    }
932
933    fn metadata(&self) -> Result<crate::fs::FileMetadata, StreamError> {
934        self.node.metadata().map_err(StreamError::from)
935    }
936
937    fn truncate(&self, _size: u64) -> Result<(), StreamError> {
938        Err(StreamError::FileSystemError(FileSystemError::new(
939            FileSystemErrorKind::ReadOnly,
940            "CPIO filesystem is read-only",
941        )))
942    }
943
944    fn as_any(&self) -> &dyn Any {
945        self
946    }
947}
948
949impl crate::object::capability::selectable::Selectable for CpioSymlinkObject {
950    fn current_ready(
951        &self,
952        interest: crate::object::capability::selectable::ReadyInterest,
953    ) -> crate::object::capability::selectable::ReadySet {
954        let mut set = crate::object::capability::selectable::ReadySet::none();
955        if interest.read {
956            set.read = true;
957        }
958        if interest.write {
959            set.write = true;
960        }
961        if interest.except {
962            set.except = false;
963        }
964        set
965    }
966
967    fn wait_until_ready(
968        &self,
969        _interest: crate::object::capability::selectable::ReadyInterest,
970        _trapframe: &mut crate::arch::Trapframe,
971        _timeout_ticks: Option<u64>,
972    ) -> crate::object::capability::selectable::SelectWaitOutcome {
973        crate::object::capability::selectable::SelectWaitOutcome::Ready
974    }
975
976    fn is_nonblocking(&self) -> bool {
977        true
978    }
979}
980
981/// Driver for CPIO-format filesystems (initramfs)
982///
983/// This driver creates filesystems from memory areas only.
984pub struct CpiofsDriver;
985
986impl FileSystemDriver for CpiofsDriver {
987    fn name(&self) -> &'static str {
988        "cpiofs"
989    }
990
991    /// This filesystem only supports creation from memory
992    fn filesystem_type(&self) -> FileSystemType {
993        FileSystemType::Memory
994    }
995
996    /// Create a file system from memory area
997    ///
998    /// # Arguments
999    ///
1000    /// * `memory_area` - A reference to the memory area containing the CPIO filesystem data
1001    ///
1002    /// # Returns
1003    ///
1004    /// A result containing a boxed CPIO filesystem or an error
1005    ///
1006    fn create_from_memory(
1007        &self,
1008        memory_area: &MemoryArea,
1009    ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1010        let data = unsafe { memory_area.as_slice() };
1011        // Create the Cpiofs from the memory data
1012        match CpioFS::new("cpiofs".to_string(), data) {
1013            Ok(cpio_fs) => Ok(cpio_fs),
1014            Err(err) => Err(FileSystemError {
1015                kind: FileSystemErrorKind::InvalidData,
1016                message: format!(
1017                    "Failed to create CPIO filesystem from memory: {}",
1018                    err.message
1019                ),
1020            }),
1021        }
1022    }
1023
1024    fn create_from_params(
1025        &self,
1026        params: &dyn crate::fs::params::FileSystemParams,
1027    ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1028        use crate::fs::params::*;
1029
1030        // Try to downcast to CpioFSParams
1031        if let Some(_cpio_params) = params.as_any().downcast_ref::<CpioFSParams>() {
1032            // CPIO filesystem requires memory area for creation, so we cannot create from parameters alone
1033            return Err(FileSystemError {
1034                kind: FileSystemErrorKind::NotSupported,
1035                message: "CPIO filesystem requires memory area for creation. Use create_from_memory instead.".to_string(),
1036            });
1037        }
1038
1039        // Try to downcast to BasicFSParams for compatibility
1040        if let Some(_basic_params) = params.as_any().downcast_ref::<BasicFSParams>() {
1041            return Err(FileSystemError {
1042                kind: FileSystemErrorKind::NotSupported,
1043                message: "CPIO filesystem requires memory area for creation. Use create_from_memory instead.".to_string(),
1044            });
1045        }
1046
1047        // If all downcasts fail, return error
1048        Err(FileSystemError {
1049            kind: FileSystemErrorKind::NotSupported,
1050            message: "CPIO filesystem requires CpioFSParams and memory area for creation"
1051                .to_string(),
1052        })
1053    }
1054}
1055
1056fn register_driver() {
1057    let fs_driver_manager = get_fs_driver_manager();
1058    fs_driver_manager.register_driver(Box::new(CpiofsDriver));
1059}
1060
1061driver_initcall!(register_driver);
1062
1063#[cfg(test)]
1064#[path = "cpiofs_tests.rs"]
1065mod tests;