// SPDX-License-Identifier: MPL-2.0 use crate::prelude::*; use super::fs_resolver::{FsPath, FsResolver}; use super::procfs::ProcFS; use super::ramfs::RamFS; use super::utils::{FileSystem, InodeMode, InodeType, MountNode}; use cpio_decoder::{CpioDecoder, FileType}; use lending_iterator::LendingIterator; use libflate::gzip::Decoder as GZipDecoder; use spin::Once; /// Unpack and prepare the rootfs from the initramfs CPIO buffer. pub fn init(initramfs_buf: &[u8]) -> Result<()> { init_root_mount(); println!("[kernel] unpacking the initramfs.cpio.gz to rootfs ..."); let fs = FsResolver::new(); let mut decoder = CpioDecoder::new( GZipDecoder::new(initramfs_buf) .map_err(|_| Error::with_message(Errno::EINVAL, "invalid gzip buffer"))?, ); loop { let Some(entry_result) = decoder.next() else { break; }; let mut entry = entry_result?; // Make sure the name is a relative path, and is not end with "/". let entry_name = entry.name().trim_start_matches('/').trim_end_matches('/'); if entry_name.is_empty() { return_errno_with_message!(Errno::EINVAL, "invalid entry name"); } if entry_name == "." { continue; } // Here we assume that the directory referred by "prefix" must has been created. // The basis of this assumption is: // The mkinitramfs script uses `find` command to ensure that the entries are // sorted that a directory always appears before its child directories and files. let (parent, name) = if let Some((prefix, last)) = entry_name.rsplit_once('/') { (fs.lookup(&FsPath::try_from(prefix)?)?, last) } else { (fs.root().clone(), entry_name) }; let metadata = entry.metadata(); let mode = InodeMode::from_bits_truncate(metadata.permission_mode()); match metadata.file_type() { FileType::File => { let dentry = parent.create(name, InodeType::File, mode)?; entry.read_all(dentry.inode().writer(0))?; } FileType::Dir => { let _ = parent.create(name, InodeType::Dir, mode)?; } FileType::Link => { let dentry = parent.create(name, InodeType::SymLink, mode)?; let link_content = { let mut link_data: Vec = Vec::new(); entry.read_all(&mut link_data)?; core::str::from_utf8(&link_data)?.to_string() }; dentry.inode().write_link(&link_content)?; } type_ => { panic!("unsupported file type = {:?} in initramfs", type_); } } } // Mount ProcFS let proc_dentry = fs.lookup(&FsPath::try_from("/proc")?)?; proc_dentry.mount(ProcFS::new())?; // Mount DevFS let dev_dentry = fs.lookup(&FsPath::try_from("/dev")?)?; dev_dentry.mount(RamFS::new())?; println!("[kernel] rootfs is ready"); Ok(()) } pub fn mount_fs_at(fs: Arc, fs_path: &FsPath) -> Result<()> { let target_dentry = FsResolver::new().lookup(fs_path)?; target_dentry.mount(fs)?; Ok(()) } static ROOT_MOUNT: Once> = Once::new(); pub fn init_root_mount() { ROOT_MOUNT.call_once(|| -> Arc { let rootfs = RamFS::new(); MountNode::new_root(rootfs) }); } pub fn root_mount() -> &'static Arc { ROOT_MOUNT.get().unwrap() }