@ -1,4 +1,5 @@
use std ::{
use std ::{
collections ::BTreeMap ,
ffi ::{ OsStr , OsString } ,
ffi ::{ OsStr , OsString } ,
fmt ::Write as _ ,
fmt ::Write as _ ,
fs ::{ self , OpenOptions } ,
fs ::{ self , OpenOptions } ,
@ -18,6 +19,42 @@ use xtask::{AYA_BUILD_INTEGRATION_BPF, Errors};
const GEN_INIT_CPIO_PATCH : & str = include_str! ( "../patches/gen_init_cpio.c.macos.diff" ) ;
const GEN_INIT_CPIO_PATCH : & str = include_str! ( "../patches/gen_init_cpio.c.macos.diff" ) ;
#[ derive(Default) ]
struct KernelPackageGroup {
kernel : Option < PathBuf > ,
debug : Option < PathBuf > ,
}
fn extract_deb ( archive : & Path , dest : & Path ) -> Result < ( ) > {
fs ::create_dir_all ( dest ) . with_context ( | | format! ( "failed to create {}" , dest . display ( ) ) ) ? ;
let mut dpkg = Command ::new ( "dpkg-deb" ) ;
dpkg . arg ( "--fsys-tarfile" )
. arg ( archive )
. stdout ( Stdio ::piped ( ) ) ;
let mut dpkg_child = dpkg
. spawn ( )
. with_context ( | | format! ( "failed to spawn {dpkg:?}" ) ) ? ;
let Child { stdout , .. } = & mut dpkg_child ;
let stdout = stdout . take ( ) . unwrap ( ) ;
let mut archive_reader = tar ::Archive ::new ( stdout ) ;
archive_reader . unpack ( dest ) . with_context ( | | {
format! (
"failed to unpack archive {} to {}" ,
archive . display ( ) ,
dest . display ( )
)
} ) ? ;
let status = dpkg_child
. wait ( )
. with_context ( | | format! ( "failed to wait for {dpkg:?}" ) ) ? ;
if ! status . success ( ) {
bail ! ( "{dpkg:?} exited with {status}" ) ;
}
Ok ( ( ) )
}
#[ derive(Parser) ]
#[ derive(Parser) ]
enum Environment {
enum Environment {
/// Runs the integration tests locally.
/// Runs the integration tests locally.
@ -285,40 +322,88 @@ pub(crate) fn run(opts: Options) -> Result<()> {
}
}
let extraction_root = tempfile ::tempdir ( ) . context ( "tempdir failed" ) ? ;
let extraction_root = tempfile ::tempdir ( ) . context ( "tempdir failed" ) ? ;
let mut package_groups : BTreeMap < OsString , KernelPackageGroup > = BTreeMap ::new ( ) ;
for archive in & kernel_archives {
let file_name = archive . file_name ( ) . ok_or_else ( | | {
anyhow ! ( "archive path missing filename: {}" , archive . display ( ) )
} ) ? ;
let file_name = file_name . to_string_lossy ( ) ;
let ( package_name , _ ) = file_name
. split_once ( '_' )
. ok_or_else ( | | anyhow ! ( "unexpected archive filename: {file_name}" ) ) ? ;
let ( base , is_debug ) = if let Some ( base ) = package_name . strip_suffix ( "-dbg" ) {
( base , true )
} else if let Some ( base ) = package_name . strip_suffix ( "-dbgsym" ) {
( base , true )
} else if let Some ( base ) = package_name . strip_suffix ( "-unsigned" ) {
( base , false )
} else {
( package_name , false )
} ;
let entry = package_groups . entry ( OsString ::from ( base ) ) . or_default ( ) ;
if is_debug {
entry . debug = Some ( archive . clone ( ) ) ;
} else {
entry . kernel = Some ( archive . clone ( ) ) ;
}
}
let mut errors = Vec ::new ( ) ;
let mut errors = Vec ::new ( ) ;
for ( index , archive ) in kernel_archives . iter ( ) . enumerate ( ) {
for ( index , ( base , group ) ) in package_groups . into_iter ( ) . enumerate ( ) {
let KernelPackageGroup { kernel , debug } = group ;
let base_display = base . to_string_lossy ( ) ;
let kernel_archive =
kernel . ok_or_else ( | | anyhow ! ( "missing kernel package for {base_display}" ) ) ? ;
let archive_dir = extraction_root
let archive_dir = extraction_root
. path ( )
. path ( )
. join ( format! ( "kernel-archive-{index}" ) ) ;
. join ( format! ( "kernel-archive-{index}-image" ) ) ;
fs ::create_dir_all ( & archive_dir )
extract_deb ( & kernel_archive , & archive_dir ) ? ;
. with_context ( | | format! ( "failed to create {}" , archive_dir . display ( ) ) ) ? ;
let mut dpkg = Command ::new ( "dpkg-deb" ) ;
let debug_maps = if let Some ( debug_archive ) = debug {
dpkg . arg ( "--fsys-tarfile" )
let debug_dir = extraction_root
. arg ( archive )
. path ( )
. stdout ( Stdio ::piped ( ) ) ;
. join ( format! ( "kernel-archive-{index}-debug" ) ) ;
let mut dpkg_child = dpkg
extract_deb ( & debug_archive , & debug_dir ) ? ;
. spawn ( )
WalkDir ::new ( & debug_dir )
. with_context ( | | format! ( "failed to spawn {dpkg:?}" ) ) ? ;
. into_iter ( )
let Child { stdout , .. } = & mut dpkg_child ;
. filter_map ( | entry | entry . ok ( ) )
let stdout = stdout . take ( ) . unwrap ( ) ;
. filter ( | entry | entry . file_type ( ) . is_file ( ) )
let mut archive_reader = tar ::Archive ::new ( stdout ) ;
. filter_map ( | entry | {
archive_reader . unpack ( & archive_dir ) . with_context ( | | {
let path = entry . into_path ( ) ;
format! (
let is_system_map = path
"failed to unpack archive {} to {}" ,
. file_name ( )
archive . display ( ) ,
. map ( | file_name | {
archive_dir . display ( )
matches! (
file_name . as_encoded_bytes ( ) ,
[
b'S' ,
b'y' ,
b's' ,
b't' ,
b'e' ,
b'm' ,
b'.' ,
b'm' ,
b'a' ,
b'p' ,
b'-' ,
..
]
)
)
} ) ? ;
} )
let status = dpkg_child
. unwrap_or ( false ) ;
. wait ( )
if is_system_map { Some ( path ) } else { None }
. with_context ( | | format! ( "failed to wait for {dpkg:?}" ) ) ? ;
} )
if ! status . success ( ) {
. collect ::< Vec < _ > > ( )
bail ! ( "{dpkg:?} exited with {status}" ) ;
} else {
}
Vec ::new ( )
} ;
let mut kernel_images = Vec ::new ( ) ;
let mut kernel_images = Vec ::new ( ) ;
let mut configs = Vec ::new ( ) ;
let mut configs = Vec ::new ( ) ;
let mut kernel_maps = Vec ::new ( ) ;
for entry in WalkDir ::new ( & archive_dir ) {
for entry in WalkDir ::new ( & archive_dir ) {
let entry = entry . with_context ( | | {
let entry = entry . with_context ( | | {
format! ( "failed to read entry in {}" , archive_dir . display ( ) )
format! ( "failed to read entry in {}" , archive_dir . display ( ) )
@ -350,22 +435,59 @@ pub(crate) fn run(opts: Options) -> Result<()> {
[ b'c' , b'o' , b'n' , b'f' , b'i' , b'g' , b'-' , .. ] = > {
[ b'c' , b'o' , b'n' , b'f' , b'i' , b'g' , b'-' , .. ] = > {
configs . push ( path ) ;
configs . push ( path ) ;
}
}
// "System.map-"
[
b'S' ,
b'y' ,
b's' ,
b't' ,
b'e' ,
b'm' ,
b'.' ,
b'm' ,
b'a' ,
b'p' ,
b'-' ,
.. ,
] = > {
kernel_maps . push ( path ) ;
}
_ = > { }
_ = > { }
}
}
}
}
}
}
let ( kernel_image , kernel_version ) = match kernel_images . as_slice ( ) {
let ( kernel_image , kernel_version ) = match kernel_images . as_slice ( ) {
[ kernel_image ] = > kernel_image ,
[ kernel_image ] = > kernel_image ,
[ ] = > bail ! ( "no kernel images in {}" , archive . display ( ) ) ,
[ ] = > bail ! ( "no kernel images in {}" , kernel_ archive. display ( ) ) ,
kernel_images = > bail ! (
kernel_images = > bail ! (
"multiple kernel images in {}: {:?}" ,
"multiple kernel images in {}: {:?}" ,
archive . display ( ) ,
kernel_ archive. display ( ) ,
kernel_images
kernel_images
) ,
) ,
} ;
} ;
let config = match configs . as_slice ( ) {
let config = match configs . as_slice ( ) {
[ config ] = > config ,
[ config ] = > config ,
configs = > bail ! ( "multiple configs in {}: {:?}" , archive . display ( ) , configs ) ,
configs = > bail ! (
"multiple configs in {}: {:?}" ,
kernel_archive . display ( ) ,
configs
) ,
} ;
let system_map = match debug_maps . as_slice ( ) {
[ system_map ] = > system_map ,
[ ] = > match kernel_maps . as_slice ( ) {
[ system_map ] = > system_map ,
kernel_maps = > bail ! (
"multiple kernel System.maps in {}: {:?}" ,
kernel_archive . display ( ) ,
kernel_maps
) ,
} ,
system_maps = > bail ! (
"multiple debug System.maps in {}: {:?}" ,
kernel_archive . display ( ) ,
system_maps
) ,
} ;
} ;
let mut modules_dirs = Vec ::new ( ) ;
let mut modules_dirs = Vec ::new ( ) ;
@ -388,10 +510,10 @@ pub(crate) fn run(opts: Options) -> Result<()> {
}
}
let modules_dir = match modules_dirs . as_slice ( ) {
let modules_dir = match modules_dirs . as_slice ( ) {
[ modules_dir ] = > modules_dir ,
[ modules_dir ] = > modules_dir ,
[ ] = > bail ! ( "no modules directories in {}" , archive. display ( ) ) ,
[ ] = > bail ! ( "no modules directories in {}" , kernel_ archive. display ( ) ) ,
modules_dirs = > bail ! (
modules_dirs = > bail ! (
"multiple modules directories in {}: {:?}" ,
"multiple modules directories in {}: {:?}" ,
archive. display ( ) ,
kernel_ archive. display ( ) ,
modules_dirs
modules_dirs
) ,
) ,
} ;
} ;
@ -505,6 +627,11 @@ pub(crate) fn run(opts: Options) -> Result<()> {
write_file ( & Path ::new ( "/boot" ) . join ( name ) , config , "644 0 0" ) ;
write_file ( & Path ::new ( "/boot" ) . join ( name ) , config , "644 0 0" ) ;
}
}
write_file ( Path ::new ( "/boot/System.map" ) , system_map , "644 0 0" ) ;
if let Some ( name ) = system_map . file_name ( ) {
write_file ( & Path ::new ( "/boot" ) . join ( name ) , system_map , "644 0 0" ) ;
}
test_distro . iter ( ) . for_each ( | ( name , path ) | {
test_distro . iter ( ) . for_each ( | ( name , path ) | {
if name = = "init" {
if name = = "init" {
write_file ( Path ::new ( "/init" ) , path , "755 0 0" ) ;
write_file ( Path ::new ( "/init" ) , path , "755 0 0" ) ;