-
Notifications
You must be signed in to change notification settings - Fork 305
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
a8c9778
commit 3ee8b86
Showing
13 changed files
with
527 additions
and
68 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#!/usr/bin/env bash | ||
|
||
set -euo pipefail | ||
|
||
IMAGES=() | ||
while IFS= read -r -d ''; do | ||
IMAGES+=("$REPLY") | ||
done < <(find test/.tmp -name 'vmlinuz-*' -print0) | ||
|
||
MODULES=() | ||
for image in ${IMAGES[@]}; do | ||
image_name=$(basename ${image}) | ||
image_name=${image_name#"vmlinuz-"} | ||
MODULES+=($(find test/.tmp -type d -ipath "*modules*" -name "${image_name#"vmlinux-"}" | head -n 1)) | ||
done | ||
|
||
images_len=${#IMAGES[@]} | ||
modules_len=${#MODULES[@]} | ||
|
||
if [ "${images_len}" != "${modules_len}" ]; then | ||
echo "IMAGES=${IMAGES[@]}" | ||
echo "MODULES=${MODULES[@]}" | ||
echo "ERROR! len images != len modules" | ||
exit 1 | ||
fi | ||
|
||
args="" | ||
for (( i=0; i<${images_len}; i++ )); do | ||
args+="-i ${IMAGES[$i]} -m ${MODULES[$i]} " | ||
done | ||
|
||
echo ${args} |
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,117 @@ | ||
//! 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 } = Parser::parse(); | ||
|
||
let modules_dir = if let Some(base_dir) = base_dir { | ||
base_dir | ||
} else { | ||
resolve_modules_dir().context("Failed to resolve modules dir")? | ||
}; | ||
|
||
let modules_alias = modules_dir.join("modules.alias"); | ||
let f = std::fs::OpenOptions::new() | ||
.create(true) | ||
.write(true) | ||
.truncate(true) | ||
.open(&modules_alias) | ||
.with_context(|| format!("failed to open: {modules_alias:?}"))?; | ||
let mut output = BufWriter::new(&f); | ||
for entry in WalkDir::new(modules_dir) { | ||
let entry = entry.context("failed to read entry in walkdir")?; | ||
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).with_context(|| format!("failed to open: {path:?}"))?; | ||
let stat = f | ||
.metadata() | ||
.with_context(|| format!("Failed to get metadata for {path:?}"))?; | ||
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 output) | ||
} else { | ||
let mut buf = Vec::with_capacity(stat.len() as usize); | ||
f.read_to_end(&mut buf) | ||
.with_context(|| format!("Failed to read: {path:?}"))?; | ||
read_aliases_from_module(&buf, &module_name, &mut output) | ||
} | ||
.with_context(|| format!("Failed to read aliases from module {path:?}"))?; | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
fn read_aliases_from_module( | ||
contents: &[u8], | ||
module_name: &str, | ||
output: &mut BufWriter<&File>, | ||
) -> Result<(), anyhow::Error> { | ||
let obj = object::read::File::parse(contents).context("not an 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("no .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=", ""); | ||
writeln!(output, "alias {} {}", alias, module_name).expect("write"); | ||
} | ||
} | ||
} | ||
}); | ||
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,26 @@ | ||
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"); | ||
let stat = modules_dir | ||
.metadata() | ||
.with_context(|| format!("{modules_dir:?} doesn't exist"))?; | ||
if stat.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); | ||
let stat = modules_dir | ||
.metadata() | ||
.with_context(|| format!("{modules_dir:?} doesn't exist"))?; | ||
anyhow::ensure!(stat.is_dir(), "{modules_dir:?} is not a directory",); | ||
Ok(modules_dir) | ||
} |
Oops, something went wrong.