-
Notifications
You must be signed in to change notification settings - Fork 308
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Refactor init into test-distro
The init module contains a small init system for running our integration tests against a kernel. While we don't need a full-blown linux distro, we do need some utilities. Once such utility is `modprobe` which allows us to load kernel modules. Rather than create a new module for this utility, I've instead refactored `init` into `test-distro` which is a module that contains multiple binaries. The xtask code has been adjusted to ensure these binaries are inserted into the correct places in our cpio archive, as well as bringing in the kernel modules. Signed-off-by: Dave Tucker <[email protected]>
- Loading branch information
1 parent
e82253c
commit c389190
Showing
12 changed files
with
497 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
[package] | ||
name = "test-distro" | ||
version = "0.1.0" | ||
publish = false | ||
authors.workspace = true | ||
license.workspace = true | ||
repository.workspace = true | ||
homepage.workspace = true | ||
edition.workspace = true | ||
|
||
[[bin]] | ||
name = "init" | ||
path = "src/init.rs" | ||
|
||
[[bin]] | ||
name = "modprobe" | ||
path = "src/modprobe.rs" | ||
|
||
[[bin]] | ||
name = "depmod" | ||
path = "src/depmod.rs" | ||
|
||
[dependencies] | ||
anyhow = { workspace = true, features = ["std"] } | ||
object = { workspace = true, features = ["elf", "read_core", "std"] } | ||
clap = { workspace = true, default-features = true, features = ["derive"] } | ||
nix = { workspace = true, features = [ | ||
"user", | ||
"fs", | ||
"mount", | ||
"reboot", | ||
"kmod", | ||
"feature", | ||
] } | ||
glob = { workspace = true } | ||
xz2 = { workspace = true } | ||
walkdir = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
//! depmod is used to build the modules.alias file to assist with loading | ||
//! kernel modules. | ||
//! | ||
//! This implementation is incredibly naive and is only designed to work within | ||
//! the constraints of the test environment. Not for production use. | ||
use std::{ | ||
fs::File, | ||
io::{BufWriter, Read, Write as _}, | ||
path::PathBuf, | ||
}; | ||
|
||
use anyhow::Context as _; | ||
use clap::Parser; | ||
use object::{Object, ObjectSection, ObjectSymbol}; | ||
use test_distro::resolve_modules_dir; | ||
use walkdir::WalkDir; | ||
use xz2::read::XzDecoder; | ||
|
||
#[derive(Parser)] | ||
struct Args { | ||
#[clap(long, short)] | ||
base_dir: Option<PathBuf>, | ||
} | ||
|
||
fn main() -> anyhow::Result<()> { | ||
let Args { base_dir } = Args::parse(); | ||
|
||
let modules_dir = if let Some(base_dir) = base_dir { | ||
base_dir | ||
} else { | ||
resolve_modules_dir().context("Failed to resolve modules dir")? | ||
}; | ||
|
||
let output = std::fs::OpenOptions::new() | ||
.create(true) | ||
.write(true) | ||
.truncate(true) | ||
.open(modules_dir.join("modules.alias")) | ||
.context("Failed to open modules.alias file")?; | ||
|
||
let mut aliases = String::new(); | ||
for entry in WalkDir::new(modules_dir) { | ||
let entry = entry.context("Failed to read entry")?; | ||
if entry.file_type().is_file() { | ||
let path = entry.path(); | ||
if let Some(extension) = path.extension() { | ||
if extension != "ko" && extension != "xz" { | ||
continue; | ||
} | ||
let module_name = path | ||
.file_stem() | ||
.expect("a file with no file stem?") | ||
.to_string_lossy() | ||
.replace(".ko", ""); | ||
let mut f = File::open(path).context("Failed to open module file")?; | ||
let stat = f.metadata().context("Failed to get metadata")?; | ||
if extension == "xz" { | ||
let mut decoder = XzDecoder::new(f); | ||
let mut decompressed = Vec::with_capacity(stat.len() as usize * 2); | ||
decoder.read_to_end(&mut decompressed)?; | ||
read_aliases_from_module(&decompressed, &module_name, &mut aliases) | ||
} else { | ||
let mut buf = Vec::with_capacity(stat.len() as usize); | ||
f.read_to_end(&mut buf) | ||
.context("Failed to read module file")?; | ||
read_aliases_from_module(&buf, &module_name, &mut aliases) | ||
} | ||
.with_context(|| { | ||
format!("Failed to read aliases from module {}", path.display()) | ||
})?; | ||
} | ||
} | ||
} | ||
let mut f = BufWriter::new(&output); | ||
f.write_all(aliases.as_bytes()).context("write")?; | ||
f.flush().context("flush")?; | ||
Ok(()) | ||
} | ||
|
||
fn read_aliases_from_module( | ||
contents: &[u8], | ||
module_name: &str, | ||
aliases: &mut String, | ||
) -> Result<(), anyhow::Error> { | ||
let obj = object::read::File::parse(contents).context("Failed to parse object file")?; | ||
|
||
let (section_idx, data) = obj | ||
.sections() | ||
.filter_map(|s| { | ||
if let Ok(name) = s.name() { | ||
if name == ".modinfo" { | ||
if let Ok(data) = s.data() { | ||
return Some((s.index(), data)); | ||
} | ||
} | ||
} | ||
None | ||
}) | ||
.next() | ||
.context("Failed to find .modinfo section")?; | ||
|
||
obj.symbols().for_each(|s| { | ||
if let Ok(name) = s.name() { | ||
if name.contains("alias") && s.section_index() == Some(section_idx) { | ||
let start = s.address() as usize; | ||
let end = start + s.size() as usize; | ||
let sym_data = &data[start..end]; | ||
if let Ok(cstr) = std::ffi::CStr::from_bytes_with_nul(sym_data) { | ||
let sym_str = cstr.to_string_lossy(); | ||
let alias = sym_str.replace("alias=", ""); | ||
aliases.push_str("alias "); | ||
aliases.push_str(&alias); | ||
aliases.push(' '); | ||
aliases.push_str(module_name); | ||
aliases.push('\n'); | ||
} | ||
} | ||
} | ||
}); | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
use std::path::PathBuf; | ||
|
||
use anyhow::Context as _; | ||
use nix::sys::utsname::uname; | ||
|
||
/// Kernel modules are in `/lib/modules`. | ||
/// They may be in the root of this directory, | ||
/// or in subdirectory named after the kernel release. | ||
pub fn resolve_modules_dir() -> anyhow::Result<PathBuf> { | ||
let modules_dir = PathBuf::from("/lib/modules"); | ||
if modules_dir.exists() && modules_dir.is_dir() { | ||
return Ok(modules_dir); | ||
} | ||
|
||
let utsname = uname().context("Failed to get kernel release")?; | ||
let release = utsname.release(); | ||
let modules_dir = modules_dir.join(release); | ||
anyhow::ensure!( | ||
modules_dir.join(release).exists(), | ||
"No kernel modules found for release: {}", | ||
release.to_string_lossy() | ||
); | ||
Ok(modules_dir) | ||
} |
Oops, something went wrong.