Merge branch 'initfs_mmap' into 'main'

Support zero-copy read-only mmaping in the initfs

See merge request redox-os/base!58
This commit is contained in:
Jeremy Soller
2025-12-12 13:54:18 -07:00
11 changed files with 62 additions and 20 deletions
Generated
+2 -2
View File
@@ -802,7 +802,7 @@ dependencies = [
[[package]]
name = "generic-rt"
version = "0.1.0"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#7fcab3a43a6cf023aacd3ac9ced2a479b7f9c455"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#9db10bb0a9651b86012a0d58a3327df95a20a297"
[[package]]
name = "getrandom"
@@ -1612,7 +1612,7 @@ dependencies = [
[[package]]
name = "redox-rt"
version = "0.1.0"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#7fcab3a43a6cf023aacd3ac9ced2a479b7f9c455"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#9db10bb0a9651b86012a0d58a3327df95a20a297"
dependencies = [
"bitflags 2.10.0",
"generic-rt",
+2 -2
View File
@@ -40,7 +40,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "generic-rt"
version = "0.1.0"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#fe5273890a8f39f8ffa1507bfb243d76270e8a27"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#9db10bb0a9651b86012a0d58a3327df95a20a297"
[[package]]
name = "goblin"
@@ -143,7 +143,7 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
[[package]]
name = "redox-rt"
version = "0.1.0"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#fe5273890a8f39f8ffa1507bfb243d76270e8a27"
source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#9db10bb0a9651b86012a0d58a3327df95a20a297"
dependencies = [
"bitflags",
"generic-rt",
-5
View File
@@ -107,16 +107,11 @@ pub fn main() -> ! {
.expect("failed to open init")
.to_upper()
.unwrap();
let memory = FdGuard::open("/scheme/memory", O_CLOEXEC)
.expect("failed to open memory")
.to_upper()
.unwrap();
fexec_impl(
image_file,
init_thr_fd,
init_proc_fd,
&memory,
path.as_bytes(),
&[path.as_bytes()],
&envs,
+37 -3
View File
@@ -6,10 +6,10 @@ use core::str;
use alloc::string::String;
use hashbrown::HashMap;
use redox_initfs::{InitFs, Inode, InodeDir, InodeKind, InodeStruct, types::Timespec};
use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct};
use redox_path::canonicalize_to_standard;
use redox_scheme::{CallerCtx, OpenResult, RequestKind, scheme::SchemeSync};
use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, RequestKind};
use redox_scheme::{SignalBehavior, Socket};
use syscall::data::Stat;
@@ -19,6 +19,7 @@ use syscall::dirent::DirentKind;
use syscall::error::*;
use syscall::flag::*;
use syscall::schemev2::NewFdFlags;
use syscall::PAGE_SIZE;
struct Handle {
inode: Inode,
@@ -37,7 +38,8 @@ impl InitFsScheme {
Self {
handles: HashMap::default(),
next_id: 0,
fs: InitFs::new(bytes).expect("failed to parse initfs"),
fs: InitFs::new(bytes, Some(PAGE_SIZE.try_into().unwrap()))
.expect("failed to parse initfs"),
}
}
@@ -303,6 +305,38 @@ impl SchemeSync for InitFsScheme {
Ok(())
}
fn mmap_prep(
&mut self,
id: usize,
offset: u64,
size: usize,
flags: MapFlags,
_ctx: &CallerCtx,
) -> syscall::Result<usize> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
let data = match Self::get_inode(&self.fs, handle.inode)?.kind() {
InodeKind::File(file) => file.data().map_err(|_| Error::new(EIO))?,
InodeKind::Dir(_) => return Err(Error::new(EISDIR)),
InodeKind::Link(link) => return Err(Error::new(ELOOP)),
InodeKind::Unknown => return Err(Error::new(EIO)),
};
if flags.contains(MapFlags::PROT_WRITE) {
return Err(Error::new(EPERM));
}
let Some(last_addr) = offset.checked_add(size as u64) else {
return Err(Error::new(EINVAL));
};
if last_addr > data.len().next_multiple_of(PAGE_SIZE) as u64 {
return Err(Error::new(EINVAL));
}
Ok(data.as_ptr() as usize)
}
}
pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! {
+1 -1
View File
@@ -1,6 +1,6 @@
#![no_std]
#![allow(internal_features)]
#![feature(core_intrinsics, let_chains, iter_intersperse, str_from_raw_parts)]
#![feature(core_intrinsics, iter_intersperse, str_from_raw_parts)]
#[cfg(target_arch = "aarch64")]
#[path = "aarch64.rs"]
+1 -1
View File
@@ -2,7 +2,7 @@
name = "redox-initfs"
version = "0.2.0"
authors = ["4lDO2 <4lDO2@protonmail.com>", "Kamil Koczurek <koczurekk@gmail.com>"]
edition = "2021"
edition = "2024"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+7 -2
View File
@@ -19,6 +19,9 @@ pub const DEFAULT_MAX_SIZE: u64 = 128 * MEBIBYTE;
#[cfg(not(debug_assertions))]
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;
enum EntryKind {
File(File),
Dir(Dir),
@@ -153,9 +156,10 @@ fn read_directory(state: &mut State, path: &Path, root_path: &Path) -> Result<Di
}
fn bump_alloc(state: &mut State, size: u64, why: &str) -> Result<u64> {
if state.offset + size <= state.max_size {
let end = (state.offset + size).next_multiple_of(PAGE_SIZE.into());
if end <= state.max_size {
let offset = state.offset;
state.offset += size;
state.offset = end;
log::debug!("Allocating range {}..{} in {}", offset, state.offset, why);
Ok(offset)
} else {
@@ -559,6 +563,7 @@ pub fn archive(
.context("failed to get initfs size")?
.len()
.into(),
page_size: PAGE_SIZE.into(),
};
write_all_at(&state.file, &header_bytes, header_offset, "writing header")
.context("failed to write header")?;
+7 -1
View File
@@ -182,7 +182,7 @@ impl<'initfs> InodeStruct<'initfs> {
}
impl<'initfs> InitFs<'initfs> {
pub fn new(base: &'initfs [u8]) -> Result<Self> {
pub fn new(base: &'initfs [u8], required_page_size: Option<u16>) -> Result<Self> {
let this = Self { base };
if base.len() < core::mem::size_of::<Header>() {
@@ -214,6 +214,12 @@ impl<'initfs> InitFs<'initfs> {
return Err(Error);
}
if let Some(required_page_size) = required_page_size
&& header.page_size.get() != required_page_size
{
return Err(Error);
}
// From now on, we can be completely sure that the header and inode tables offsets are
// valid, and thus continue based on that assumption.
+1
View File
@@ -73,6 +73,7 @@ pub struct Header {
pub inode_count: U16,
pub bootstrap_entry: U64,
pub initfs_size: U64,
pub page_size: U16,
}
const _: () = {
+3 -2
View File
@@ -1,6 +1,6 @@
use std::{collections::HashMap, path::Path};
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result, anyhow};
use redox_initfs::{InitFs, InodeKind, InodeStruct};
#[derive(Debug, Clone, PartialEq)]
@@ -83,7 +83,8 @@ fn archive_and_read() -> Result<()> {
archive_common::archive(&args).context("failed to archive")?;
let data = std::fs::read(args.destination_path).context("failed to read new archive")?;
let filesystem = redox_initfs::InitFs::new(&data).context("failed to parse archive header")?;
let filesystem =
redox_initfs::InitFs::new(&data, None).context("failed to parse archive header")?;
let inode = filesystem
.get_inode(redox_initfs::InitFs::ROOT_INODE)
.ok_or_else(|| anyhow!("Failed to get root inode"))?;
+1 -1
View File
@@ -23,7 +23,7 @@ fn main() -> Result<()> {
.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")?;
let initfs = InitFs::new(&bytes, None).context("failed to parse initfs header")?;
dbg!(initfs.header());