Optimize the CpioEntry to send data to the Write trait

This commit is contained in:
LI Qing 2023-07-21 16:04:12 +08:00 committed by Tate, Hongliang Tian
parent 4b3cf8daeb
commit 13c4c614b5
11 changed files with 275 additions and 117 deletions

112
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }
core2 = { version = "0.4", default_features = false, features = ["alloc"] }
lending-iterator = "0.1.7"

View File

@ -9,4 +9,17 @@ pub enum Error {
FileTypeError,
FileNameError,
BufferShortError,
IoError,
}
impl From<core2::io::Error> for Error {
#[inline]
fn from(err: core2::io::Error) -> Self {
use core2::io::ErrorKind;
match err.kind() {
ErrorKind::UnexpectedEof => Self::BufferShortError,
_ => Self::IoError,
}
}
}

View File

@ -4,10 +4,11 @@
//!
//! ```rust
//! use cpio_decoder::CpioDecoder;
//! use lending_iterator::LendingIterator;
//!
//! let short_buffer: Vec<u8> = 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<R> {
inner: R,
reader: R,
is_error: bool,
}
impl<R> CpioDecoder<R>
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<R> {
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<R> LendingIterator for CpioDecoder<R>
where
R: Read,
{
type Item = Result<CpioEntry>;
type Item<'a> = Result<CpioEntry<'a, R>>;
fn next(&mut self) -> Option<Result<CpioEntry>> {
/// Stops if reaches to the trailer entry or encounters an error.
fn next<'a>(self: &'a mut Self) -> Option<Self::Item<'a>> {
// 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<u8>,
reader: &'a mut R,
data_padding_len: usize,
}
impl CpioEntry {
fn new<R>(reader: &mut R) -> Result<Self>
where
R: Read,
{
let (metadata, name, data) = {
impl<'a, R> CpioEntry<'a, R>
where
R: Read,
{
fn new(reader: &'a mut R) -> Result<Self> {
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<u8> = 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<W>(&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::<Self>()];
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(),

View File

@ -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<u8> = Vec::new();
assert!(entry.read_all(&mut buffer).is_ok());
}
#[test]
fn test_short_buffer() {
let short_buffer: Vec<u8> = 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));
}

View File

@ -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"

View File

@ -232,6 +232,9 @@ impl From<cpio_decoder::error::Error> 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")
}
}
}
}

View File

@ -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<u8> = 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_);
}
}
}

View File

@ -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;

View File

@ -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<usize> {
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(())
}
}

View File

@ -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()