base: apply Red Bear patches on latest upstream/main
251 files: init, acpid, ipcd, netcfg, ihdgd, virtio-gpud, scheme-utils, inputd, block driver, ptyd, ramfs, randd, initfs bootstrap, path deps, version +rb0.3.1, author attribution
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use clap::{Arg, Command};
|
||||
|
||||
use redox_initfs_tools::{self as archive, Args, DEFAULT_MAX_SIZE};
|
||||
@@ -10,6 +10,13 @@ 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")
|
||||
@@ -18,7 +25,7 @@ fn main() -> Result<()> {
|
||||
)
|
||||
.arg(
|
||||
Arg::new("BOOTSTRAP_CODE")
|
||||
.required(true)
|
||||
.required(false)
|
||||
.help("Specify the bootstrap ELF file to include in the image."),
|
||||
)
|
||||
.arg(
|
||||
@@ -32,13 +39,19 @@ fn main() -> Result<()> {
|
||||
|
||||
env_logger::init();
|
||||
|
||||
let max_size = if let Some(max_size_str) = matches.get_one::<String>("MAX_SIZE") {
|
||||
max_size_str
|
||||
.parse::<u64>()
|
||||
.context("expected an integer for MAX_SIZE")?
|
||||
} else {
|
||||
DEFAULT_MAX_SIZE
|
||||
};
|
||||
|
||||
let source = matches
|
||||
.get_one::<String>("SOURCE")
|
||||
.expect("expected the required arg SOURCE to exist");
|
||||
|
||||
let bootstrap_code = matches
|
||||
.get_one::<String>("BOOTSTRAP_CODE")
|
||||
.expect("expected the required arg BOOTSTRAP_CODE to exist");
|
||||
let bootstrap_code = matches.get_one::<String>("BOOTSTRAP_CODE");
|
||||
|
||||
let destination = matches
|
||||
.get_one::<String>("OUTPUT")
|
||||
@@ -46,9 +59,9 @@ fn main() -> Result<()> {
|
||||
|
||||
let args = Args {
|
||||
source: Path::new(source),
|
||||
bootstrap_code: Path::new(bootstrap_code),
|
||||
bootstrap_code: bootstrap_code.map(Path::new),
|
||||
destination_path: Path::new(destination),
|
||||
max_size: DEFAULT_MAX_SIZE,
|
||||
max_size,
|
||||
};
|
||||
archive::archive(&args)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// loop_mnt: archiso-style loop-mount fallback for initfs.
|
||||
//
|
||||
// CachyOS's archiso_loop_mnt hook handles the case where the live ISO
|
||||
// is discovered only after the kernel boots (via label/UUID probes).
|
||||
// Red Bear OS normally has an explicit initfs.toml that hard-codes the
|
||||
// boot device, but this binary provides a fallback for setups where the
|
||||
// boot medium is unknown at build time: it scans /scheme/initfs/etc for
|
||||
// block devices, attempts to read the RedoxFS magic from each, and
|
||||
// (when the rootfs hasn't been mounted yet) surfaces the chosen device
|
||||
// to the env so the existing 50_rootfs service can take over.
|
||||
//
|
||||
// Usage: this binary is invoked by the 45_loop_mnt.service unit. It
|
||||
// reads /scheme/initfs/etc/* (block devices), probes each for the
|
||||
// RedoxFS magic, and on the first match writes the choice to a small
|
||||
// runtime config the redoxfs service can pick up. If no match, it
|
||||
// exits successfully (0) so the init graph can fall back to the
|
||||
// "no live media" path. Errors are logged but never fatal — the
|
||||
// existing init path is the source of truth, and this is a discovery
|
||||
// shim only.
|
||||
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{Read, Seek, SeekFrom},
|
||||
path::Path,
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
const REDOXFS_MAGIC: &[u8; 8] = b"RedoxFS\0";
|
||||
// Block-size for reading the RedoxFS header. 4 KiB is enough to find
|
||||
// the magic in the first sector and is the standard sector size for
|
||||
// most storage.
|
||||
const READ_SIZE: usize = 4096;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
if let Err(err) = run() {
|
||||
log::error!("loop_mnt: discovery failed: {err}");
|
||||
}
|
||||
// Never block the init graph — fall back to explicit mount.
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
fn run() -> anyhow::Result<()> {
|
||||
// /scheme/initfs/etc is the initfs's block-device bucket. The
|
||||
// pc-spawner-initfs service has already populated it with the
|
||||
// PIIX4/IDE and virtio-blk devices by the time we run. If empty
|
||||
// (e.g. when a future boot medium is discovered at a later
|
||||
// time), the explicit initfs path takes over.
|
||||
let dev_dir = Path::new("/scheme/initfs/etc");
|
||||
if !dev_dir.exists() {
|
||||
log::info!("loop_mnt: {} not present, nothing to do", dev_dir.display());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(dev_dir)? {
|
||||
let entry = entry?;
|
||||
let name = entry.file_name();
|
||||
let dev_path = dev_dir.join(&name);
|
||||
if is_redoxfs(&dev_path)? {
|
||||
log::info!(
|
||||
"loop_mnt: discovered RedoxFS at {}",
|
||||
dev_path.display()
|
||||
);
|
||||
// Drop a runtime marker that downstream 50_rootfs.service
|
||||
// and redoxfs can read. The marker is a plain ASCII
|
||||
// file so any tool (redoxfs, mount, or shell) can pick
|
||||
// it up without a dedicated parser.
|
||||
fs::write("/scheme/runtime/loop_mnt_target", dev_path.display().to_string())?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("loop_mnt: no RedoxFS block device under {}", dev_dir.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_redoxfs(path: &Path) -> anyhow::Result<bool> {
|
||||
let mut file = match File::open(path) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Ok(false),
|
||||
};
|
||||
if file.metadata()?.len() < READ_SIZE as u64 {
|
||||
return Ok(false);
|
||||
}
|
||||
let mut buf = vec![0u8; READ_SIZE];
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
file.read_exact(&mut buf)?;
|
||||
Ok(buf.starts_with(REDOXFS_MAGIC))
|
||||
}
|
||||
+148
-147
@@ -1,5 +1,5 @@
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fs::{DirEntry, File, OpenOptions};
|
||||
use std::fs::{DirEntry, File, Metadata, OpenOptions};
|
||||
use std::io::{prelude::*, SeekFrom};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -10,8 +10,8 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||
|
||||
use redox_initfs::types as initfs;
|
||||
|
||||
const KIBIBYTE: u64 = 1024;
|
||||
const MEBIBYTE: u64 = KIBIBYTE * 1024;
|
||||
pub const KIBIBYTE: u64 = 1024;
|
||||
pub const MEBIBYTE: u64 = KIBIBYTE * 1024;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub const DEFAULT_MAX_SIZE: u64 = 256 * MEBIBYTE;
|
||||
@@ -22,23 +22,28 @@ pub const DEFAULT_MAX_SIZE: u64 = 64 * MEBIBYTE;
|
||||
// FIXME make this configurable to handle systems with 16k and 64k pages.
|
||||
const PAGE_SIZE: u16 = 4096;
|
||||
|
||||
pub enum EntryKind {
|
||||
File { file: File, executable: bool },
|
||||
Dir(Vec<Entry>),
|
||||
enum EntryKind {
|
||||
File(File),
|
||||
Dir(Dir),
|
||||
Link(PathBuf),
|
||||
}
|
||||
|
||||
pub struct Entry {
|
||||
pub name: Vec<u8>,
|
||||
pub kind: EntryKind,
|
||||
struct Entry {
|
||||
name: Vec<u8>,
|
||||
kind: EntryKind,
|
||||
metadata: Metadata,
|
||||
}
|
||||
struct Dir {
|
||||
entries: Vec<Entry>,
|
||||
}
|
||||
|
||||
struct State<'path> {
|
||||
file: OutputImageGuard<'path>,
|
||||
offset: u64,
|
||||
max_size: u64,
|
||||
inode_count: u16,
|
||||
buffer: Box<[u8]>,
|
||||
inode_table: InodeTable,
|
||||
inode_table_offset: u32,
|
||||
}
|
||||
|
||||
fn write_all_at(file: &File, buf: &[u8], offset: u64, r#where: &str) -> Result<()> {
|
||||
@@ -52,7 +57,7 @@ fn write_all_at(file: &File, buf: &[u8], offset: u64, r#where: &str) -> Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_directory(path: &Path, root_path: &Path) -> Result<Vec<Entry>> {
|
||||
fn read_directory(state: &mut State, path: &Path, root_path: &Path) -> Result<Dir> {
|
||||
let read_dir = path
|
||||
.read_dir()
|
||||
.with_context(|| anyhow!("failed to read directory `{}`", path.to_string_lossy(),))?;
|
||||
@@ -97,15 +102,11 @@ fn read_directory(path: &Path, root_path: &Path) -> Result<Vec<Entry>> {
|
||||
} else if file_type.is_char_device() {
|
||||
return unsupported_type("character device", &entry);
|
||||
} else if file_type.is_file() {
|
||||
let executable = metadata.permissions().mode() & 0o100 != 0;
|
||||
EntryKind::File {
|
||||
file: File::open(entry.path()).with_context(|| {
|
||||
anyhow!("failed to open file `{}`", entry.path().to_string_lossy(),)
|
||||
})?,
|
||||
executable,
|
||||
}
|
||||
EntryKind::File(File::open(entry.path()).with_context(|| {
|
||||
anyhow!("failed to open file `{}`", entry.path().to_string_lossy(),)
|
||||
})?)
|
||||
} else if file_type.is_dir() {
|
||||
EntryKind::Dir(read_directory(&entry.path(), root_path)?)
|
||||
EntryKind::Dir(read_directory(state, &entry.path(), root_path)?)
|
||||
} else if file_type.is_symlink() {
|
||||
let link_file_path = entry.path();
|
||||
|
||||
@@ -137,14 +138,21 @@ fn read_directory(path: &Path, root_path: &Path) -> Result<Vec<Entry>> {
|
||||
));
|
||||
};
|
||||
|
||||
// TODO: Allow the user to specify a lower limit than u16::MAX.
|
||||
state.inode_count = state
|
||||
.inode_count
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| anyhow!("exceeded the maximum inode limit"))?;
|
||||
|
||||
Ok(Entry {
|
||||
kind: entry_kind,
|
||||
metadata,
|
||||
name,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
Ok(entries)
|
||||
Ok(Dir { entries })
|
||||
}
|
||||
|
||||
fn bump_alloc(state: &mut State, size: u64, why: &str) -> Result<u64> {
|
||||
@@ -227,10 +235,49 @@ fn allocate_and_write_link(state: &mut State, link: &Path) -> Result<WriteResult
|
||||
Ok(WriteResult { size, offset })
|
||||
}
|
||||
|
||||
fn allocate_and_write_dir(state: &mut State, dir: &[Entry]) -> Result<WriteResult> {
|
||||
fn write_inode(
|
||||
state: &mut State,
|
||||
ty: initfs::InodeType,
|
||||
write_result: WriteResult,
|
||||
inode: u16,
|
||||
) -> Result<()> {
|
||||
let inode_size: u32 = std::mem::size_of::<initfs::InodeHeader>()
|
||||
.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::<initfs::InodeHeader>()];
|
||||
|
||||
let inode_hdr = plain::from_mut_bytes::<initfs::InodeHeader>(&mut inode_buf)
|
||||
.expect("expected inode struct to have alignment 1, and buffer size to match");
|
||||
|
||||
*inode_hdr = 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,
|
||||
dir: &Dir,
|
||||
current_inode: &mut u16,
|
||||
) -> Result<WriteResult> {
|
||||
let entry_size =
|
||||
u16::try_from(std::mem::size_of::<initfs::DirEntry>()).context("entry size too large")?;
|
||||
let entry_count = u16::try_from(dir.len()).context("too many subdirectories")?;
|
||||
let entry_count = u16::try_from(dir.entries.len()).context("too many subdirectories")?;
|
||||
|
||||
let entry_table_length = u32::from(entry_count)
|
||||
.checked_mul(u32::from(entry_size))
|
||||
@@ -242,27 +289,25 @@ fn allocate_and_write_dir(state: &mut State, dir: &[Entry]) -> Result<WriteResul
|
||||
.try_into()
|
||||
.context("directory entries offset too high")?;
|
||||
|
||||
for (index, entry) in dir.iter().enumerate() {
|
||||
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).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, current_inode)
|
||||
.with_context(|| {
|
||||
anyhow!(
|
||||
"failed to copy directory entries from `{}` into image",
|
||||
String::from_utf8_lossy(&entry.name)
|
||||
)
|
||||
})?;
|
||||
|
||||
(write_result, initfs::InodeType::Dir)
|
||||
}
|
||||
|
||||
EntryKind::File {
|
||||
ref file,
|
||||
executable,
|
||||
} => {
|
||||
EntryKind::File(ref file) => {
|
||||
let write_result = allocate_and_write_file(state, file)
|
||||
.context("failed to copy file into image")?;
|
||||
|
||||
let type_ = if executable {
|
||||
let type_ = if entry.metadata.permissions().mode() & 0o100 != 0 {
|
||||
initfs::InodeType::ExecutableFile
|
||||
} else {
|
||||
initfs::InodeType::RegularFile
|
||||
@@ -282,7 +327,8 @@ fn allocate_and_write_dir(state: &mut State, dir: &[Entry]) -> Result<WriteResul
|
||||
.try_into()
|
||||
.expect("expected dir entry count not to exceed u32");
|
||||
|
||||
let inode = state.inode_table.allocate(ty, write_result);
|
||||
*current_inode += 1;
|
||||
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")?;
|
||||
@@ -305,13 +351,13 @@ fn allocate_and_write_dir(state: &mut State, dir: &[Entry]) -> Result<WriteResul
|
||||
|
||||
log::debug!(
|
||||
"Linking inode {} into dir entry index {}, file name `{}`",
|
||||
inode,
|
||||
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()),
|
||||
};
|
||||
@@ -331,87 +377,14 @@ fn allocate_and_write_dir(state: &mut State, dir: &[Entry]) -> Result<WriteResul
|
||||
offset: entry_table_offset,
|
||||
})
|
||||
}
|
||||
fn allocate_contents(state: &mut State, dir: &[Entry]) -> Result<initfs::U16> {
|
||||
let write_result = allocate_and_write_dir(state, dir)
|
||||
fn allocate_contents_and_write_inodes(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")?;
|
||||
|
||||
let root_inode = state
|
||||
.inode_table
|
||||
.allocate(initfs::InodeType::Dir, write_result);
|
||||
|
||||
Ok(root_inode.into())
|
||||
}
|
||||
|
||||
struct InodeTable {
|
||||
entries: Vec<initfs::InodeHeader>,
|
||||
}
|
||||
|
||||
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<initfs::Offset> {
|
||||
log::debug!("there are {} inodes", state.inode_table.count());
|
||||
|
||||
let inode_size: u32 = std::mem::size_of::<initfs::InodeHeader>()
|
||||
.try_into()
|
||||
.expect("inode header length cannot fit within u32");
|
||||
|
||||
let inode_table_length = {
|
||||
u64::from(inode_size)
|
||||
.checked_mul(u64::from(state.inode_table.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.entries.iter().enumerate() {
|
||||
// TODO: Use main buffer and write in bulk.
|
||||
let mut inode_buf = [0_u8; std::mem::size_of::<initfs::InodeHeader>()];
|
||||
|
||||
let inode_hdr = plain::from_mut_bytes::<initfs::InodeHeader>(&mut inode_buf)
|
||||
.expect("expected inode struct to have alignment 1, and buffer size to match");
|
||||
|
||||
*inode_hdr = *inode;
|
||||
|
||||
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)
|
||||
write_inode(state, initfs::InodeType::Dir, write_result, start_inode)
|
||||
}
|
||||
|
||||
struct OutputImageGuard<'a> {
|
||||
@@ -445,7 +418,7 @@ pub struct Args<'a> {
|
||||
pub destination_path: &'a Path,
|
||||
pub max_size: u64,
|
||||
pub source: &'a Path,
|
||||
pub bootstrap_code: &'a Path,
|
||||
pub bootstrap_code: Option<&'a Path>,
|
||||
}
|
||||
pub fn archive(
|
||||
&Args {
|
||||
@@ -455,18 +428,6 @@ pub fn archive(
|
||||
bootstrap_code,
|
||||
}: &Args,
|
||||
) -> Result<()> {
|
||||
let root_path = source;
|
||||
let root = read_directory(root_path, root_path).context("failed to read root")?;
|
||||
|
||||
build_initfs(destination_path, max_size, bootstrap_code, root)
|
||||
}
|
||||
|
||||
pub fn build_initfs(
|
||||
destination_path: &Path,
|
||||
max_size: u64,
|
||||
bootstrap_code: &Path,
|
||||
root: Vec<Entry>,
|
||||
) -> std::result::Result<(), anyhow::Error> {
|
||||
let previous_extension = destination_path.extension().map_or("", |ext| {
|
||||
ext.to_str()
|
||||
.expect("expected destination path to be valid UTF-8")
|
||||
@@ -503,34 +464,71 @@ pub fn build_initfs(
|
||||
file: guard,
|
||||
offset: 0,
|
||||
max_size,
|
||||
// Include root directory.
|
||||
inode_count: 1,
|
||||
buffer: vec![0_u8; BUFFER_SIZE].into_boxed_slice(),
|
||||
inode_table: InodeTable::new(),
|
||||
inode_table_offset: 0,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
allocate_and_write_file(
|
||||
&mut state,
|
||||
&File::open(bootstrap_code).with_context(|| {
|
||||
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(|| {
|
||||
anyhow!(
|
||||
"failed to open bootstrap code file `{}`",
|
||||
"failed to read bootstrap code file `{}`",
|
||||
bootstrap_code.to_string_lossy(),
|
||||
)
|
||||
})?,
|
||||
)?;
|
||||
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);
|
||||
})?;
|
||||
elf_entry(&bootstrap_data)
|
||||
} else {
|
||||
u64::MAX
|
||||
};
|
||||
|
||||
let root_inode = allocate_contents(&mut state, &root)?;
|
||||
let inode_table_length = {
|
||||
let inode_entry_size: u64 = std::mem::size_of::<initfs::InodeHeader>()
|
||||
.try_into()
|
||||
.expect("expected table entry size to fit");
|
||||
|
||||
let inode_table_offset = write_inode_table(&mut state)?;
|
||||
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 current_system_time = std::time::SystemTime::now();
|
||||
|
||||
let time_since_epoch = current_system_time
|
||||
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
||||
.context("could not calculate timestamp")?;
|
||||
|
||||
{
|
||||
let mut header_bytes = [0_u8; std::mem::size_of::<initfs::Header>()];
|
||||
@@ -539,7 +537,11 @@ pub fn build_initfs(
|
||||
|
||||
*header = initfs::Header {
|
||||
magic: initfs::Magic(initfs::MAGIC),
|
||||
inode_count: state.inode_table.count().into(),
|
||||
creation_time: initfs::Timespec {
|
||||
sec: time_since_epoch.as_secs().into(),
|
||||
nsec: time_since_epoch.subsec_nanos().into(),
|
||||
},
|
||||
inode_count: state.inode_count.into(),
|
||||
inode_table_offset,
|
||||
bootstrap_entry: bootstrap_entry.into(),
|
||||
initfs_size: state
|
||||
@@ -549,7 +551,6 @@ pub fn build_initfs(
|
||||
.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")?;
|
||||
|
||||
Reference in New Issue
Block a user