1use alloc::{boxed::Box, format, string::{String, ToString}, sync::Arc, vec::Vec};
37use spin::{Mutex, RwLock};
38
39use crate::{driver_initcall, fs::{
40 get_fs_driver_manager, Directory, DirectoryEntry, FileHandle, FileMetadata, FileOperations, FileSystem, FileSystemDriver, FileSystemError, FileSystemErrorKind, FileSystemType, FileType, Result, VirtualFileSystem
41}, vm::vmem::MemoryArea};
42
43#[derive(Debug, Clone)]
45pub struct CpiofsEntry {
46 pub name: String,
47 pub file_type: FileType,
48 pub size: usize,
49 pub modified_time: u64,
50 pub data: Option<Vec<u8>>, }
52
53pub struct Cpiofs {
55 name: &'static str,
56 entries: Mutex<Vec<CpiofsEntry>>, mounted: bool,
58 mount_point: String,
59}
60
61impl Cpiofs {
62 pub fn new(name: &'static str, cpio_data: &[u8]) -> Result<Self> {
74 let entries = Self::parse_cpio(cpio_data)?;
75 Ok(Self {
76 name,
77 entries: Mutex::new(entries),
78 mounted: false,
79 mount_point: String::new(),
80 })
81 }
82
83 fn parse_cpio(cpio_data: &[u8]) -> Result<Vec<CpiofsEntry>> {
94 let mut entries = Vec::new();
95 let mut offset = 0;
96
97 while offset < cpio_data.len() {
98 if offset + 110 > cpio_data.len() {
99 break; }
101
102 let header = &cpio_data[offset..offset + 110];
104 let name_size = usize::from_str_radix(
105 core::str::from_utf8(&header[94..102]).unwrap_or("0"),
106 16,
107 )
108 .unwrap_or(0);
109
110 if &header[0..6] != b"070701" {
112 return Err(FileSystemError {
113 kind: FileSystemErrorKind::InvalidData,
114 message: format!("Invalid CPIO magic number {:#x} at offset {}",
115 u32::from_str_radix(core::str::from_utf8(&header[0..6]).unwrap_or("0"), 16).unwrap_or(0),
116 offset,
117 ),
118 });
119 }
120
121 let name_offset = offset + 110;
123 let name_end = name_offset + name_size - 1; if name_end > cpio_data.len() {
125 break; }
127
128 let name = core::str::from_utf8(&cpio_data[name_offset..name_end])
129 .unwrap_or("")
130 .to_string();
131
132 if name == "TRAILER!!!" {
134 break;
135 }
136
137 let mode = usize::from_str_radix(
139 core::str::from_utf8(&header[14..22]).unwrap_or("0"),
140 16,
141 ).unwrap_or(0);
142
143 let file_type = if mode & 0o170000 == 0o040000 {
144 FileType::Directory
145 } else if mode & 0o170000 == 0o100000 {
146 FileType::RegularFile
147 } else {
148 FileType::Unknown
149 };
150
151 let modified_time = u64::from_str_radix(
152 core::str::from_utf8(&header[46..54]).unwrap_or("0"),
153 16,
154 ).unwrap_or(0);
155
156 let file_size = usize::from_str_radix(
158 core::str::from_utf8(&header[54..62]).unwrap_or("0"),
159 16,
160 )
161 .unwrap_or(0);
162
163 let data_offset = (name_offset + name_size + 3) & !3; let data_end = data_offset + file_size;
166 let data = if file_size > 0 && data_end <= cpio_data.len() {
167 Some(cpio_data[data_offset..data_end].to_vec())
168 } else {
169 None
170 };
171
172 entries.push(CpiofsEntry {
173 name,
174 file_type,
175 size: file_size,
176 modified_time,
177 data,
178 });
179
180 offset = (data_end + 3) & !3; }
183
184 Ok(entries)
185 }
186
187 fn normalize_path(&self, path: &str) -> String {
188 if path.starts_with('/') {
190 path.trim_start_matches('/').to_string()
191 } else {
192 path.to_string()
193 }
194 }
195
196}
197
198impl FileSystem for Cpiofs {
199 fn mount(&mut self, mount_point: &str) -> Result<()> {
200 if self.mounted {
201 return Err(FileSystemError {
202 kind: FileSystemErrorKind::AlreadyExists,
203 message: "File system already mounted".to_string(),
204 });
205 }
206 self.mounted = true;
207 self.mount_point = mount_point.to_string();
208 Ok(())
209 }
210
211 fn unmount(&mut self) -> Result<()> {
212 if !self.mounted {
213 return Err(FileSystemError {
214 kind: FileSystemErrorKind::NotFound,
215 message: "File system not mounted".to_string(),
216 });
217 }
218 self.mounted = false;
219 self.mount_point = String::new();
220 Ok(())
221 }
222
223 fn name(&self) -> &str {
224 self.name
225 }
226}
227
228impl FileOperations for Cpiofs {
229 fn open(&self, path: &str, _flags: u32) -> Result<Arc<dyn crate::fs::FileHandle>> {
230 let path = self.normalize_path(path);
231 let entries = self.entries.lock();
232 if let Some(entry) = entries.iter().find(|e| e.name == path) {
233 Ok(Arc::new(CpiofsFileHandle {
234 content: RwLock::new(entry.data.clone().unwrap_or_default()),
235 position: RwLock::new(0),
236 }))
237 } else {
238 Err(FileSystemError {
239 kind: FileSystemErrorKind::NotFound,
240 message: format!("File not found: {}", path),
241 })
242 }
243 }
244
245 fn read_dir(&self, _path: &str) -> Result<Vec<DirectoryEntry>> {
246 let path = self.normalize_path(_path);
247 let entries = self.entries.lock();
248
249 let filtered_entries: Vec<DirectoryEntry> = entries
251 .iter()
252 .filter_map(|e| {
253 let parent_path = e.name.rfind('/').map_or("", |idx| &e.name[..idx]);
255 if parent_path == path {
256 let file_name = e.name.rfind('/').map_or(&e.name[..], |idx| &e.name[idx + 1..]);
258 Some(DirectoryEntry {
259 name: file_name.to_string(),
260 file_type: e.file_type,
261 size: e.size,
262 metadata: None,
263 })
264 } else {
265 None
266 }
267 })
268 .collect();
269
270 if filtered_entries.is_empty() && path != "" && path != "/" {
271 return Err(FileSystemError {
273 kind: FileSystemErrorKind::NotFound,
274 message: format!("Directory not found: {}", _path),
275 });
276 }
277
278 Ok(filtered_entries)
279 }
280
281 fn create_file(&self, _path: &str, _file_type: FileType) -> Result<()> {
282 Err(FileSystemError {
283 kind: FileSystemErrorKind::ReadOnly,
284 message: "Initramfs is read-only".to_string(),
285 })
286 }
287
288 fn create_dir(&self, _path: &str) -> Result<()> {
289 Err(FileSystemError {
290 kind: FileSystemErrorKind::ReadOnly,
291 message: "Initramfs is read-only".to_string(),
292 })
293 }
294
295 fn remove(&self, _path: &str) -> Result<()> {
296 Err(FileSystemError {
297 kind: FileSystemErrorKind::ReadOnly,
298 message: "Initramfs is read-only".to_string(),
299 })
300 }
301
302 fn metadata(&self, path: &str) -> Result<FileMetadata> {
303 let path = self.normalize_path(path);
304 let entries = self.entries.lock();
305 if let Some(entry) = entries.iter().find(|e| e.name == path) {
306 Ok(FileMetadata {
307 file_type: entry.file_type,
308 size: entry.size,
309 permissions: crate::fs::FilePermission {
310 read: true,
311 write: false,
312 execute: false,
313 },
314 created_time: 0,
315 modified_time: entry.modified_time,
316 accessed_time: 0,
317 })
318 } else {
319 Err(FileSystemError {
320 kind: FileSystemErrorKind::NotFound,
321 message: format!("File not found: {}", path),
322 })
323 }
324 }
325
326 fn root_dir(&self) -> Result<crate::fs::Directory> {
327 Ok(Directory::open(self.mount_point.clone() + "/"))
328 }
329}
330
331struct CpiofsFileHandle {
332 content: RwLock<Vec<u8>>,
333 position: RwLock<usize>,
334}
335
336impl FileHandle for CpiofsFileHandle {
337 fn read(&self, buffer: &mut [u8]) -> Result<usize> {
338 let content = self.content.read();
339 let mut position = self.position.write();
340 let available = content.len() - *position;
341 let to_read = buffer.len().min(available);
342 buffer[..to_read].copy_from_slice(&content[*position..*position + to_read]);
343 *position += to_read;
344 Ok(to_read)
345 }
346
347 fn write(&self, _buffer: &[u8]) -> Result<usize> {
348 Err(FileSystemError {
349 kind: FileSystemErrorKind::ReadOnly,
350 message: "Initramfs is read-only".to_string(),
351 })
352 }
353
354 fn seek(&self, whence: crate::fs::SeekFrom) -> Result<u64> {
355 let mut position = self.position.write();
356 let content = self.content.read();
357 let new_pos = match whence {
358 crate::fs::SeekFrom::Start(offset) => offset as usize,
359 crate::fs::SeekFrom::Current(offset) => {
360 if offset < 0 && *position < offset.abs() as usize {
361 0
362 } else if offset < 0 {
363 *position - offset.abs() as usize
364 } else {
365 *position + offset as usize
366 }
367 },
368 crate::fs::SeekFrom::End(offset) => {
369 let end = content.len();
370 if offset < 0 && end < offset.abs() as usize {
371 0
372 } else if offset < 0 {
373 end - offset.abs() as usize
374 } else {
375 end + offset as usize
376 }
377 },
378 };
379
380 *position = new_pos;
381 Ok(*position as u64)
382 }
383
384 fn release(&self) -> Result<()> {
385 Ok(())
386 }
387
388 fn metadata(&self) -> Result<FileMetadata> {
389 let content = self.content.read();
390 Ok(FileMetadata {
391 file_type: FileType::RegularFile,
392 size: content.len(),
393 permissions: crate::fs::FilePermission {
394 read: true,
395 write: false,
396 execute: false,
397 },
398 created_time: 0,
399 modified_time: 0,
400 accessed_time: 0,
401 })
402 }
403}
404
405pub struct CpiofsDriver;
409
410impl FileSystemDriver for CpiofsDriver {
411 fn name(&self) -> &'static str {
412 "cpiofs"
413 }
414
415 fn filesystem_type(&self) -> FileSystemType {
417 FileSystemType::Memory
418 }
419
420 fn create_from_memory(&self, memory_area: &MemoryArea) -> Result<Box<dyn VirtualFileSystem>> {
431 let data = unsafe { memory_area.as_slice() };
432 match Cpiofs::new("cpiofs", data) {
434 Ok(cpio_fs) => Ok(Box::new(cpio_fs)),
435 Err(err) => Err(FileSystemError {
436 kind: FileSystemErrorKind::InvalidData,
437 message: format!("Failed to create CPIO filesystem from memory: {}", err.message),
438 })
439 }
440 }
441
442 fn create_with_params(&self, params: &dyn crate::fs::params::FileSystemParams) -> Result<Box<dyn VirtualFileSystem>> {
443 use crate::fs::params::*;
444
445 if let Some(_cpio_params) = params.as_any().downcast_ref::<CpioFSParams>() {
447 return Err(FileSystemError {
449 kind: FileSystemErrorKind::NotSupported,
450 message: "CPIO filesystem requires memory area for creation. Use create_from_memory instead.".to_string(),
451 });
452 }
453
454 if let Some(_basic_params) = params.as_any().downcast_ref::<BasicFSParams>() {
456 return Err(FileSystemError {
457 kind: FileSystemErrorKind::NotSupported,
458 message: "CPIO filesystem requires memory area for creation. Use create_from_memory instead.".to_string(),
459 });
460 }
461
462 Err(FileSystemError {
464 kind: FileSystemErrorKind::NotSupported,
465 message: "CPIO filesystem requires CpioFSParams and memory area for creation".to_string(),
466 })
467 }
468}
469
470fn register_driver() {
471 let fs_driver_manager = get_fs_driver_manager();
472 fs_driver_manager.register_driver(Box::new(CpiofsDriver));
473}
474
475driver_initcall!(register_driver);
476
477#[cfg(test)]
478mod tests;