From 8b1fb36e6bb2b3087e3760750a88104d2eec7256 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:49:49 +0200 Subject: [PATCH 1/6] initfs: Fix test --- initfs/tools/tests/archive_and_read.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/initfs/tools/tests/archive_and_read.rs b/initfs/tools/tests/archive_and_read.rs index 42ec0079b9..ae260965f9 100644 --- a/initfs/tools/tests/archive_and_read.rs +++ b/initfs/tools/tests/archive_and_read.rs @@ -94,11 +94,11 @@ fn archive_and_read() -> Result<()> { let reference_tree = Node::dir([( b"foo", Node::dir([ + (b"file-link.txt".as_slice(), Node::link(b"file.txt")), ( b"file.txt".as_slice(), Node::file(b"This is a file meant to be used in a redox-initfs test.\n"), ), - (b"file-link.txt".as_slice(), Node::link(b"foo/file.txt")), ]), )]); From 42fee3ca8d74f0caff4fd2196028898239fc5c0f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 23 Apr 2026 19:04:27 +0200 Subject: [PATCH 2/6] initfs: Simplify archive cli --- initfs/tools/data/foo/bootstrap.elf | Bin 0 -> 28 bytes initfs/tools/src/bin/archive.rs | 27 ++++++--------------- initfs/tools/src/lib.rs | 32 +++++++++++-------------- initfs/tools/tests/archive_and_read.rs | 6 ++++- 4 files changed, 26 insertions(+), 39 deletions(-) create mode 100644 initfs/tools/data/foo/bootstrap.elf diff --git a/initfs/tools/data/foo/bootstrap.elf b/initfs/tools/data/foo/bootstrap.elf new file mode 100644 index 0000000000000000000000000000000000000000..0d77a7513aadff0cd5783fbd35a106f12c8cfa98 GIT binary patch literal 28 Pcmb<-^>JflWWWjlBm@Cj literal 0 HcmV?d00001 diff --git a/initfs/tools/src/bin/archive.rs b/initfs/tools/src/bin/archive.rs index 128e2ab6a9..11b54313b5 100644 --- a/initfs/tools/src/bin/archive.rs +++ b/initfs/tools/src/bin/archive.rs @@ -1,6 +1,6 @@ use std::path::Path; -use anyhow::{Context, Result}; +use anyhow::Result; use clap::{Arg, Command}; use redox_initfs_tools::{self as archive, Args, DEFAULT_MAX_SIZE}; @@ -10,13 +10,6 @@ fn main() -> Result<()> { .about("create an initfs image from a directory") .version(clap::crate_version!()) .author(clap::crate_authors!()) - .arg( - Arg::new("MAX_SIZE") - .long("max-size") - .short('m') - .required(false) - .help("Set the upper limit for how large the image can become (default 64 MiB)."), - ) // TODO: support non-utf8 paths (applies to other paths as well) .arg( Arg::new("SOURCE") @@ -25,7 +18,7 @@ fn main() -> Result<()> { ) .arg( Arg::new("BOOTSTRAP_CODE") - .required(false) + .required(true) .help("Specify the bootstrap ELF file to include in the image."), ) .arg( @@ -39,19 +32,13 @@ fn main() -> Result<()> { env_logger::init(); - let max_size = if let Some(max_size_str) = matches.get_one::("MAX_SIZE") { - max_size_str - .parse::() - .context("expected an integer for MAX_SIZE")? - } else { - DEFAULT_MAX_SIZE - }; - let source = matches .get_one::("SOURCE") .expect("expected the required arg SOURCE to exist"); - let bootstrap_code = matches.get_one::("BOOTSTRAP_CODE"); + let bootstrap_code = matches + .get_one::("BOOTSTRAP_CODE") + .expect("expected the required arg BOOTSTRAP_CODE to exist"); let destination = matches .get_one::("OUTPUT") @@ -59,9 +46,9 @@ fn main() -> Result<()> { let args = Args { source: Path::new(source), - bootstrap_code: bootstrap_code.map(Path::new), + bootstrap_code: Path::new(bootstrap_code), destination_path: Path::new(destination), - max_size, + max_size: DEFAULT_MAX_SIZE, }; archive::archive(&args) } diff --git a/initfs/tools/src/lib.rs b/initfs/tools/src/lib.rs index 00d9791168..f6d730899e 100644 --- a/initfs/tools/src/lib.rs +++ b/initfs/tools/src/lib.rs @@ -418,7 +418,7 @@ pub struct Args<'a> { pub destination_path: &'a Path, pub max_size: u64, pub source: &'a Path, - pub bootstrap_code: Option<&'a Path>, + pub bootstrap_code: &'a Path, } pub fn archive( &Args { @@ -479,26 +479,22 @@ pub fn archive( let header_offset = bump_alloc(&mut state, 4096, "allocate header")?; assert_eq!(header_offset, 0); - let bootstrap_entry = if let Some(bootstrap_code) = bootstrap_code { - allocate_and_write_file( - &mut state, - &File::open(bootstrap_code).with_context(|| { - anyhow!( - "failed to open bootstrap code file `{}`", - bootstrap_code.to_string_lossy(), - ) - })?, - )?; - let bootstrap_data = std::fs::read(bootstrap_code).with_context(|| { + allocate_and_write_file( + &mut state, + &File::open(bootstrap_code).with_context(|| { anyhow!( - "failed to read bootstrap code file `{}`", + "failed to open bootstrap code file `{}`", bootstrap_code.to_string_lossy(), ) - })?; - elf_entry(&bootstrap_data) - } else { - u64::MAX - }; + })?, + )?; + let bootstrap_data = std::fs::read(bootstrap_code).with_context(|| { + anyhow!( + "failed to read bootstrap code file `{}`", + bootstrap_code.to_string_lossy(), + ) + })?; + let bootstrap_entry = elf_entry(&bootstrap_data); let inode_table_length = { let inode_entry_size: u64 = std::mem::size_of::() diff --git a/initfs/tools/tests/archive_and_read.rs b/initfs/tools/tests/archive_and_read.rs index ae260965f9..cdbe505dbb 100644 --- a/initfs/tools/tests/archive_and_read.rs +++ b/initfs/tools/tests/archive_and_read.rs @@ -77,7 +77,7 @@ fn archive_and_read() -> Result<()> { let args = redox_initfs_tools::Args { destination_path: &Path::new(env!("CARGO_TARGET_TMPDIR")).join("out.img"), source: Path::new("data"), - bootstrap_code: None, + bootstrap_code: Path::new("data/foo/bootstrap.elf"), max_size: redox_initfs_tools::DEFAULT_MAX_SIZE, }; redox_initfs_tools::archive(&args).context("failed to archive")?; @@ -94,6 +94,10 @@ fn archive_and_read() -> Result<()> { let reference_tree = Node::dir([( b"foo", Node::dir([ + ( + b"bootstrap.elf".as_slice(), + Node::file("\x7FELF\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"), + ), (b"file-link.txt".as_slice(), Node::link(b"file.txt")), ( b"file.txt".as_slice(), From 7e4e3864e555107f9150b4cb26c6d2b1ad5e54f4 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:14:53 +0200 Subject: [PATCH 3/6] initfs: Write inode table at the end --- initfs/tools/src/lib.rs | 118 ++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/initfs/tools/src/lib.rs b/initfs/tools/src/lib.rs index f6d730899e..d1e0f7c333 100644 --- a/initfs/tools/src/lib.rs +++ b/initfs/tools/src/lib.rs @@ -8,7 +8,7 @@ use std::os::unix::fs::{FileExt, FileTypeExt, PermissionsExt}; use anyhow::{anyhow, bail, Context, Result}; -use redox_initfs::types as initfs; +use redox_initfs::types::{self as initfs, Offset}; pub const KIBIBYTE: u64 = 1024; pub const MEBIBYTE: u64 = KIBIBYTE * 1024; @@ -43,7 +43,7 @@ struct State<'path> { max_size: u64, inode_count: u16, buffer: Box<[u8]>, - inode_table_offset: u32, + inode_table: [Option; 65536], } fn write_all_at(file: &File, buf: &[u8], offset: u64, r#where: &str) -> Result<()> { @@ -235,40 +235,12 @@ fn allocate_and_write_link(state: &mut State, link: &Path) -> Result Result<()> { - let inode_size: u32 = std::mem::size_of::() - .try_into() - .expect("inode header length cannot fit within u32"); - - // TODO: Use main buffer and write in bulk. - let mut inode_buf = [0_u8; std::mem::size_of::()]; - - let inode_hdr = plain::from_mut_bytes::(&mut inode_buf) - .expect("expected inode struct to have alignment 1, and buffer size to match"); - - *inode_hdr = initfs::InodeHeader { +fn write_inode(state: &mut State, ty: initfs::InodeType, write_result: WriteResult, inode: u16) { + state.inode_table[usize::from(inode)] = Some(initfs::InodeHeader { type_: (ty as u32).into(), length: initfs::Length(write_result.size.into()), offset: initfs::Offset(write_result.offset.into()), - }; - - log::debug!( - "Writing inode index {} from offset {}", - inode, - state.inode_table_offset - ); - write_all_at( - &state.file, - &inode_buf, - u64::from(state.inode_table_offset + u32::from(inode) * inode_size), - "write_inode", - ) - .context("failed to write inode struct to disk image") + }); } fn allocate_and_write_dir( state: &mut State, @@ -328,7 +300,7 @@ fn allocate_and_write_dir( .expect("expected dir entry count not to exceed u32"); *current_inode += 1; - write_inode(state, ty, write_result, *current_inode)?; + write_inode(state, ty, write_result, *current_inode); let (name_offset, name_len) = { let name_len: u16 = entry.name.len().try_into().context("file name too long")?; @@ -377,14 +349,62 @@ fn allocate_and_write_dir( offset: entry_table_offset, }) } -fn allocate_contents_and_write_inodes(state: &mut State, dir: &Dir) -> Result<()> { +fn allocate_contents(state: &mut State, dir: &Dir) -> Result<()> { let start_inode = 0; let mut current_inode = 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(state, initfs::InodeType::Dir, write_result, start_inode) + write_inode(state, initfs::InodeType::Dir, write_result, start_inode); + + Ok(()) +} + +fn write_inode_table(state: &mut State) -> Result { + let inode_size: u32 = std::mem::size_of::() + .try_into() + .expect("inode header length cannot fit within u32"); + + let inode_table_length = { + u64::from(inode_size) + .checked_mul(u64::from(state.inode_count)) + .ok_or_else(|| anyhow!("inode table too large"))? + }; + + let inode_table_offset = bump_alloc(state, inode_table_length, "allocate inode table")?; + let inode_table_offset = + u32::try_from(inode_table_offset).with_context(|| "inode table located too far away")?; + + for (i, inode) in state.inode_table[..usize::from(state.inode_count)] + .iter() + .enumerate() + { + // TODO: Use main buffer and write in bulk. + let mut inode_buf = [0_u8; std::mem::size_of::()]; + + let inode_hdr = plain::from_mut_bytes::(&mut inode_buf) + .expect("expected inode struct to have alignment 1, and buffer size to match"); + + *inode_hdr = inode.unwrap(); + + log::debug!( + "Writing inode index {} from offset {}", + i, + inode_table_offset + ); + write_all_at( + &state.file, + &inode_buf, + u64::from(inode_table_offset + u32::try_from(i).unwrap() * inode_size), + "write_inode", + ) + .context("failed to write inode struct to disk image")?; + } + + let inode_table_offset = initfs::Offset(inode_table_offset.into()); + + Ok(inode_table_offset) } struct OutputImageGuard<'a> { @@ -467,7 +487,7 @@ pub fn archive( // Include root directory. inode_count: 1, buffer: vec![0_u8; BUFFER_SIZE].into_boxed_slice(), - inode_table_offset: 0, + inode_table: [None; 65536], }; let root_path = source; @@ -496,29 +516,9 @@ pub fn archive( })?; let bootstrap_entry = elf_entry(&bootstrap_data); - let inode_table_length = { - let inode_entry_size: u64 = std::mem::size_of::() - .try_into() - .expect("expected table entry size to fit"); + allocate_contents(&mut state, &root)?; - inode_entry_size - .checked_mul(u64::from(state.inode_count)) - .ok_or_else(|| anyhow!("inode table too large"))? - }; - - let inode_table_offset = bump_alloc(&mut state, inode_table_length, "allocate inode table")?; - - // Finally, write the header to the disk image. - - let inode_table_offset = initfs::Offset( - u32::try_from(inode_table_offset) - .with_context(|| "inode table located too far away")? - .into(), - ); - - state.inode_table_offset = inode_table_offset.0.get(); - - allocate_contents_and_write_inodes(&mut state, &root)?; + let inode_table_offset = write_inode_table(&mut state)?; let current_system_time = std::time::SystemTime::now(); From 108a9343e354aec24450589365487f6f4959c4dd Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:24:58 +0200 Subject: [PATCH 4/6] initfs: Move inode_count write from read_directory to allocate_contents --- initfs/tools/src/lib.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/initfs/tools/src/lib.rs b/initfs/tools/src/lib.rs index d1e0f7c333..7b973f2aa3 100644 --- a/initfs/tools/src/lib.rs +++ b/initfs/tools/src/lib.rs @@ -57,7 +57,7 @@ fn write_all_at(file: &File, buf: &[u8], offset: u64, r#where: &str) -> Result<( Ok(()) } -fn read_directory(state: &mut State, path: &Path, root_path: &Path) -> Result { +fn read_directory(path: &Path, root_path: &Path) -> Result { let read_dir = path .read_dir() .with_context(|| anyhow!("failed to read directory `{}`", path.to_string_lossy(),))?; @@ -106,7 +106,7 @@ fn read_directory(state: &mut State, path: &Path, root_path: &Path) -> Result Result Result<()> { write_inode(state, initfs::InodeType::Dir, write_result, start_inode); + state.inode_count = current_inode + 1; + Ok(()) } fn write_inode_table(state: &mut State) -> Result { + log::debug!("there are {} inodes", state.inode_count); + let inode_size: u32 = std::mem::size_of::() .try_into() .expect("inode header length cannot fit within u32"); @@ -448,6 +446,9 @@ pub fn archive( bootstrap_code, }: &Args, ) -> Result<()> { + let root_path = source; + let root = read_directory(root_path, root_path).context("failed to read root")?; + let previous_extension = destination_path.extension().map_or("", |ext| { ext.to_str() .expect("expected destination path to be valid UTF-8") @@ -490,11 +491,6 @@ pub fn archive( inode_table: [None; 65536], }; - let root_path = source; - let root = read_directory(&mut state, root_path, root_path).context("failed to read root")?; - - log::debug!("there are {} inodes", state.inode_count); - // NOTE: The header is always stored at offset zero. let header_offset = bump_alloc(&mut state, 4096, "allocate header")?; assert_eq!(header_offset, 0); From 2052b7021248c58d3f9c8a7ee54d5e078ba5b517 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:36:17 +0200 Subject: [PATCH 5/6] initfs: Allocate inodes in write_inode --- bootstrap/src/initfs.rs | 2 +- initfs/src/lib.rs | 5 ++-- initfs/src/types.rs | 1 + initfs/tools/src/lib.rs | 39 +++++++++++++++++--------- initfs/tools/tests/archive_and_read.rs | 2 +- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/bootstrap/src/initfs.rs b/bootstrap/src/initfs.rs index 209e771f37..c33918da06 100644 --- a/bootstrap/src/initfs.rs +++ b/bootstrap/src/initfs.rs @@ -135,7 +135,7 @@ impl SchemeSync for InitFsScheme { // filter out double slashes (e.g. /usr//bin/...) .filter(|c| !c.is_empty()); - let mut current_inode = InitFs::ROOT_INODE; + let mut current_inode = self.fs.root_inode(); while let Some(component) = components.next() { match component { diff --git a/initfs/src/lib.rs b/initfs/src/lib.rs index 16e3bc3d23..db7ed8b008 100644 --- a/initfs/src/lib.rs +++ b/initfs/src/lib.rs @@ -268,8 +268,9 @@ impl<'initfs> InitFs<'initfs> { plain::slice_from_bytes::(inode_table_bytes) .expect("expected inode struct alignment to be 1") } - pub const ROOT_INODE: Inode = Inode(0); - + pub fn root_inode(&self) -> Inode { + Inode(self.get_header_assume_valid().root_inode.get()) + } pub fn all_inodes(&self) -> impl Iterator { (0..self.inode_count()).map(Inode) } diff --git a/initfs/src/types.rs b/initfs/src/types.rs index 8ef4a87833..8dce929151 100644 --- a/initfs/src/types.rs +++ b/initfs/src/types.rs @@ -70,6 +70,7 @@ pub struct Header { pub bootstrap_entry: U64, pub initfs_size: U64, pub page_size: U16, + pub root_inode: U16, } const _: () = { diff --git a/initfs/tools/src/lib.rs b/initfs/tools/src/lib.rs index 7b973f2aa3..59d1afd4ab 100644 --- a/initfs/tools/src/lib.rs +++ b/initfs/tools/src/lib.rs @@ -229,12 +229,20 @@ fn allocate_and_write_link(state: &mut State, link: &Path) -> Result u16 { + let inode = *current_inode; + *current_inode += 1; state.inode_table[usize::from(inode)] = Some(initfs::InodeHeader { type_: (ty as u32).into(), length: initfs::Length(write_result.size.into()), offset: initfs::Offset(write_result.offset.into()), }); + inode } fn allocate_and_write_dir( state: &mut State, @@ -293,8 +301,7 @@ fn allocate_and_write_dir( .try_into() .expect("expected dir entry count not to exceed u32"); - *current_inode += 1; - write_inode(state, ty, write_result, *current_inode); + let inode = write_inode(state, ty, write_result, current_inode); let (name_offset, name_len) = { let name_len: u16 = entry.name.len().try_into().context("file name too long")?; @@ -317,13 +324,13 @@ fn allocate_and_write_dir( log::debug!( "Linking inode {} into dir entry index {}, file name `{}`", - current_inode, + inode, index, String::from_utf8_lossy(&entry.name) ); *direntry = initfs::DirEntry { - inode: (*current_inode).into(), + inode: inode.into(), name_len: name_len.into(), name_offset: initfs::Offset(name_offset.into()), }; @@ -343,18 +350,22 @@ fn allocate_and_write_dir( offset: entry_table_offset, }) } -fn allocate_contents(state: &mut State, dir: &Dir) -> Result<()> { - let start_inode = 0; - let mut current_inode = start_inode; +fn allocate_contents(state: &mut State, dir: &Dir) -> Result { + let mut current_inode = 0; let write_result = allocate_and_write_dir(state, dir, &mut current_inode) .context("failed to allocate and write all directories and files")?; - write_inode(state, initfs::InodeType::Dir, write_result, start_inode); + let root_inode = write_inode( + state, + initfs::InodeType::Dir, + write_result, + &mut current_inode, + ); - state.inode_count = current_inode + 1; + state.inode_count = current_inode; - Ok(()) + Ok(root_inode.into()) } fn write_inode_table(state: &mut State) -> Result { @@ -485,8 +496,7 @@ pub fn archive( file: guard, offset: 0, max_size, - // Include root directory. - inode_count: 1, + inode_count: 0, buffer: vec![0_u8; BUFFER_SIZE].into_boxed_slice(), inode_table: [None; 65536], }; @@ -512,7 +522,7 @@ pub fn archive( })?; let bootstrap_entry = elf_entry(&bootstrap_data); - allocate_contents(&mut state, &root)?; + let root_inode = allocate_contents(&mut state, &root)?; let inode_table_offset = write_inode_table(&mut state)?; @@ -543,6 +553,7 @@ pub fn archive( .len() .into(), page_size: PAGE_SIZE.into(), + root_inode, }; write_all_at(&state.file, &header_bytes, header_offset, "writing header") .context("failed to write header")?; diff --git a/initfs/tools/tests/archive_and_read.rs b/initfs/tools/tests/archive_and_read.rs index cdbe505dbb..f32ea0ad3d 100644 --- a/initfs/tools/tests/archive_and_read.rs +++ b/initfs/tools/tests/archive_and_read.rs @@ -86,7 +86,7 @@ fn archive_and_read() -> Result<()> { let filesystem = redox_initfs::InitFs::new(&data, None).context("failed to parse archive header")?; let inode = filesystem - .get_inode(redox_initfs::InitFs::ROOT_INODE) + .get_inode(filesystem.root_inode()) .ok_or_else(|| anyhow!("Failed to get root inode"))?; let tree = build_tree(filesystem, inode)?; From dd27c1ae087acb3667f0c71cfc87df086a40a81f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:43:40 +0200 Subject: [PATCH 6/6] initfs: Introduce InodeTable --- initfs/tools/src/lib.rs | 97 +++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/initfs/tools/src/lib.rs b/initfs/tools/src/lib.rs index 59d1afd4ab..38bc941e96 100644 --- a/initfs/tools/src/lib.rs +++ b/initfs/tools/src/lib.rs @@ -41,9 +41,8 @@ struct State<'path> { file: OutputImageGuard<'path>, offset: u64, max_size: u64, - inode_count: u16, buffer: Box<[u8]>, - inode_table: [Option; 65536], + inode_table: InodeTable, } fn write_all_at(file: &File, buf: &[u8], offset: u64, r#where: &str) -> Result<()> { @@ -229,26 +228,7 @@ fn allocate_and_write_link(state: &mut State, link: &Path) -> Result u16 { - let inode = *current_inode; - *current_inode += 1; - state.inode_table[usize::from(inode)] = Some(initfs::InodeHeader { - type_: (ty as u32).into(), - length: initfs::Length(write_result.size.into()), - offset: initfs::Offset(write_result.offset.into()), - }); - inode -} -fn allocate_and_write_dir( - state: &mut State, - dir: &Dir, - current_inode: &mut u16, -) -> Result { +fn allocate_and_write_dir(state: &mut State, dir: &Dir) -> Result { let entry_size = u16::try_from(std::mem::size_of::()).context("entry size too large")?; let entry_count = u16::try_from(dir.entries.len()).context("too many subdirectories")?; @@ -266,13 +246,12 @@ fn allocate_and_write_dir( 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, current_inode) - .with_context(|| { - anyhow!( - "failed to copy directory entries from `{}` into image", - String::from_utf8_lossy(&entry.name) - ) - })?; + let write_result = allocate_and_write_dir(state, subdir).with_context(|| { + anyhow!( + "failed to copy directory entries from `{}` into image", + String::from_utf8_lossy(&entry.name) + ) + })?; (write_result, initfs::InodeType::Dir) } @@ -301,7 +280,7 @@ fn allocate_and_write_dir( .try_into() .expect("expected dir entry count not to exceed u32"); - let inode = write_inode(state, ty, write_result, current_inode); + let inode = state.inode_table.allocate(ty, write_result); let (name_offset, name_len) = { let name_len: u16 = entry.name.len().try_into().context("file name too long")?; @@ -351,25 +330,45 @@ fn allocate_and_write_dir( }) } fn allocate_contents(state: &mut State, dir: &Dir) -> Result { - let mut current_inode = 0; - - let write_result = allocate_and_write_dir(state, dir, &mut current_inode) + let write_result = allocate_and_write_dir(state, dir) .context("failed to allocate and write all directories and files")?; - let root_inode = write_inode( - state, - initfs::InodeType::Dir, - write_result, - &mut current_inode, - ); - - state.inode_count = current_inode; + let root_inode = state + .inode_table + .allocate(initfs::InodeType::Dir, write_result); Ok(root_inode.into()) } +struct InodeTable { + entries: Vec, +} + +impl InodeTable { + fn new() -> Self { + Self { entries: vec![] } + } + + fn count(&self) -> u16 { + self.entries + .len() + .try_into() + .expect("inode count too large") + } + + fn allocate(&mut self, ty: initfs::InodeType, write_result: WriteResult) -> u16 { + let inode = self.entries.len(); + self.entries.push(initfs::InodeHeader { + type_: (ty as u32).into(), + length: initfs::Length(write_result.size.into()), + offset: initfs::Offset(write_result.offset.into()), + }); + inode.try_into().expect("inode count too large") + } +} + fn write_inode_table(state: &mut State) -> Result { - log::debug!("there are {} inodes", state.inode_count); + log::debug!("there are {} inodes", state.inode_table.count()); let inode_size: u32 = std::mem::size_of::() .try_into() @@ -377,7 +376,7 @@ fn write_inode_table(state: &mut State) -> Result { let inode_table_length = { u64::from(inode_size) - .checked_mul(u64::from(state.inode_count)) + .checked_mul(u64::from(state.inode_table.count())) .ok_or_else(|| anyhow!("inode table too large"))? }; @@ -385,17 +384,14 @@ fn write_inode_table(state: &mut State) -> Result { let inode_table_offset = u32::try_from(inode_table_offset).with_context(|| "inode table located too far away")?; - for (i, inode) in state.inode_table[..usize::from(state.inode_count)] - .iter() - .enumerate() - { + for (i, inode) in state.inode_table.entries.iter().enumerate() { // TODO: Use main buffer and write in bulk. let mut inode_buf = [0_u8; std::mem::size_of::()]; let inode_hdr = plain::from_mut_bytes::(&mut inode_buf) .expect("expected inode struct to have alignment 1, and buffer size to match"); - *inode_hdr = inode.unwrap(); + *inode_hdr = *inode; log::debug!( "Writing inode index {} from offset {}", @@ -496,9 +492,8 @@ pub fn archive( file: guard, offset: 0, max_size, - inode_count: 0, buffer: vec![0_u8; BUFFER_SIZE].into_boxed_slice(), - inode_table: [None; 65536], + inode_table: InodeTable::new(), }; // NOTE: The header is always stored at offset zero. @@ -543,7 +538,7 @@ pub fn archive( sec: time_since_epoch.as_secs().into(), nsec: time_since_epoch.subsec_nanos().into(), }, - inode_count: state.inode_count.into(), + inode_count: state.inode_table.count().into(), inode_table_offset, bootstrap_entry: bootstrap_entry.into(), initfs_size: state