1use 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
34pub struct CpioFS {
36 fs_id: FileSystemId,
38 root_node: Arc<CpioNode>,
40
41 name: String,
43}
44
45pub struct CpioNode {
47 name: String,
49
50 file_type: FileType,
52
53 content: Vec<u8>,
55
56 children: RwLock<BTreeMap<String, Arc<CpioNode>>>,
58
59 filesystem: RwLock<Option<Arc<CpioFS>>>,
61
62 file_id: usize,
64
65 parent: RwLock<Option<Weak<CpioNode>>>,
67}
68
69impl CpioNode {
70 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 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 *child.parent.write() = Some(Arc::downgrade(self));
97 let mut children = self.children.write();
98 children.insert(name, child);
99 Ok(())
100 }
101
102 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 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 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 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 filesystem.parse_cpio_archive(cpio_data)?;
185 Ok(filesystem)
186 }
187
188 pub fn create_from_option_string(
191 option: Option<&str>,
192 cpio_data: &[u8],
193 ) -> Arc<dyn FileSystemOperations> {
194 let name = "cpiofs".to_string();
196 CpioFS::new(name, cpio_data).expect("Failed to create CpioFS")
198 as Arc<dyn FileSystemOperations>
199 }
200
201 fn parse_cpio_archive(self: &Arc<Self>, data: &[u8]) -> Result<(), FileSystemError> {
203 let mut offset = 0;
205 let mut file_id = 2;
206 while offset + 110 <= data.len() {
207 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]; let name_str = core::str::from_utf8(name).unwrap_or("").to_string();
263 let file_start = (name_end + 3) & !3; 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 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 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 let base_name = if let Some(pos) = name_str.rfind('/') {
287 &name_str[pos + 1..]
288 } else {
289 &name_str[..]
290 };
291
292 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 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 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 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 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 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 entries.push(DirectoryEntryInternal {
461 name: ".".to_string(),
462 file_type: FileType::Directory,
463 file_id: cpio_node.file_id as u64,
464 });
465
466 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 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
492pub 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); }
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 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 }
554
555 fn on_unmapped(&self, _vaddr: usize, _length: usize) {
556 }
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
665pub 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 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 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 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); }
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 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 }
762
763 fn on_unmapped(&self, _vaddr: usize, _length: usize) {
764 }
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 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
826pub 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 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); }
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 }
891
892 fn on_unmapped(&self, _vaddr: usize, _length: usize) {
893 }
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
981pub struct CpiofsDriver;
985
986impl FileSystemDriver for CpiofsDriver {
987 fn name(&self) -> &'static str {
988 "cpiofs"
989 }
990
991 fn filesystem_type(&self) -> FileSystemType {
993 FileSystemType::Memory
994 }
995
996 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 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 if let Some(_cpio_params) = params.as_any().downcast_ref::<CpioFSParams>() {
1032 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 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 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;