diff --git a/Cargo.lock b/Cargo.lock index afe9eb44..b0e9e2df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,7 @@ version = "0.1.0" dependencies = [ "core2", "int-to-c-enum", + "lending-iterator", ] [[package]] @@ -285,6 +286,35 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "ext-trait" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d772df1c1a777963712fb68e014235e80863d6a91a85c4e06ba2d16243a310e5" +dependencies = [ + "ext-trait-proc_macros", +] + +[[package]] +name = "ext-trait-proc_macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab7934152eaf26aa5aa9f7371408ad5af4c31357073c9e84c3b9d7f11ad639a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "extension-traits" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a296e5a895621edf9fa8329c83aa1cb69a964643e36cf54d8d7a69b789089537" +dependencies = [ + "ext-trait", +] + [[package]] name = "fnv" version = "1.0.7" @@ -575,6 +605,7 @@ dependencies = [ "ascii", "bitflags", "controlled", + "core2", "cpio-decoder", "getrandom", "int-to-c-enum", @@ -589,6 +620,7 @@ dependencies = [ "jinux-util", "keyable-arc", "lazy_static", + "lending-iterator", "libflate", "log", "lru", @@ -661,6 +693,31 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lending-iterator" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc07588c853b50689205fb5c00498aa681d89828e0ce8cbd965ebc7a5d8ae260" +dependencies = [ + "extension-traits", + "lending-iterator-proc_macros", + "macro_rules_attribute", + "never-say-never", + "nougat", + "polonius-the-crab", +] + +[[package]] +name = "lending-iterator-proc_macros" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5445dd1c0deb1e97b8a16561d17fc686ca83e8411128fb036e9668a72d51b1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "libc" version = "0.2.146" @@ -725,6 +782,22 @@ dependencies = [ "hashbrown 0.13.1", ] +[[package]] +name = "macro_rules_attribute" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf0c9b980bf4f3a37fd7b1c066941dd1b1d0152ce6ee6e8fe8c49b9f6810d862" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58093314a45e00c77d5c508f76e77c3396afbbc0d01506e7fae47b018bac2b1d" + [[package]] name = "managed" version = "0.8.0" @@ -746,12 +819,45 @@ dependencies = [ "autocfg", ] +[[package]] +name = "never-say-never" +version = "6.6.666" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5a574dadd7941adeaa71823ecba5e28331b8313fb2e1c6a5c7e5981ea53ad6" + +[[package]] +name = "nougat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b57b9ced431322f054fc673f1d3c7fa52d80efd9df74ad2fc759f044742510" +dependencies = [ + "macro_rules_attribute", + "nougat-proc_macros", +] + +[[package]] +name = "nougat-proc_macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84f77a45e99a2f9b492695d99e1c23844619caa5f3e57647cffacad773ca257" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "once_cell" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pod" version = "0.1.0" @@ -770,6 +876,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "polonius-the-crab" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a69ee997a6282f8462abf1e0d8c38c965e968799e912b3bed8c9e8a28c2f9f" + [[package]] name = "proc-macro-error" version = "1.0.4" diff --git a/services/libs/cpio-decoder/Cargo.toml b/services/libs/cpio-decoder/Cargo.toml index 5905bf07..3b47f1b9 100644 --- a/services/libs/cpio-decoder/Cargo.toml +++ b/services/libs/cpio-decoder/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] int-to-c-enum = { path = "../../libs/int-to-c-enum" } -core2 = { version = "0.4", default_features = false, features = ["alloc"] } \ No newline at end of file +core2 = { version = "0.4", default_features = false, features = ["alloc"] } +lending-iterator = "0.1.7" \ No newline at end of file diff --git a/services/libs/cpio-decoder/src/error.rs b/services/libs/cpio-decoder/src/error.rs index 193f3afc..39efbae8 100644 --- a/services/libs/cpio-decoder/src/error.rs +++ b/services/libs/cpio-decoder/src/error.rs @@ -9,4 +9,17 @@ pub enum Error { FileTypeError, FileNameError, BufferShortError, + IoError, +} + +impl From for Error { + #[inline] + fn from(err: core2::io::Error) -> Self { + use core2::io::ErrorKind; + + match err.kind() { + ErrorKind::UnexpectedEof => Self::BufferShortError, + _ => Self::IoError, + } + } } diff --git a/services/libs/cpio-decoder/src/lib.rs b/services/libs/cpio-decoder/src/lib.rs index 3df0c03d..aa2d7a9d 100644 --- a/services/libs/cpio-decoder/src/lib.rs +++ b/services/libs/cpio-decoder/src/lib.rs @@ -4,10 +4,11 @@ //! //! ```rust //! use cpio_decoder::CpioDecoder; +//! use lending_iterator::LendingIterator; //! //! let short_buffer: Vec = Vec::new(); //! let mut decoder = CpioDecoder::new(short_buffer.as_slice()); -//! for entry_result in decoder.decode_entries() { +//! if let Some(entry_result) = decoder.next() { //! println!("The entry_result is: {:?}", entry_result); //! } //! ``` @@ -21,16 +22,17 @@ extern crate alloc; use crate::error::{Error, Result}; use alloc::string::{String, ToString}; use alloc::vec; -use alloc::vec::Vec; -use core2::io::Read; +use core::cmp::min; +use core2::io::{Read, Write}; use int_to_c_enum::TryFromInt; +use lending_iterator::prelude::*; pub mod error; #[cfg(test)] mod test; -/// A CPIO (the newc format) decoder. +/// A CPIO (the newc format) decoder to iterator over the results of CPIO entries. /// /// "newc" is the new portable format and CRC format. /// @@ -41,37 +43,16 @@ mod test; /// All the fields in the header are ISO 646 (approximately ASCII) strings /// of hexadecimal numbers, left padded, not NULL terminated. pub struct CpioDecoder { - inner: R, + reader: R, + is_error: bool, } impl CpioDecoder where R: Read, { - /// create a decoder to decode the CPIO. - pub fn new(inner: R) -> Self { - Self { inner } - } - - /// Return an iterator trying to decode the entries in the CPIO. - pub fn decode_entries(&mut self) -> CpioEntryIter { - CpioEntryIter::new(&mut self.inner) - } -} - -/// An iterator over the results of CPIO entries. -/// -/// It stops if reaches to the trailer entry or encounters an error. -pub struct CpioEntryIter<'a, R> { - reader: &'a mut R, - is_error: bool, -} - -impl<'a, R> CpioEntryIter<'a, R> -where - R: Read, -{ - fn new(reader: &'a mut R) -> Self { + /// Create a decoder. + pub fn new(reader: R) -> Self { Self { reader, is_error: false, @@ -79,19 +60,21 @@ where } } -impl<'a, R> Iterator for CpioEntryIter<'a, R> +#[gat] +impl LendingIterator for CpioDecoder where R: Read, { - type Item = Result; + type Item<'a> = Result>; - fn next(&mut self) -> Option> { + /// Stops if reaches to the trailer entry or encounters an error. + fn next<'a>(self: &'a mut Self) -> Option> { // Stop to iterate entries if encounters an error. if self.is_error { return None; } - let entry_result = CpioEntry::new(self.reader); + let entry_result = CpioEntry::new(&mut self.reader); match &entry_result { Ok(entry) => { // A correct CPIO buffer must end with a trailer. @@ -109,25 +92,24 @@ where /// A file entry in the CPIO. #[derive(Debug)] -pub struct CpioEntry { +pub struct CpioEntry<'a, R> { metadata: FileMetadata, name: String, - data: Vec, + reader: &'a mut R, + data_padding_len: usize, } -impl CpioEntry { - fn new(reader: &mut R) -> Result - where - R: Read, - { - let (metadata, name, data) = { +impl<'a, R> CpioEntry<'a, R> +where + R: Read, +{ + fn new(reader: &'a mut R) -> Result { + let (metadata, name, data_padding_len) = { let header = Header::new(reader)?; let name = { let name_size = read_hex_bytes_to_u32(&header.name_size)? as usize; let mut name_bytes = vec![0u8; name_size]; - reader - .read_exact(&mut name_bytes) - .map_err(|_| Error::BufferShortError)?; + reader.read_exact(&mut name_bytes)?; let name = core::ffi::CStr::from_bytes_with_nul(&name_bytes) .map_err(|_| Error::FileNameError)?; name.to_str().map_err(|_| Error::Utf8Error)?.to_string() @@ -137,38 +119,22 @@ impl CpioEntry { } else { FileMetadata::new(&header)? }; - let data = { - let pad_header_len = align_up_pad(header.len() + name.len() + 1, 4); - if pad_header_len > 0 { - let mut pad_buf = vec![0u8; pad_header_len]; - reader - .read_exact(&mut pad_buf) - .map_err(|_| Error::BufferShortError)?; + let data_padding_len = { + let header_padding_len = align_up_pad(header.len() + name.len() + 1, 4); + if header_padding_len > 0 { + let mut pad_buf = vec![0u8; header_padding_len]; + reader.read_exact(&mut pad_buf)?; } - let mut data: Vec = Vec::new(); - let data_size = metadata.size as usize; - if data_size > 0 { - data.resize_with(data_size, Default::default); - reader - .read_exact(&mut data) - .map_err(|_| Error::BufferShortError)?; - } - data + align_up_pad(metadata.size() as usize, 4) }; - let pad_data_len = align_up_pad(data.len(), 4); - if pad_data_len > 0 { - let mut pad_buf = vec![0u8; pad_data_len]; - reader - .read_exact(&mut pad_buf) - .map_err(|_| Error::BufferShortError)?; - } - (metadata, name, data) + (metadata, name, data_padding_len) }; Ok(Self { metadata, name, - data, + reader, + data_padding_len, }) } @@ -182,12 +148,28 @@ impl CpioEntry { &self.name } - /// The data of the file. - pub fn data(&self) -> &[u8] { - &self.data + /// Read all data to the writer. + pub fn read_all(&mut self, mut writer: W) -> Result<()> + where + W: Write, + { + let data_len = self.metadata().size() as usize; + let mut send_len = 0; + let mut buffer = vec![0u8; 0x1000]; + while send_len < data_len { + let len = min(buffer.len(), data_len - send_len); + self.reader.read_exact(&mut buffer[..len])?; + writer.write_all(&mut buffer[..len])?; + send_len += len; + } + if self.data_padding_len > 0 { + self.reader + .read_exact(&mut buffer[..self.data_padding_len])?; + } + Ok(()) } - fn is_trailer(&self) -> bool { + pub fn is_trailer(&self) -> bool { &self.name == TRAILER_NAME } } @@ -344,9 +326,7 @@ impl Header { R: Read, { let mut buf = vec![0u8; core::mem::size_of::()]; - reader - .read_exact(&mut buf) - .map_err(|_| Error::BufferShortError)?; + reader.read_exact(&mut buf)?; let header = Self { magic: <[u8; 6]>::try_from(&buf[0..6]).unwrap(), diff --git a/services/libs/cpio-decoder/src/test.rs b/services/libs/cpio-decoder/src/test.rs index ef376ac8..2573eb46 100644 --- a/services/libs/cpio-decoder/src/test.rs +++ b/services/libs/cpio-decoder/src/test.rs @@ -1,5 +1,6 @@ use super::error::*; use super::{CpioDecoder, FileType}; +use lending_iterator::LendingIterator; #[test] fn test_decoder() { @@ -24,48 +25,55 @@ fn test_decoder() { output.stdout }; - assert!(CpioDecoder::new(buffer.as_slice()).decode_entries().count() > 3); let mut decoder = CpioDecoder::new(buffer.as_slice()); - for (idx, entry_result) in decoder.decode_entries().enumerate() { - let entry = entry_result.unwrap(); - if idx == 0 { - assert!(entry.name() == "."); - assert!(entry.metadata().file_type() == FileType::Dir); - assert!(entry.metadata().ino() > 0); - } - if idx == 1 { - assert!(entry.name() == "src"); - assert!(entry.metadata().file_type() == FileType::Dir); - assert!(entry.metadata().ino() > 0); - } - if idx == 2 { - assert!( - entry.name() == "src/lib.rs" - || entry.name() == "src/test.rs" - || entry.name() == "src/error.rs" - ); - assert!(entry.metadata().file_type() == FileType::File); - assert!(entry.metadata().ino() > 0); - } - } + // 1st entry + let entry = { + let entry_result = decoder.next().unwrap(); + entry_result.unwrap() + }; + assert!(entry.name() == "."); + assert!(entry.metadata().file_type() == FileType::Dir); + assert!(entry.metadata().ino() > 0); + // 2nd entry + let entry = { + let entry_result = decoder.next().unwrap(); + entry_result.unwrap() + }; + assert!(entry.name() == "src"); + assert!(entry.metadata().file_type() == FileType::Dir); + assert!(entry.metadata().ino() > 0); + + // 3rd entry + let mut entry = { + let entry_result = decoder.next().unwrap(); + entry_result.unwrap() + }; + assert!( + entry.name() == "src/lib.rs" + || entry.name() == "src/test.rs" + || entry.name() == "src/error.rs" + ); + assert!(entry.metadata().file_type() == FileType::File); + assert!(entry.metadata().ino() > 0); + assert!(entry.metadata().size() > 0); + let mut buffer: Vec = Vec::new(); + assert!(entry.read_all(&mut buffer).is_ok()); } #[test] fn test_short_buffer() { let short_buffer: Vec = Vec::new(); let mut decoder = CpioDecoder::new(short_buffer.as_slice()); - for entry_result in decoder.decode_entries() { - assert!(entry_result.is_err()); - assert!(entry_result.err() == Some(Error::BufferShortError)); - } + let entry_result = decoder.next().unwrap(); + assert!(entry_result.is_err()); + assert!(entry_result.err() == Some(Error::BufferShortError)); } #[test] fn test_invalid_buffer() { let buffer: &[u8] = b"invalidmagic.invalidmagic.invalidmagic.invalidmagic.invalidmagic.invalidmagic.invalidmagic.invalidmagic.invalidmagic.invalidmagic"; let mut decoder = CpioDecoder::new(buffer); - for entry_result in decoder.decode_entries() { - assert!(entry_result.is_err()); - assert!(entry_result.err() == Some(Error::MagicError)); - } + let entry_result = decoder.next().unwrap(); + assert!(entry_result.is_err()); + assert!(entry_result.err() == Some(Error::MagicError)); } diff --git a/services/libs/jinux-std/Cargo.toml b/services/libs/jinux-std/Cargo.toml index dc5fab1d..666cab4c 100644 --- a/services/libs/jinux-std/Cargo.toml +++ b/services/libs/jinux-std/Cargo.toml @@ -36,7 +36,8 @@ ringbuf = { version = "0.3.2", default-features = false, features = ["alloc"] } keyable-arc = { path = "../keyable-arc" } # unzip initramfs libflate = { git = "https://github.com/jinzhao-dev/libflate", rev = "b781da6", features = ["no_std"] } - +core2 = { version = "0.4", default_features = false, features = ["alloc"] } +lending-iterator = "0.1.7" spin = "0.9.4" vte = "0.10" lru = "0.9.0" diff --git a/services/libs/jinux-std/src/error.rs b/services/libs/jinux-std/src/error.rs index f0646159..c4b5f724 100644 --- a/services/libs/jinux-std/src/error.rs +++ b/services/libs/jinux-std/src/error.rs @@ -232,6 +232,9 @@ impl From for Error { cpio_decoder::error::Error::BufferShortError => { Error::with_message(Errno::EINVAL, "CPIO buffer is too short") } + cpio_decoder::error::Error::IoError => { + Error::with_message(Errno::EIO, "CPIO buffer I/O error") + } } } } diff --git a/services/libs/jinux-std/src/fs/initramfs.rs b/services/libs/jinux-std/src/fs/initramfs.rs index e4279caa..df896427 100644 --- a/services/libs/jinux-std/src/fs/initramfs.rs +++ b/services/libs/jinux-std/src/fs/initramfs.rs @@ -3,6 +3,7 @@ use super::utils::{InodeMode, InodeType}; use crate::prelude::*; use cpio_decoder::{CpioDecoder, FileType}; +use lending_iterator::LendingIterator; use libflate::gzip::Decoder as GZipDecoder; /// Unpack and prepare the fs from the ramdisk CPIO buffer. @@ -13,8 +14,13 @@ pub fn init(gzip_ramdisk_buf: &[u8]) -> Result<()> { GZipDecoder::new(gzip_ramdisk_buf) .map_err(|_| Error::with_message(Errno::EINVAL, "invalid gzip buffer"))?, ); - for entry_result in decoder.decode_entries() { - let entry = entry_result?; + + 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('/'); @@ -40,18 +46,22 @@ pub fn init(gzip_ramdisk_buf: &[u8]) -> Result<()> { match metadata.file_type() { FileType::File => { let dentry = parent.create(name, InodeType::File, mode)?; - dentry.vnode().write_at(0, entry.data())?; + entry.read_all(dentry.vnode().writer(0))?; } FileType::Dir => { let _ = parent.create(name, InodeType::Dir, mode)?; } FileType::Link => { let dentry = parent.create(name, InodeType::SymLink, mode)?; - let link_content = core::str::from_utf8(entry.data())?; - dentry.vnode().write_link(link_content)?; + 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.vnode().write_link(&link_content)?; } type_ => { - warn!("unsupported file type = {:?} in initramfs", type_); + panic!("unsupported file type = {:?} in initramfs", type_); } } } diff --git a/services/libs/jinux-std/src/fs/utils/mod.rs b/services/libs/jinux-std/src/fs/utils/mod.rs index ebf9d094..14b61df1 100644 --- a/services/libs/jinux-std/src/fs/utils/mod.rs +++ b/services/libs/jinux-std/src/fs/utils/mod.rs @@ -14,7 +14,7 @@ pub use ioctl::IoctlCmd; pub use page_cache::PageCache; pub use poll::{Pollee, Poller}; pub use status_flags::StatusFlags; -pub use vnode::Vnode; +pub use vnode::{Vnode, VnodeWriter}; mod access_mode; mod channel; diff --git a/services/libs/jinux-std/src/fs/utils/vnode.rs b/services/libs/jinux-std/src/fs/utils/vnode.rs index 78770183..806593ca 100644 --- a/services/libs/jinux-std/src/fs/utils/vnode.rs +++ b/services/libs/jinux-std/src/fs/utils/vnode.rs @@ -8,6 +8,7 @@ use crate::vm::vmo::Vmo; use alloc::string::String; use core::time::Duration; +use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write}; use jinux_frame::vm::VmIo; use jinux_rights::Full; @@ -245,4 +246,33 @@ impl Vnode { pub fn is_dentry_cacheable(&self) -> bool { self.inner.read().inode.is_dentry_cacheable() } + + pub fn writer(&self, from_offset: usize) -> VnodeWriter { + VnodeWriter { + inner: &self, + offset: from_offset, + } + } +} + +pub struct VnodeWriter<'a> { + inner: &'a Vnode, + offset: usize, +} + +impl<'a> Write for VnodeWriter<'a> { + #[inline] + fn write(&mut self, buf: &[u8]) -> IoResult { + let write_len = self + .inner + .write_at(self.offset, buf) + .map_err(|_| IoError::new(IoErrorKind::WriteZero, "failed to write buffer"))?; + self.offset += write_len; + Ok(write_len) + } + + #[inline] + fn flush(&mut self) -> IoResult<()> { + Ok(()) + } } diff --git a/services/libs/jinux-std/src/vm/vmo/mod.rs b/services/libs/jinux-std/src/vm/vmo/mod.rs index a494eba7..ada10240 100644 --- a/services/libs/jinux-std/src/vm/vmo/mod.rs +++ b/services/libs/jinux-std/src/vm/vmo/mod.rs @@ -216,7 +216,7 @@ impl VmoInner { if self.should_share_frame_with_parent(write_page) { return Ok(inherited_frame); } - + let frame = { let options = VmAllocOptions::new(1); VmFrameVec::allocate(&options)?.pop().unwrap()