1use alloc::{
7 boxed::Box, collections::BTreeMap, format, string::{String, ToString}, sync::Arc, vec::Vec
8};
9use spin::RwLock;
10use core::any::Any;
11use alloc::sync::Weak;
12
13use crate::{driver_initcall, fs::{core::DirectoryEntryInternal, get_fs_driver_manager, vfs_v2::core::{
14 FileSystemOperations, VfsNode
15}, FileSystemDriver, FileSystemType}, vm::vmem::MemoryArea};
16use crate::fs::{
17 FileSystemError, FileSystemErrorKind, FileMetadata, FileObject, FileType, FilePermission
18};
19use crate::object::capability::{StreamOps, StreamError};
20
21pub struct CpioFS {
23 root_node: Arc<CpioNode>,
25
26 name: String,
28}
29
30pub struct CpioNode {
32 name: String,
34
35 file_type: FileType,
37
38 content: Vec<u8>,
40
41 children: RwLock<BTreeMap<String, Arc<CpioNode>>>,
43
44 filesystem: RwLock<Option<Arc<CpioFS>>>,
46
47 file_id: usize,
49
50 parent: RwLock<Option<Weak<CpioNode>>>,
52}
53
54impl CpioNode {
55 pub fn new(name: String, file_type: FileType, content: Vec<u8>, file_id: usize) -> Arc<Self> {
57 Arc::new(Self {
58 name,
59 file_type,
60 content,
61 children: RwLock::new(BTreeMap::new()),
62 filesystem: RwLock::new(None),
63 file_id,
64 parent: RwLock::new(None),
65 })
66 }
67
68 pub fn add_child(self: &Arc<Self>, name: String, child: Arc<CpioNode>) -> Result<(), FileSystemError> {
70 if self.file_type != FileType::Directory {
71 return Err(FileSystemError::new(
72 FileSystemErrorKind::NotADirectory,
73 "Cannot add child to non-directory node"
74 ));
75 }
76 *child.parent.write() = Some(Arc::downgrade(self));
78 let mut children = self.children.write();
79 children.insert(name, child);
80 Ok(())
81 }
82
83 pub fn get_child(&self, name: &str) -> Option<Arc<CpioNode>> {
85 let children = self.children.read();
86 children.get(name).cloned()
87 }
88
89 pub fn parent_file_id(&self) -> Option<u64> {
90 self.parent.read().as_ref()?.upgrade().map(|p| p.file_id as u64)
91 }
92
93 pub fn from_vfsnode_arc(node: &Arc<dyn VfsNode>) -> Option<Arc<CpioNode>> {
95 match Arc::downcast::<CpioNode>(node.clone()) {
96 Ok(cpio_node) => Some(cpio_node),
97 Err(_) => None,
98 }
99 }
100}
101
102impl VfsNode for CpioNode {
103 fn id(&self) -> u64 {
104 self.file_id as u64
105 }
106
107 fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>> {
108 let fs_guard = self.filesystem.read();
109 fs_guard.as_ref().map(|fs| Arc::downgrade(fs) as Weak<dyn FileSystemOperations>)
110 }
111
112 fn metadata(&self) -> Result<FileMetadata, FileSystemError> {
113 Ok(FileMetadata {
114 file_type: self.file_type.clone(),
115 size: self.content.len(),
116 created_time: 0,
117 modified_time: 0,
118 accessed_time: 0,
119 permissions: FilePermission {
120 read: true,
121 write: false,
122 execute: false,
123 },
124 file_id: self.file_id as u64,
125 link_count: 1,
126 })
127 }
128
129 fn as_any(&self) -> &dyn Any {
130 self
131 }
132
133 fn read_link(&self) -> Result<String, FileSystemError> {
134 match &self.file_type {
136 FileType::SymbolicLink(target) => Ok(target.clone()),
137 _ => Err(FileSystemError::new(
138 FileSystemErrorKind::NotSupported,
139 "Not a symbolic link"
140 ))
141 }
142 }
143}
144
145impl CpioFS {
146 pub fn new(name: String, cpio_data: &[u8]) -> Result<Arc<Self>, FileSystemError> {
148 let root_node = CpioNode::new("/".to_string(), FileType::Directory, Vec::new(), 1);
149 let filesystem = Arc::new(Self {
150 root_node: Arc::clone(&root_node),
151 name,
152 });
153 {
154 let mut fs_guard = root_node.filesystem.write();
155 *fs_guard = Some(Arc::clone(&filesystem));
156 }
157 filesystem.parse_cpio_archive(cpio_data)?;
159 Ok(filesystem)
160 }
161
162 pub fn create_from_option_string(option: Option<&str>, cpio_data: &[u8]) -> Arc<dyn FileSystemOperations> {
165 let name = "cpiofs".to_string();
167 CpioFS::new(name, cpio_data).expect("Failed to create CpioFS") as Arc<dyn FileSystemOperations>
169 }
170
171 fn parse_cpio_archive(self: &Arc<Self>, data: &[u8]) -> Result<(), FileSystemError> {
173 let mut offset = 0;
175 let mut file_id = 2;
176 while offset + 110 <= data.len() {
177 let magic = &data[offset..offset+6];
179 if magic != b"070701" {
180 break;
181 }
182 let _inode = match core::str::from_utf8(&data[offset+6..offset+14]) {
183 Ok(s) => u32::from_str_radix(s, 16).map_err(|_| FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid inode value"))?,
184 Err(_) => return Err(FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid UTF-8 in inode field")),
185 };
186 let mode = match core::str::from_utf8(&data[offset+14..offset+22]) {
187 Ok(s) => u32::from_str_radix(s, 16).map_err(|_| FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid mode value"))?,
188 Err(_) => return Err(FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid UTF-8 in mode field")),
189 };
190 let namesize = match core::str::from_utf8(&data[offset+94..offset+102]) {
191 Ok(s) => usize::from_str_radix(s, 16).map_err(|_| FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid namesize value"))?,
192 Err(_) => return Err(FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid UTF-8 in namesize field")),
193 };
194 let filesize = match core::str::from_utf8(&data[offset+54..offset+62]) {
195 Ok(s) => usize::from_str_radix(s, 16).map_err(|_| FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid filesize value"))?,
196 Err(_) => return Err(FileSystemError::new(FileSystemErrorKind::InvalidData, "Invalid UTF-8 in filesize field")),
197 };
198 let name_start = offset + 110;
199 let name_end = name_start + namesize;
200 if name_end > data.len() { break; }
201 let name = &data[name_start..name_end-1]; let name_str = core::str::from_utf8(name).unwrap_or("").to_string();
203 let file_start = (name_end + 3) & !3; let file_end = file_start + filesize;
205 if file_end > data.len() { break; }
206 if name_str == "TRAILER!!!" { break; }
207
208 let (file_type, content) = match mode & 0o170000 {
210 0o040000 => (FileType::Directory, Vec::new()),
211 0o100000 => (FileType::RegularFile, data[file_start..file_end].to_vec()),
212 0o120000 => {
213 let target_bytes = data[file_start..file_end].to_vec();
215 let target_path = String::from_utf8(target_bytes.clone()).unwrap_or_else(|_| String::new());
216 (FileType::SymbolicLink(target_path), target_bytes)
217 },
218 _ => (FileType::RegularFile, data[file_start..file_end].to_vec()),
219 };
220 let base_name = if let Some(pos) = name_str.rfind('/') {
222 &name_str[pos+1..]
223 } else {
224 &name_str[..]
225 };
226
227 if base_name == "." || base_name == ".." {
229 offset = (file_end + 3) & !3;
230 continue;
231 }
232
233 let node = CpioNode::new(base_name.to_string(), file_type, content, file_id);
234 {
235 let mut fs_guard = node.filesystem.write();
236 *fs_guard = Some(Arc::clone(self));
237 }
238 file_id += 1;
239 let parent_path = if let Some(pos) = name_str.rfind('/') { &name_str[..pos] } else { "" };
241 let parent = if parent_path.is_empty() {
242 Arc::clone(&self.root_node)
243 } else {
244 let mut cur = Arc::clone(&self.root_node);
246 for part in parent_path.split('/') {
247 if part.is_empty() { continue; }
248 if let Some(child) = cur.get_child(part) {
249 cur = child;
250 } else {
251 let dir = CpioNode::new(part.to_string(), FileType::Directory, Vec::new(), file_id);
253 {
254 let mut fs_guard = dir.filesystem.write();
255 *fs_guard = Some(Arc::clone(self));
256 }
257 file_id += 1;
258 cur.add_child(part.to_string(), Arc::clone(&dir)).ok();
259 cur = dir;
260 }
261 }
262 cur
263 };
264 parent.add_child(base_name.to_string(), Arc::clone(&node)).ok();
265 offset = (file_end + 3) & !3;
266 }
267 Ok(())
268 }
269}
270
271impl FileSystemOperations for CpioFS {
272 fn lookup(
273 &self,
274 parent_node: &Arc<dyn VfsNode>,
275 name: &String,
276 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
277 let cpio_parent = parent_node.as_any()
279 .downcast_ref::<CpioNode>()
280 .ok_or_else(|| FileSystemError::new(
281 FileSystemErrorKind::NotSupported,
282 "Invalid node type for CpioFS"
283 ))?;
284
285 cpio_parent.get_child(name)
287 .map(|n| n as Arc<dyn VfsNode>)
288 .ok_or_else(|| FileSystemError::new(
289 FileSystemErrorKind::NotFound,
290 format!("File not found: {} in {}", name, cpio_parent.name)
291 ))
292 }
293
294 fn open(
295 &self,
296 node: &Arc<dyn VfsNode>,
297 _flags: u32,
298 ) -> Result<Arc<dyn FileObject>, FileSystemError> {
299 let cpio_node = node.as_any()
300 .downcast_ref::<CpioNode>()
301 .ok_or_else(|| FileSystemError::new(
302 FileSystemErrorKind::NotSupported,
303 "Invalid node type for CpioFS"
304 ))?;
305
306 match cpio_node.file_type {
307 FileType::RegularFile => {
308 Ok(Arc::new(CpioFileObject::new(Arc::clone(node))))
309 },
310 FileType::Directory => {
311 Ok(Arc::new(CpioDirectoryObject::new(Arc::clone(node))))
312 },
313 _ => Err(FileSystemError::new(
314 FileSystemErrorKind::NotSupported,
315 "Unsupported file type"
316 ))
317 }
318 }
319
320 fn create(
321 &self,
322 _parent_node: &Arc<dyn VfsNode>,
323 _name: &String,
324 _file_type: FileType,
325 _mode: u32,
326 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
327 Err(FileSystemError::new(
328 FileSystemErrorKind::ReadOnly,
329 "CPIO filesystem is read-only"
330 ))
331 }
332
333 fn remove(
334 &self,
335 _parent_node: &Arc<dyn VfsNode>,
336 _name: &String,
337 ) -> Result<(), FileSystemError> {
338 Err(FileSystemError::new(
339 FileSystemErrorKind::ReadOnly,
340 "CPIO filesystem is read-only"
341 ))
342 }
343
344 fn root_node(&self) -> Arc<dyn VfsNode> {
345 Arc::clone(&self.root_node) as Arc<dyn VfsNode>
346 }
347
348 fn name(&self) -> &str {
349 &self.name
350 }
351
352 fn is_read_only(&self) -> bool {
353 true
354 }
355
356 fn readdir(
357 &self,
358 node: &Arc<dyn VfsNode>,
359 ) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
360 let cpio_node = node.as_any()
361 .downcast_ref::<CpioNode>()
362 .ok_or_else(|| FileSystemError::new(
363 FileSystemErrorKind::NotSupported,
364 "Invalid node type for CpioFS"
365 ))?;
366 if cpio_node.file_type != FileType::Directory {
367 return Err(FileSystemError::new(
368 FileSystemErrorKind::NotADirectory,
369 "Not a directory"
370 ));
371 }
372 let mut entries = Vec::new();
373
374 entries.push(DirectoryEntryInternal {
376 name: ".".to_string(),
377 file_type: FileType::Directory,
378 file_id: cpio_node.file_id as u64,
379 });
380
381 let parent_file_id = cpio_node.parent_file_id().unwrap_or(0);
383 entries.push(DirectoryEntryInternal {
384 name: "..".to_string(),
385 file_type: FileType::Directory,
386 file_id: parent_file_id,
387 });
388
389 let children = cpio_node.children.read();
391 for child in children.values() {
392 entries.push(DirectoryEntryInternal {
393 name: child.name.clone(),
394 file_type: child.file_type.clone(),
395 file_id: child.file_id as u64,
396 });
397 }
398
399 Ok(entries)
400 }
401}
402
403pub struct CpioFileObject {
405 node: Arc<dyn VfsNode>,
406 position: RwLock<u64>,
407}
408
409impl CpioFileObject {
410 pub fn new(node: Arc<dyn VfsNode>) -> Self {
411 Self {
412 node,
413 position: RwLock::new(0),
414 }
415 }
416}
417
418impl StreamOps for CpioFileObject {
419 fn read(&self, buf: &mut [u8]) -> Result<usize, StreamError> {
420 let cpio_node = self.node.as_any()
421 .downcast_ref::<CpioNode>()
422 .ok_or(StreamError::IoError)?;
423
424 let mut pos = self.position.write();
425 let start = *pos as usize;
426 let end = (start + buf.len()).min(cpio_node.content.len());
427
428 if start >= cpio_node.content.len() {
429 return Ok(0); }
431
432 let bytes_to_read = end - start;
433 buf[..bytes_to_read].copy_from_slice(&cpio_node.content[start..end]);
434 *pos += bytes_to_read as u64;
435
436 Ok(bytes_to_read)
437 }
438
439 fn write(&self, _buf: &[u8]) -> Result<usize, StreamError> {
440 Err(StreamError::PermissionDenied)
441 }
442}
443
444impl FileObject for CpioFileObject {
445 fn seek(&self, whence: crate::fs::SeekFrom) -> Result<u64, StreamError> {
446 let cpio_node = self.node.as_any()
447 .downcast_ref::<CpioNode>()
448 .ok_or(StreamError::IoError)?;
449
450 let mut pos = self.position.write();
451 let file_size = cpio_node.content.len() as u64;
452
453 let new_pos = match whence {
454 crate::fs::SeekFrom::Start(offset) => offset,
455 crate::fs::SeekFrom::Current(offset) => {
456 if offset >= 0 {
457 *pos + offset as u64
458 } else {
459 pos.saturating_sub((-offset) as u64)
460 }
461 },
462 crate::fs::SeekFrom::End(offset) => {
463 if offset >= 0 {
464 file_size + offset as u64
465 } else {
466 file_size.saturating_sub((-offset) as u64)
467 }
468 }
469 };
470
471 *pos = new_pos.min(file_size);
472 Ok(*pos)
473 }
474
475 fn metadata(&self) -> Result<crate::fs::FileMetadata, StreamError> {
476 self.node.metadata().map_err(StreamError::from)
477 }
478
479 fn truncate(&self, _size: u64) -> Result<(), StreamError> {
480 Err(StreamError::PermissionDenied)
481 }
482}
483
484pub struct CpioDirectoryObject {
486 node: Arc<dyn VfsNode>,
487 position: RwLock<u64>,
488}
489
490impl CpioDirectoryObject {
491 pub fn new(node: Arc<dyn VfsNode>) -> Self {
492 Self {
493 node,
494 position: RwLock::new(0),
495 }
496 }
497}
498
499impl StreamOps for CpioDirectoryObject {
500 fn read(&self, buf: &mut [u8]) -> Result<usize, StreamError> {
501 let cpio_node = self.node.as_any()
502 .downcast_ref::<CpioNode>()
503 .ok_or(StreamError::NotSupported)?;
504 if cpio_node.file_type != FileType::Directory {
505 return Err(StreamError::NotSupported);
506 }
507 let mut all_entries = Vec::new();
508 all_entries.push(crate::fs::DirectoryEntryInternal {
510 name: ".".to_string(),
511 file_type: FileType::Directory,
512 size: 0,
513 file_id: cpio_node.file_id as u64,
514 metadata: None,
515 });
516 let parent_file_id = cpio_node.parent_file_id().unwrap_or(0);
518 all_entries.push(crate::fs::DirectoryEntryInternal {
519 name: "..".to_string(),
520 file_type: FileType::Directory,
521 size: 0,
522 file_id: parent_file_id,
523 metadata: None,
524 });
525 for child in cpio_node.children.read().values() {
527 all_entries.push(crate::fs::DirectoryEntryInternal {
528 name: child.name.clone(),
529 file_type: child.file_type.clone(),
530 size: child.content.len(),
531 file_id: child.file_id as u64,
532 metadata: None,
533 });
534 }
535
536 let position = *self.position.read() as usize;
537 if position >= all_entries.len() {
538 return Ok(0); }
540 let internal_entry = &all_entries[position];
541 let dir_entry = crate::fs::DirectoryEntry::from_internal(internal_entry);
542 let entry_size = dir_entry.entry_size();
543 if buf.len() < entry_size {
544 return Err(StreamError::InvalidArgument);
545 }
546 let entry_bytes = unsafe {
547 core::slice::from_raw_parts(
548 &dir_entry as *const _ as *const u8,
549 entry_size
550 )
551 };
552 buf[..entry_size].copy_from_slice(entry_bytes);
553 *self.position.write() += 1;
554 Ok(entry_size)
555 }
556 fn write(&self, _buf: &[u8]) -> Result<usize, StreamError> {
557 Err(StreamError::FileSystemError(FileSystemError::new(
558 FileSystemErrorKind::ReadOnly,
559 "CPIO filesystem is read-only"
560 )))
561 }
562}
563
564impl FileObject for CpioDirectoryObject {
565 fn seek(&self, _whence: crate::fs::SeekFrom) -> Result<u64, StreamError> {
566 Err(StreamError::NotSupported)
568 }
569
570 fn metadata(&self) -> Result<crate::fs::FileMetadata, StreamError> {
571 self.node.metadata().map_err(StreamError::from)
572 }
573
574 fn truncate(&self, _size: u64) -> Result<(), StreamError> {
575 Err(StreamError::FileSystemError(FileSystemError::new(
576 FileSystemErrorKind::ReadOnly,
577 "CPIO filesystem is read-only"
578 )))
579 }
580}
581
582
583pub struct CpiofsDriver;
587
588impl FileSystemDriver for CpiofsDriver {
589 fn name(&self) -> &'static str {
590 "cpiofs"
591 }
592
593 fn filesystem_type(&self) -> FileSystemType {
595 FileSystemType::Memory
596 }
597
598 fn create_from_memory(&self, memory_area: &MemoryArea) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
609 let data = unsafe { memory_area.as_slice() };
610 match CpioFS::new("cpiofs".to_string(), data) {
612 Ok(cpio_fs) => Ok(cpio_fs),
613 Err(err) => Err(FileSystemError {
614 kind: FileSystemErrorKind::InvalidData,
615 message: format!("Failed to create CPIO filesystem from memory: {}", err.message),
616 })
617 }
618 }
619
620 fn create_from_params(&self, params: &dyn crate::fs::params::FileSystemParams) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
621 use crate::fs::params::*;
622
623 if let Some(_cpio_params) = params.as_any().downcast_ref::<CpioFSParams>() {
625 return Err(FileSystemError {
627 kind: FileSystemErrorKind::NotSupported,
628 message: "CPIO filesystem requires memory area for creation. Use create_from_memory instead.".to_string(),
629 });
630 }
631
632 if let Some(_basic_params) = params.as_any().downcast_ref::<BasicFSParams>() {
634 return Err(FileSystemError {
635 kind: FileSystemErrorKind::NotSupported,
636 message: "CPIO filesystem requires memory area for creation. Use create_from_memory instead.".to_string(),
637 });
638 }
639
640 Err(FileSystemError {
642 kind: FileSystemErrorKind::NotSupported,
643 message: "CPIO filesystem requires CpioFSParams and memory area for creation".to_string(),
644 })
645 }
646}
647
648fn register_driver() {
649 let fs_driver_manager = get_fs_driver_manager();
650 fs_driver_manager.register_driver(Box::new(CpiofsDriver));
651}
652
653driver_initcall!(register_driver);
654
655#[cfg(test)]
656#[path = "cpiofs_tests.rs"]
657mod tests;