From 89b8fb8984cf96c418880b7dcd9ce3d6afc3f71c Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 26 Mar 2022 18:15:49 +0100 Subject: [PATCH] Refactor into a separate library and tools crate. --- Cargo.toml | 2 +- src/lib.rs | 7 ++ src/types.rs | 7 +- tests/archive_and_read.rs | 2 +- {redox-initfs-ar => tools}/Cargo.toml | 9 ++ .../archive.rs => tools/src/archive_common.rs | 56 +++++------ .../src/main.rs => tools/src/bin/archive.rs | 11 ++- tools/src/bin/dump.rs | 96 +++++++++++++++++++ 8 files changed, 148 insertions(+), 42 deletions(-) rename {redox-initfs-ar => tools}/Cargo.toml (74%) rename redox-initfs-ar/src/archive.rs => tools/src/archive_common.rs (91%) rename redox-initfs-ar/src/main.rs => tools/src/bin/archive.rs (86%) create mode 100644 tools/src/bin/dump.rs diff --git a/Cargo.toml b/Cargo.toml index d27ae0e6da..5de53b06c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,5 +27,5 @@ anyhow = "1" [workspace] members = [ - "redox-initfs-ar", + "tools", ] diff --git a/src/lib.rs b/src/lib.rs index 9a1682b800..50c63e892b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,9 @@ impl<'initfs> InitFs<'initfs> { plain::from_bytes::
(&self.base[..core::mem::size_of::
()]) .expect("expected header type to require no alignment, and size to be sufficient") } + pub fn header(&self) -> &Header { + self.get_header_assume_valid() + } fn header_len_8() -> u8 { core::mem::size_of::
() .try_into() @@ -248,6 +251,10 @@ impl<'initfs> InitFs<'initfs> { } pub const ROOT_INODE: Inode = Inode(0); + pub fn all_inodes(&self) -> impl Iterator { + (0..self.inode_count()).map(Inode) + } + pub fn inode_count(&self) -> u16 { self.get_header_assume_valid().inode_count.get() } diff --git a/src/types.rs b/src/types.rs index 405387f1e3..24508fcfbc 100644 --- a/src/types.rs +++ b/src/types.rs @@ -4,7 +4,7 @@ pub const MAGIC: [u8; 8] = *b"RedoxFtw"; macro_rules! primitive( ($wrapper:ident, $bits:expr, $primitive:ident) => { #[repr(transparent)] - #[derive(Clone, Copy, Debug, Default)] + #[derive(Clone, Copy, Default)] pub struct $wrapper([u8; $bits / 8]); impl $wrapper { @@ -31,6 +31,11 @@ macro_rules! primitive( wrapper.get() } } + impl core::fmt::Debug for $wrapper { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#0width$x}", self.get(), width = 2 * core::mem::size_of::<$primitive>()) + } + } } ); diff --git a/tests/archive_and_read.rs b/tests/archive_and_read.rs index d7bd1e2d3c..d26df6c71a 100644 --- a/tests/archive_and_read.rs +++ b/tests/archive_and_read.rs @@ -4,7 +4,7 @@ use std::path::Path; use anyhow::{anyhow, bail, Context, Result}; -#[path = "../redox-initfs-ar/src/archive.rs"] +#[path = "../tools/src/archive_common.rs"] mod archive; #[test] diff --git a/redox-initfs-ar/Cargo.toml b/tools/Cargo.toml similarity index 74% rename from redox-initfs-ar/Cargo.toml rename to tools/Cargo.toml index 7b711b9ff4..82330525c9 100644 --- a/redox-initfs-ar/Cargo.toml +++ b/tools/Cargo.toml @@ -8,11 +8,20 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +path = "src/bin/archive.rs" +name = "redox-initfs-ar" + +[[bin]] +path = "src/bin/dump.rs" +name = "redox-initfs-dump" + [dependencies] anyhow = "1" clap = "2.33" env_logger = "0.8" log = "0.4" plain = "0.2" +twox-hash = "1.6" redox-initfs = { path = ".." } diff --git a/redox-initfs-ar/src/archive.rs b/tools/src/archive_common.rs similarity index 91% rename from redox-initfs-ar/src/archive.rs rename to tools/src/archive_common.rs index b006fa6154..a340aeecad 100644 --- a/redox-initfs-ar/src/archive.rs +++ b/tools/src/archive_common.rs @@ -34,11 +34,12 @@ struct State<'path> { max_size: u64, inode_count: u16, buffer: Box<[u8]>, + inode_table_offset: u32, } fn write_all_at(file: &File, buf: &[u8], offset: u64, r#where: &str) -> Result<()> { file.write_all_at(buf, offset)?; - log::debug!("Wrote {}..{} within {}", offset, offset + buf.len() as u64, r#where); + log::trace!("Wrote {}..{} within {}", offset, offset + buf.len() as u64, r#where); Ok(()) } @@ -176,8 +177,7 @@ fn write_inode( ty: initfs::InodeType, metadata: &Metadata, write_result: WriteResult, - inode_table_offset: u32, - index: u16, + inode: u16, ) -> Result<()> { let inode_size: u32 = std::mem::size_of::() .try_into() @@ -188,23 +188,23 @@ fn write_inode( // TODO: Use main buffer and write in bulk. let mut inode_buf = [0_u8; std::mem::size_of::()]; - let inode = plain::from_mut_bytes::(&mut inode_buf) + let inode_hdr = plain::from_mut_bytes::(&mut inode_buf) .expect("expected inode struct to have alignment 1, and buffer size to match"); - *inode = initfs::InodeHeader { + *inode_hdr = initfs::InodeHeader { type_and_mode: type_and_mode.into(), length: write_result.size.into(), offset: initfs::Offset(write_result.offset.into()), - gid: metadata.gid().into(), - uid: metadata.uid().into(), + gid: 0.into(),//metadata.gid().into(), + uid: 0.into(),//metadata.uid().into(), }; - log::debug!("Writing inode index {} from offset {}", index, inode_table_offset); + log::debug!("Writing inode index {} from offset {}", inode, state.inode_table_offset); write_all_at( &*state.file, &inode_buf, - u64::from(inode_table_offset + u32::from(index) * inode_size), + u64::from(state.inode_table_offset + u32::from(inode) * inode_size), "write_inode", ) .context("failed to write inode struct to disk image") @@ -212,8 +212,7 @@ fn write_inode( fn allocate_and_write_dir( state: &mut State, dir: &Dir, - inode_table_offset: u32, - start_inode: &mut u16, + current_inode: &mut u16, ) -> Result { let entry_size = u16::try_from(std::mem::size_of::()).context("entry size too large")?; @@ -228,22 +227,11 @@ fn allocate_and_write_dir( .try_into() .context("directory entries offset too high")?; - let this_start_inode = *start_inode; - - let inode_size: u32 = std::mem::size_of::() - .try_into() - .expect("inode header length cannot fit within u32"); - - *start_inode += entry_count; - - let inode_table_offset = - inode_table_offset + u32::from(this_start_inode) * u32::from(inode_size); - for (index, entry) in dir.entries.iter().enumerate() { let (write_result, ty) = match entry.kind { EntryKind::Dir(ref subdir) => { let write_result = - allocate_and_write_dir(state, subdir, inode_table_offset, start_inode) + allocate_and_write_dir(state, subdir, current_inode) .with_context(|| { anyhow!( "failed to copy directory entries from `{}` into image", @@ -266,13 +254,13 @@ fn allocate_and_write_dir( .try_into() .expect("expected dir entry count not to exceed u32"); + *current_inode += 1; write_inode( state, ty, &entry.metadata, write_result, - inode_table_offset, - index, + *current_inode, )?; let (name_offset, name_len) = { @@ -293,10 +281,10 @@ fn allocate_and_write_dir( let direntry = plain::from_mut_bytes::(&mut direntry_buf) .expect("expected dir entry struct to have alignment 1, and buffer size to match"); - let inode = this_start_inode + index; + log::debug!("Linking inode {} into dir entry index {}, file name `{}`", current_inode, index, String::from_utf8_lossy(&entry.name)); *direntry = initfs::DirEntry { - inode: inode.into(), + inode: (*current_inode).into(), name_len: name_len.into(), name_offset: initfs::Offset(name_offset.into()), }; @@ -319,13 +307,12 @@ fn allocate_and_write_dir( fn allocate_contents_and_write_inodes( state: &mut State, dir: &Dir, - inode_table_offset: u32, root_metadata: Metadata, ) -> Result<()> { - let index = 0; - let mut start_inode = 1; + let start_inode = 0; + let mut current_inode = start_inode; - let write_result = allocate_and_write_dir(state, dir, inode_table_offset, &mut start_inode) + let write_result = allocate_and_write_dir(state, dir, &mut current_inode) .context("failed to allocate and write all directories and files")?; write_inode( @@ -333,8 +320,7 @@ fn allocate_contents_and_write_inodes( initfs::InodeType::Dir, &root_metadata, write_result, - inode_table_offset, - index, + start_inode, ) } @@ -409,6 +395,7 @@ pub fn archive(&Args { destination_path, max_size, source }: &Args) -> Result<() // Include root directory. inode_count: 1, buffer: vec![0_u8; BUFFER_SIZE].into_boxed_slice(), + inode_table_offset: 0, }; let root_path = source; @@ -449,10 +436,11 @@ pub fn archive(&Args { destination_path, max_size, source }: &Args) -> Result<() .into(), ); + state.inode_table_offset = inode_table_offset.0.get(); + allocate_contents_and_write_inodes( &mut state, &root, - inode_table_offset.0.get(), root_metadata, )?; diff --git a/redox-initfs-ar/src/main.rs b/tools/src/bin/archive.rs similarity index 86% rename from redox-initfs-ar/src/main.rs rename to tools/src/bin/archive.rs index 0f3349edf0..ac14f11bcd 100644 --- a/redox-initfs-ar/src/main.rs +++ b/tools/src/bin/archive.rs @@ -3,12 +3,13 @@ use std::path::Path; use anyhow::{Context, Result}; use clap::{App, Arg}; -mod archive; -use self::archive::*; +#[path = "../archive_common.rs"] +mod archive_common; +use self::archive_common::{self as archive, Args, DEFAULT_MAX_SIZE}; fn main() -> Result<()> { - let matches = App::new(clap::crate_name!()) - .about(clap::crate_description!()) + let matches = App::new("redox-initfs-ar") + .about("create an initfs image from a directory") .version(clap::crate_version!()) .author(clap::crate_authors!()) .arg( @@ -58,5 +59,5 @@ fn main() -> Result<()> { destination_path: Path::new(destination), max_size, }; - self::archive::archive(&args) + archive::archive(&args) } diff --git a/tools/src/bin/dump.rs b/tools/src/bin/dump.rs new file mode 100644 index 0000000000..8f7c20af75 --- /dev/null +++ b/tools/src/bin/dump.rs @@ -0,0 +1,96 @@ +use anyhow::{Context, Result}; +use clap::{App, Arg}; + +use redox_initfs::InitFs; + +fn main() -> Result<()> { + let matches = App::new("redox-initfs-dump") + .about("dump initfs metadata") + .version(clap::crate_version!()) + .author(clap::crate_authors!()) + .arg( + Arg::with_name("IMAGE") + .takes_value(true) + .required(true) + .help("specify the image to dump") + ) + .get_matches(); + + let source = matches + .value_of_os("IMAGE") + .expect("expected the required arg IMAGE to exist"); + + let bytes = std::fs::read(source).context("failed to read image into memory")?; + let initfs = InitFs::new(&bytes).context("failed to parse initfs header")?; + + dbg!(initfs.header()); + + for inode in initfs.all_inodes() { + print!("{:?}: ", inode); + + let inode_struct = match initfs.get_inode(inode) { + Some(s) => s, + None => { + println!("failed to obtain."); + continue; + } + }; + print!("mode={:#0o}, uid={}, gid={}, kind=", inode_struct.mode(), inode_struct.uid(), inode_struct.gid()); + + use redox_initfs::InodeKind; + + match inode_struct.kind() { + InodeKind::Unknown => println!("(unknown)"), + InodeKind::Dir(dir) => { + print!("dir{{"); + let ec = match dir.entry_count().ok() { + Some(c) => c, + None => { + println!("(failed to get entry count)}}"); + continue; + } + }; + println!("entries=["); + + for entry in 0..ec { + let entry = match dir.get_entry(entry).ok().flatten() { + Some(e) => e, + None => { + println!("\t(unknown),"); + continue; + } + }; + let name = match entry.name().ok() { + Some(name) => name, + None => { + println!("\t(unknown name),"); + continue; + } + }; + println!("\t`{}` => {:?},", String::from_utf8_lossy(name), entry.inode()); + } + + println!("]}}"); + } + InodeKind::File(file) => { + print!("file{{"); + + match file.data().ok() { + Some(d) => { + use std::hash::Hasher; + let mut hasher = twox_hash::XxHash64::with_seed(0); + hasher.write(d); + print!("len={}, hash={:#0x}", d.len(), hasher.finish()); + } + None => { + print!("(failed to get data)"); + } + } + + println!("}}"); + } + } + } + + Ok(()) +}