From 7f40b6b21a1620d6647b0374f65e80770d65b985 Mon Sep 17 00:00:00 2001 From: Sangeetha Thackarajan Date: Mon, 25 May 2026 12:35:27 +0530 Subject: [PATCH 1/3] fix: shellcheck errors and warnings across scripts address failures caught by `shellcheck` | | In autorun/fcoe_local.sh line 121: | while [ ! -d /sys/class/fc_remote_ports/rport-* ]; do | ^-- SC2144 (error): -d doesn't work with globs. Use a for loop. | | | In autorun/fcoe_local.sh line 133: | for b in "${target}/${hctl}/block/*"; do | ^-------------------------^ SC2066 (error): Since you double quoted this, it will not word split, and the loop will only run once. | | | In autorun/fstests_ntfs3.sh line 11: | ln -s "${mkfsbin}" "${mkfsbin}3" || break | ^---^ SC2105 (error): break is only valid in loops. | | | In autorun/ocfs2.sh line 39: | for i in $(ls /sys/block); do | ^--------------^ SC2045 (error): Iterating over ls output is fragile. Use globs. | | | In autorun/openiscsi.sh line 39: | for i in $(ls /etc/iscsi/nodes/*/*/default); do | ^-- SC2045 (error): Iterating over ls output is fragile. Use globs. | | | In cut/fstests_xfs.sh line 17: | [[ ${man_deps[@]} == ${man_deps[@]%.gz} ]] || man_deps+=(zcat gzip) | ^------------^ SC2199 (error): Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @). | ^----------------^ SC2199 (error): Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @). | | | In cut/fstests_xfs.sh line 18: | [[ ${man_deps[@]} == ${man_deps[@]%.bz2} ]] || man_deps+=(bzcat) | ^------------^ SC2199 (error): Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @). | ^-----------------^ SC2199 (error): Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @). | | | In cut/fstests_xfs.sh line 19: | [[ ${man_deps[@]} == ${man_deps[@]%.xz} ]] || man_deps+=(xzcat) | ^------------^ SC2199 (error): Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @). | ^----------------^ SC2199 (error): Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @). | | | In selftest/selftest.sh line 60: | for t in $(ls "selftest/test/"${filter}); do | ^-----------------------------^ SC2045 (error): Iterating over ls output is fragile. Use globs. | | For more information: | https://www.shellcheck.net/wiki/SC2045 -- Iterating over ls output is fragi... | https://www.shellcheck.net/wiki/SC2066 -- Since you double quoted this, it ... | https://www.shellcheck.net/wiki/SC2105 -- break is only valid in loops. Changes include: - Replace `ls` output with safe bash globs SC2045 - Fixed path resolution when migrating away from `ls` basenames. - Fixed `break` (SC2105). - Replaced implicit array concatenation in `[[ ]]` tests with explicit expansion syntax (SC2199). - Replaced unsafe dir glob inside `[ -d ]` SC2144. - Fixed SC2166. Signed-off-by: Sangeetha Thackarajan --- autorun/fcoe_local.sh | 8 ++++++-- autorun/fstests_ntfs3.sh | 2 +- autorun/lib/fstests.sh | 16 ++++++++-------- autorun/ocfs2.sh | 6 +++--- autorun/openiscsi.sh | 2 +- cut/fstests_xfs.sh | 6 +++--- selftest/selftest.sh | 2 +- vm_autorun.env | 1 + 8 files changed, 24 insertions(+), 19 deletions(-) diff --git a/autorun/fcoe_local.sh b/autorun/fcoe_local.sh index 76c7618a..4767ff58 100755 --- a/autorun/fcoe_local.sh +++ b/autorun/fcoe_local.sh @@ -118,7 +118,11 @@ create_acl() { } find_fcoedev() { - while [ ! -d /sys/class/fc_remote_ports/rport-* ]; do + while true; do + rports=(/sys/class/fc_remote_ports/rport-*/) + if [ -d "${rports[0]}" ]; then + break + fi sleep 1 done for rport in /sys/class/fc_remote_ports/rport-*; do @@ -130,7 +134,7 @@ find_fcoedev() { -o ${hctl} = "subsystem" ]; then continue; fi - for b in "${target}/${hctl}/block/*"; do + for b in "${target}/${hctl}/block/"*; do bdev="$(basename ${b})" if readlink -f /sys/block/${bdev} | grep -q "${hctl}"; then echo $bdev diff --git a/autorun/fstests_ntfs3.sh b/autorun/fstests_ntfs3.sh index 3c21125e..eab31461 100644 --- a/autorun/fstests_ntfs3.sh +++ b/autorun/fstests_ntfs3.sh @@ -8,7 +8,7 @@ set -x # create mkfs symlink for xfstests, which assumes mkfs.$FSTYPE mkfsbin="$(type -P mkfs.ntfs)" -ln -s "${mkfsbin}" "${mkfsbin}3" || break +ln -s "${mkfsbin}" "${mkfsbin}3" || _fatal "failed to create mkfs.ntfs3 symlink" modprobe virtio_blk modprobe zram num_devices="0" || _fatal "failed to load zram module" diff --git a/autorun/lib/fstests.sh b/autorun/lib/fstests.sh index b94d7e86..a1d84660 100644 --- a/autorun/lib/fstests.sh +++ b/autorun/lib/fstests.sh @@ -21,12 +21,12 @@ _fstests_devs_provision() { declare -A _CFG=(["SCRATCH_DEV"]="" ["SCRATCH_LOGDEV"]="" \ ["SCRATCH_RTDEV"]="" ["TEST_DEV"]="") - for i in $(ls /sys/block); do - _ser="$(cat /sys/block/${i}/serial 2>/dev/null)" || - _ser="$(cat /sys/block/${i}/device/serial 2>/dev/null)" || + for i in /sys/block/*; do + _ser="$(cat ${i}/serial 2>/dev/null)" || + _ser="$(cat ${i}/device/serial 2>/dev/null)" || continue ser="${_ser// }" - [[ -v "_CFG[$ser]" ]] && _CFG[$ser]="/dev/${i}" + [[ -v "_CFG[$ser]" ]] && _CFG[$ser]="/dev/${i##*/}" done [ -b "${_CFG[TEST_DEV]}" ] || _fstests_devs_zram_setup TEST_DEV @@ -54,12 +54,12 @@ _fstests_devs_pool_provision() { ["TEST_DEV"]="") declare -a _POOL=() - for i in $(ls /sys/block); do - _ser="$(cat /sys/block/${i}/serial 2>/dev/null)" || - _ser="$(cat /sys/block/${i}/device/serial 2>/dev/null)" || + for i in /sys/block/*; do + _ser="$(cat ${i}/serial 2>/dev/null)" || + _ser="$(cat ${i}/device/serial 2>/dev/null)" || continue ser="${_ser// }" - devp="/dev/${i}" + devp="/dev/${i##*/}" [[ -v "_CFG[$ser]" ]] && _CFG[$ser]="$devp" [[ $ser == "SCRATCH_DEV"* && -b "$devp" ]] && _POOL+=("$devp") done diff --git a/autorun/ocfs2.sh b/autorun/ocfs2.sh index 9ce15830..065793f0 100755 --- a/autorun/ocfs2.sh +++ b/autorun/ocfs2.sh @@ -36,9 +36,9 @@ EOF # expect a device with serial=OCFS2 declare -A _CFG=(["OCFS2"]="") -for i in $(ls /sys/block); do - ser="$(cat /sys/block/${i}/serial 2>/dev/null)" || continue - [[ -v "_CFG[$ser]" ]] && _CFG[$ser]="/dev/${i}" +for i in /sys/block/*; do + ser="$(cat ${i}/serial 2>/dev/null)" || continue + [[ -v "_CFG[$ser]" ]] && _CFG[$ser]="/dev/${i##*/}" done [ -b "${_CFG[OCFS2]}" ] || _fatal "block device with serial=OCFS2 required" diff --git a/autorun/openiscsi.sh b/autorun/openiscsi.sh index c47b59f2..49028e05 100755 --- a/autorun/openiscsi.sh +++ b/autorun/openiscsi.sh @@ -36,7 +36,7 @@ iscsid || _fatal iscsiadm -m discovery -t sendtargets -p $INITIATOR_DISCOVERY_ADDR || _fatal # auth for normal (non-discovery) sessions -for i in $(ls /etc/iscsi/nodes/*/*/default); do +for i in /etc/iscsi/nodes/*/*/default; do if [[ -n "${ISCSI_USER}${ISCSI_PASS}" ]]; then sed -i "s#node.session.auth.authmethod = .*#node.session.auth.authmethod = CHAP#" $i echo "node.session.auth.username = $ISCSI_USER" >> $i diff --git a/cut/fstests_xfs.sh b/cut/fstests_xfs.sh index b2677a3e..9ddc81ac 100755 --- a/cut/fstests_xfs.sh +++ b/cut/fstests_xfs.sh @@ -14,9 +14,9 @@ mem_rsc="$((2048 + (zram_bytes * 2 / 1048576)))M" man_deps=(man /etc/manpath.config \ $(man --path xfs_io xfs_spaceman xfs_db xfs_quota)) -[[ ${man_deps[@]} == ${man_deps[@]%.gz} ]] || man_deps+=(zcat gzip) -[[ ${man_deps[@]} == ${man_deps[@]%.bz2} ]] || man_deps+=(bzcat) -[[ ${man_deps[@]} == ${man_deps[@]%.xz} ]] || man_deps+=(xzcat) +[[ "${man_deps[*]}" == "${man_deps[*]%.gz}" ]] || man_deps+=(zcat gzip) +[[ "${man_deps[*]}" == "${man_deps[*]%.bz2}" ]] || man_deps+=(bzcat) +[[ "${man_deps[*]}" == "${man_deps[*]%.xz}" ]] || man_deps+=(xzcat) # xfs/122: pull in compiler and xfs headers _rt_require_gcc req_inst stdio.h xfs/xfs.h xfs/xfs_types.h xfs/xfs_fs.h \ diff --git a/selftest/selftest.sh b/selftest/selftest.sh index d617086b..fceefc69 100755 --- a/selftest/selftest.sh +++ b/selftest/selftest.sh @@ -57,7 +57,7 @@ _run_tests() { local tnum=0 local t - for t in $(ls "selftest/test/"${filter}); do + for t in selftest/test/${filter}*; do if ! [ -x "${t}" ]; then echo "$t skipped" continue diff --git a/vm_autorun.env b/vm_autorun.env index ca80c02e..f1cba1a2 100644 --- a/vm_autorun.env +++ b/vm_autorun.env @@ -2,6 +2,7 @@ # Copyright (C) SUSE LLC 2016-2022, all rights reserved. . /rapido.conf +shopt -s nullglob if [[ -n "$DRACUT_SYSTEMD" ]]; then alias shutdown='kill -SIGRTMIN+4 1' From c1e39942f365dc10377e569d4c0bfcdfccbb87fa Mon Sep 17 00:00:00 2001 From: Sangeetha Thackarajan Date: Mon, 25 May 2026 13:01:56 +0530 Subject: [PATCH 2/3] style: apply rustfmt across the codebase this is purely formatting change with no functional alterations (done in preparation for new CI/lint job). cmd: `git ls-files "*.rs" | grep -v "^src/third_party" | xargs rustfmt` Signed-off-by: Sangeetha Thackarajan --- src/bin/dracut-cpio.rs | 101 ++--- src/bin/kmod/kmod_context.rs | 24 +- src/bin/lscpio.rs | 4 +- src/bin/rapido-cut.rs | 719 ++++++++++++++++------------------- src/bin/rapido-init.rs | 133 ++++--- src/bin/rapido-vm.rs | 162 ++++---- src/cpio/src/lib.rs | 146 ++++--- src/lib.rs | 62 +-- 8 files changed, 634 insertions(+), 717 deletions(-) diff --git a/src/bin/dracut-cpio.rs b/src/bin/dracut-cpio.rs index ef97e14a..7d1ded78 100644 --- a/src/bin/dracut-cpio.rs +++ b/src/bin/dracut-cpio.rs @@ -72,14 +72,14 @@ fn archive_loop( cpio::S_IFREG => { let f = fs::OpenOptions::new().read(true).open(&path)?; cpio::archive_file(&mut state, path, &amd, &f, &mut writer)?; - }, + } cpio::S_IFLNK => { let tgt = fs::read_link(path)?; cpio::archive_symlink(&mut state, path, &amd, &tgt, &mut writer)?; - }, + } _ => { cpio::archive_path(&mut state, path, &amd, &mut writer)?; - }, + } }; } let mut final_off = cpio::archive_trailer(&mut state, &mut writer)?; @@ -101,9 +101,7 @@ fn params_usage(params: &[Argument]) { println!("\nExample: find fs-tree/ | dracut-cpio archive.cpio\n"); } -fn params_process( - props: &mut cpio::ArchiveProperties, -) -> argument::Result<(PathBuf, bool)> { +fn params_process(props: &mut cpio::ArchiveProperties) -> argument::Result<(PathBuf, bool)> { // If OUTPUT file exists, then zero-truncate it instead of appending. The // default append behaviour chains archives back-to-back, i.e. multiple // archives will be separated by a TRAILER and 512-byte padding. @@ -254,11 +252,7 @@ fn main() -> io::Result<()> { initial_off ); } else { - println!( - "wrote {} bytes to archive {}", - wrote, - output_path.display() - ); + println!("wrote {} bytes to archive {}", wrote, output_path.display()); } Ok(()) @@ -351,8 +345,7 @@ mod tests { } // execute coreutils mknod NAME TYPE [MAJOR MINOR] - pub fn create_tmp_mknod(&mut self, name: &str, typ: char, - maj_min: Option<(u32, u32)>) { + pub fn create_tmp_mknod(&mut self, name: &str, typ: char, maj_min: Option<(u32, u32)>) { let t = typ.to_string(); let proc = match maj_min { Some(maj_min) => { @@ -360,8 +353,8 @@ mod tests { Command::new("mknod") .args(&[name, &t, &maj.to_string(), &min.to_string()]) .spawn() - }, - None => Command::new("mknod").args(&[name, &t]).spawn() + } + None => Command::new("mknod").args(&[name, &t]).spawn(), }; let status = proc.expect("mknod failed to start").wait().unwrap(); assert!(status.success()); @@ -439,7 +432,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new("file.txt\n".as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert_eq!(wrote, 512); @@ -461,7 +455,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new("file.txt\n".as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 2 + 33); @@ -483,7 +478,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new("./file.txt\n".as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert_eq!(wrote, 512); @@ -505,7 +501,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new("dir\n".as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert_eq!(wrote, 512); @@ -529,7 +526,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new(file_list.as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 3 + 512 * 32); @@ -553,7 +551,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new(file_list.as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 4 + 512 * 32); @@ -577,7 +576,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new(file_list.as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 4 + 512 * 32); @@ -601,7 +601,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new("file.txt\nsymlink.txt\n".as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert_eq!(wrote, 512); @@ -624,7 +625,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new("fifo\n".as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert_eq!(wrote, 512); @@ -652,7 +654,8 @@ mod tests { let f = fs::File::create(&drout).unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::Cursor::new("/dev/zero\n".as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(drout); assert_eq!(wrote, 512); @@ -817,7 +820,8 @@ mod tests { let f = fs::File::create("dracut/dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new(file_list.as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut/dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 5 + 512 * 8); @@ -837,7 +841,6 @@ mod tests { .expect("GNU cpio failed to start"); assert!(status.success()); - for f in file_list.split('\n') { if f == "" { continue; @@ -870,7 +873,8 @@ mod tests { let f = fs::File::create("dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new(file_list.as_bytes()); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(PathBuf::from("dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 4 + 512 * 12); @@ -940,7 +944,8 @@ mod tests { let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new(file_list.as_bytes()); assert_eq!(cpio::ArchiveProperties::default().fixed_mtime, None); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files .push(PathBuf::from("extractor/dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 3 + 33 + 55); @@ -1032,7 +1037,8 @@ mod tests { let mut reader = io::BufReader::new(file_list.as_bytes()); assert_eq!(cpio::ArchiveProperties::default().fixed_uid, None); assert_eq!(cpio::ArchiveProperties::default().fixed_gid, None); - let wrote = archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files .push(PathBuf::from("extractor/dracut.cpio")); assert!(wrote > cpio::NEWC_HDR_LEN * 3 + 33 + 55); @@ -1078,24 +1084,21 @@ mod tests { // create GNU cpio archive twd.create_tmp_dir("gnucpio_xtr"); gnu_cpio_create(file_list.as_bytes(), "gnucpio_xtr/gnu.cpio"); - twd.cleanup_files.push(PathBuf::from("gnucpio_xtr/gnu.cpio")); + twd.cleanup_files + .push(PathBuf::from("gnucpio_xtr/gnu.cpio")); // create Dracut cpio archive twd.create_tmp_dir("dracut_xtr"); let f = fs::File::create("dracut_xtr/dracut.cpio").unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::BufReader::new(file_list.as_bytes()); - let wrote = archive_loop( - &mut reader, - &mut writer, - cpio::ArchiveProperties::default() - ) - .unwrap(); - twd.cleanup_files.push(PathBuf::from("dracut_xtr/dracut.cpio")); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); + twd.cleanup_files + .push(PathBuf::from("dracut_xtr/dracut.cpio")); let file_list_count = file_list.split_terminator('\n').count() as u64; - assert!(wrote >= cpio::NEWC_HDR_LEN * file_list_count - + (file_list.len() as u64)); + assert!(wrote >= cpio::NEWC_HDR_LEN * file_list_count + (file_list.len() as u64)); let status = Command::new("cpio") .current_dir("gnucpio_xtr") @@ -1123,8 +1126,12 @@ mod tests { for s in file_list.split_terminator('\n') { let gmd = fs::symlink_metadata("gnucpio_xtr/".to_owned() + s).unwrap(); let dmd = fs::symlink_metadata("dracut_xtr/".to_owned() + s).unwrap(); - print!("{}: cpio extracted dev_t gnu: {:#x}, dracut: {:#x}\n", - s, gmd.rdev(), dmd.rdev()); + print!( + "{}: cpio extracted dev_t gnu: {:#x}, dracut: {:#x}\n", + s, + gmd.rdev(), + dmd.rdev() + ); assert!(gmd.rdev() == dmd.rdev()); } } @@ -1145,7 +1152,7 @@ mod tests { Err(_) => { println!("SKIPPED: this test requires /tmp stat access"); return; - }, + } Ok(md) => md, }; @@ -1172,12 +1179,8 @@ mod tests { let f = fs::File::create(&drout).unwrap(); let mut writer = io::BufWriter::new(f); let mut reader = io::Cursor::new(file_list.as_bytes()); - let wrote = archive_loop( - &mut reader, - &mut writer, - cpio::ArchiveProperties::default() - ) - .unwrap(); + let wrote = + archive_loop(&mut reader, &mut writer, cpio::ArchiveProperties::default()).unwrap(); twd.cleanup_files.push(drout); assert!(wrote > cpio::NEWC_HDR_LEN); diff --git a/src/bin/kmod/kmod_context.rs b/src/bin/kmod/kmod_context.rs index a29d744a..a6d3e84a 100644 --- a/src/bin/kmod/kmod_context.rs +++ b/src/bin/kmod/kmod_context.rs @@ -408,7 +408,10 @@ mod tests { let mod_b = ctx.modules_hash.get("mod_b").expect("mod_b not found"); assert_eq!(mod_b.status, ModuleStatus::LoadableModule); assert_eq!(mod_b.rel_path, "kernel/mod-b.ko"); - assert!(mod_b.hard_deps_paths.is_empty(), "mod_b should have no hard deps"); + assert!( + mod_b.hard_deps_paths.is_empty(), + "mod_b should have no hard deps" + ); cleanup_test_dir(&root_path); } @@ -559,10 +562,7 @@ mod tests { ModuleStatus::Builtin, "builtin_mod1 status incorrect" ); - assert!( - mod1.rel_path.is_empty(), - "Builtin path should be empty" - ); + assert!(mod1.rel_path.is_empty(), "Builtin path should be empty"); // Check builtin_mod2 (normalization) let mod2 = ctx @@ -590,10 +590,7 @@ mod tests { write_test_file(&root_path, "modules.dep", modules_dep_content); // builtin mod_b collides with modules.dep entry - let modules_builtin_content = concat!( - "kernel/builtin_mod1.ko\n", - "kernel/mod_b.ko\n" - ); + let modules_builtin_content = concat!("kernel/builtin_mod1.ko\n", "kernel/mod_b.ko\n"); write_test_file(&root_path, "modules.builtin", modules_builtin_content); ctx.load_hard_dependencies().unwrap(); @@ -751,8 +748,7 @@ mod tests { let mod_a = context.find("mod_a").expect("mod_a should be found"); assert_eq!(mod_a.status, ModuleStatus::LoadableModule); assert_eq!( - mod_a.rel_path, - "kernel/mod_a.ko", + mod_a.rel_path, "kernel/mod_a.ko", "Path should point to the module file" ); assert_eq!( @@ -780,8 +776,7 @@ mod tests { let mod_b = context.find("mod_b").expect("mod_b should be found"); assert_eq!(mod_b.status, ModuleStatus::LoadableModule); assert_eq!( - mod_b.rel_path, - "kernel/mod_b.ko.xz", + mod_b.rel_path, "kernel/mod_b.ko.xz", "Path should point to the compressed module file" ); assert_eq!( @@ -893,8 +888,7 @@ mod tests { "Resolved module status should be LoadableModule" ); assert_eq!( - aliased_mod.rel_path, - "kernel/arch/x86/sub/mod32c-intel.ko.zst", + aliased_mod.rel_path, "kernel/arch/x86/sub/mod32c-intel.ko.zst", "Resolved module path is incorrect" ); diff --git a/src/bin/lscpio.rs b/src/bin/lscpio.rs index 8b083b31..d88efa55 100644 --- a/src/bin/lscpio.rs +++ b/src/bin/lscpio.rs @@ -14,7 +14,9 @@ fn main() -> io::Result<()> { // avoid BufReader: regular fh benchmarked slightly faster, despite fewer // syscalls: read(hdr)+read(name)+seek() vs read(hdr+name+readahead)+seek() - let f = fs::OpenOptions::new().read(true).open(args.nth(1).unwrap())?; + let f = fs::OpenOptions::new() + .read(true) + .open(args.nth(1).unwrap())?; let mut archive_walker = cpio::archive_walk(f)?; while let Some(archive_ent) = archive_walker.next() { let archive_ent = archive_ent?; diff --git a/src/bin/rapido-cut.rs b/src/bin/rapido-cut.rs index e82a980c..03844d0b 100644 --- a/src/bin/rapido-cut.rs +++ b/src/bin/rapido-cut.rs @@ -8,11 +8,11 @@ use std::io; use std::io::BufRead; use std::io::Seek; use std::io::Write; -use std::path::{self, Path, PathBuf, Component}; +use std::path::{self, Component, Path, PathBuf}; use elf::abi; -use elf::ElfStream; use elf::endian::AnyEndian; +use elf::ElfStream; use crosvm::argument::{self, Argument}; mod kmod; @@ -25,9 +25,21 @@ const BIN_PATHS: [&str; 5] = ["/usr/bin", "/usr/sbin", "/usr/lib/systemd", "/bin // Extra search paths may be added at runtime via ELF RUNPATH/LibRunPath. // $ARCH-linux-gnu is for Debian/Ubuntu. #[cfg(target_arch = "x86_64")] -const LIB_PATHS: [&str; 5] = ["/usr/lib64", "/usr/lib", "/lib64", "/lib", "/usr/lib/x86_64-linux-gnu"]; +const LIB_PATHS: [&str; 5] = [ + "/usr/lib64", + "/usr/lib", + "/lib64", + "/lib", + "/usr/lib/x86_64-linux-gnu", +]; #[cfg(target_arch = "aarch64")] -const LIB_PATHS: [&str; 5] = ["/usr/lib64", "/usr/lib", "/lib64", "/lib", "/usr/lib/aarch64-linux-gnu"]; +const LIB_PATHS: [&str; 5] = [ + "/usr/lib64", + "/usr/lib", + "/lib64", + "/lib", + "/usr/lib/aarch64-linux-gnu", +]; #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] const LIB_PATHS: [&str; 4] = ["/usr/lib64", "/usr/lib", "/lib64", "/lib"]; // FIXME: don't assume cwd parent location @@ -37,7 +49,7 @@ const RAPIDO_INIT_PATH: &str = "target/release/rapido-init"; // FIXME: don't assume cwd location const RAPIDO_BASH_RC_PATH: &str = "vm_autorun.env"; -const GATHER_ITEM_IGNORE_PARENT: u32 = 1<<0; +const GATHER_ITEM_IGNORE_PARENT: u32 = 1 << 0; // Don't print debug messages on release builds... #[cfg(debug_assertions)] @@ -102,9 +114,14 @@ enum GatherEnt { fn path_stat(ent: &GatherEnt) -> Result { let name: &str = match ent { GatherEnt::Path(p) => match fs::symlink_metadata(&p) { - Ok(md) => return Ok(Fsent {path: p.clone(), md: md}), + Ok(md) => { + return Ok(Fsent { + path: p.clone(), + md: md, + }) + } Err(e) => return Err(e), - } + }, GatherEnt::Name(n) => &n, GatherEnt::NameDst(n, _) => n, GatherEnt::NameStatic(n) => n, @@ -116,7 +133,7 @@ fn path_stat(ent: &GatherEnt) -> Result { for dir in paths.iter() { let p = PathBuf::from(dir).join(n); if let Ok(md) = fs::symlink_metadata(&p) { - return Ok(Fsent {path: p, md: md}); + return Ok(Fsent { path: p, md: md }); } } // fallback to LIB_PATHS search @@ -133,15 +150,15 @@ fn path_stat(ent: &GatherEnt) -> Result { return match fs::symlink_metadata(name) { Ok(md) => Ok(Fsent { path: path::absolute(name).expect("absolute failed for good path"), - md: md + md: md, }), Err(_) => { return Err(io::Error::new( io::ErrorKind::NotFound, - format!("{} missing", name) + format!("{} missing", name), )); } - } + }; } // TODO: set search_paths with name above. Tuple assignment didn't work for me. @@ -154,7 +171,7 @@ fn path_stat(ent: &GatherEnt) -> Result { for dir in search_paths.iter() { let p = PathBuf::from(dir).join(name); if let Ok(md) = fs::symlink_metadata(&p) { - return Ok(Fsent {path: p, md: md}); + return Ok(Fsent { path: p, md: md }); } } @@ -165,7 +182,7 @@ fn path_stat(ent: &GatherEnt) -> Result { format!("{} missing from: {:?} & {:?}", name, p, search_paths) } _ => format!("{} missing from: {:?}", name, search_paths), - } + }, )); } @@ -176,7 +193,7 @@ fn path_stat(ent: &GatherEnt) -> Result { fn elf_deps( f: &fs::File, path: &Path, - dups_filter: &mut HashSet + dups_filter: &mut HashSet, ) -> Result, io::Error> { let mut ret: Vec = vec![]; @@ -197,15 +214,15 @@ fn elf_deps( return Ok(ret); } d.unwrap() - }, + } Err(e) => { eprintln!("{:?} elf .dynamic error: {:?}", path, e); return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } }; - let mut runpath_offs: Vec = vec!(); - let mut needed_offs: Vec = vec!(); + let mut runpath_offs: Vec = vec![]; + let mut needed_offs: Vec = vec![]; for dyna in dynamics.iter() { let v = match dyna.d_tag { abi::DT_NEEDED => &mut needed_offs, @@ -226,7 +243,7 @@ fn elf_deps( Err(e) => { eprintln!("{:?} bad elf dynamic sym table: {:?}", path, e); return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(tup) => { if tup.is_none() { dout!("no tables for {:?}", path); @@ -234,16 +251,17 @@ fn elf_deps( } let (_, strs) = tup.unwrap(); strs - }, + } }; // get full list of runpaths first - let runpaths: Vec = runpath_offs.into_iter().filter_map(|o| { - match dynsyms_strs.get(o) { + let runpaths: Vec = runpath_offs + .into_iter() + .filter_map(|o| match dynsyms_strs.get(o) { Err(_) => None, Ok(s) => Some(s.to_string()), - } - }).collect(); + }) + .collect(); for str_off in needed_offs { match dynsyms_strs.get(str_off) { @@ -262,11 +280,11 @@ fn elf_deps( } else { dout!("duplicate elf dependency({:?}): {:?}", str_off, sraw); } - }, + } Err(e) => { eprintln!("{:?} bad elf dynamic sym table: {:?}", path, e); return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } }; } @@ -302,7 +320,7 @@ fn gather_archive_dirs( } // mock up md to use for any parent directories. 0111: allow traversal - let parent_dirs_amd = cpio::ArchiveMd{ + let parent_dirs_amd = cpio::ArchiveMd { mode: match child_amd.mode & cpio::S_IFMT { cpio::S_IFDIR => child_amd.mode, _ => (child_amd.mode & !cpio::S_IFMT) | cpio::S_IFDIR | 0111, @@ -329,11 +347,11 @@ fn gather_archive_dirs( } // keep walking with @here now at parent continue; - }, + } Component::Prefix(_) => { eprintln!("non-Unix path prefixes not supported"); return Err(io::Error::from(io::ErrorKind::InvalidInput)); - }, + } Component::Normal(c) => here.push(c), } @@ -392,13 +410,7 @@ fn gather_archive_elfs( } let amd = cpio::ArchiveMd::from(&cpio_state, &got.md)?; - gather_archive_dirs( - dst.parent(), - &amd, - paths_seen, - cpio_state, - &mut cpio_writer - )?; + gather_archive_dirs(dst.parent(), &amd, paths_seen, cpio_state, &mut cpio_writer)?; match amd.mode & cpio::S_IFMT { cpio::S_IFLNK => { // symlinks are tricky, so provide some restrictions: @@ -413,16 +425,10 @@ fn gather_archive_elfs( Err(e) => { eprintln!("{:?} canonicalize failed: {:?}", src, e); continue; - }, + } Ok(t) => t, }; - cpio::archive_symlink( - cpio_state, - &dst, - &amd, - &canon_tgt, - &mut cpio_writer - )?; + cpio::archive_symlink(cpio_state, &dst, &amd, &canon_tgt, &mut cpio_writer)?; dout!("archived symlink: {:?} ({:?})", &dst, canon_tgt); // could add a Path ent type to avoid String conversion here... @@ -453,18 +459,11 @@ fn gather_archive_elfs( dout!("archived elf: {:?}→{:?}", src, &dst); } cpio::S_IFDIR => { - cpio::archive_path( - cpio_state, - &dst, - &amd, - &mut cpio_writer - )?; + cpio::archive_path(cpio_state, &dst, &amd, &mut cpio_writer)?; dout!("archived elf dir: {:?}→{:?}", src, &dst); let mut entries = fs::read_dir(src)? - .map(|res| res.map( - |e| GatherEnt::Path(src.join(e.file_name())) - )) + .map(|res| res.map(|e| GatherEnt::Path(src.join(e.file_name())))) .collect::, io::Error>>()?; // sort for reproducibility entries.sort(); @@ -495,22 +494,10 @@ fn archive_kmod_path( ) -> io::Result<()> { let md = fs::symlink_metadata(src)?; let amd = cpio::ArchiveMd::from(cpio_state, &md)?; - gather_archive_dirs( - dst.parent(), - &amd, - paths_seen, - cpio_state, - &mut cpio_writer - )?; + gather_archive_dirs(dst.parent(), &amd, paths_seen, cpio_state, &mut cpio_writer)?; let kmod_f = fs::File::open(src)?; - cpio::archive_file( - cpio_state, - dst, - &amd, - &kmod_f, - cpio_writer, - )?; + cpio::archive_file(cpio_state, dst, &amd, &kmod_f, cpio_writer)?; dout!("archived kmod: {:?} -> {:?}", src, dst); Ok(()) } @@ -529,7 +516,7 @@ fn archive_kmods_symlink( false => (libp, Path::new("/usr/lib")), true => (Path::new("/lib/modules"), Path::new("/usr/lib/modules")), }; - let amd = cpio::ArchiveMd{ + let amd = cpio::ArchiveMd { nlink: 1, mode: cpio::S_IFLNK | 0o777, uid: 0, @@ -557,7 +544,7 @@ fn gather_archive_kmod_and_deps( Some(m) if m.status == ModuleStatus::Builtin => { dout!("{} builtin", name); return Ok(()); - }, + } Some(m) => m, }; @@ -572,7 +559,7 @@ fn gather_archive_kmod_and_deps( &kmod_dst, paths_seen, cpio_state, - &mut cpio_writer + &mut cpio_writer, )?; } else { dout!("skipping duplicate kmod {:?} and all deps", &kmod_dst); @@ -588,7 +575,7 @@ fn gather_archive_kmod_and_deps( &kmod_dst, paths_seen, cpio_state, - &mut cpio_writer + &mut cpio_writer, )?; } else { dout!("skipping duplicate kmod {:?}", &kmod_dst); @@ -597,14 +584,17 @@ fn gather_archive_kmod_and_deps( // Attempt to pull in soft and weak dependencies for root_mod. // Not sure if we should be checking root_mod dependents. - for soft_mod in root_mod.soft_deps_pre.iter() + for soft_mod in root_mod + .soft_deps_pre + .iter() .chain(root_mod.soft_deps_post.iter()) - .chain(root_mod.weak_deps.iter()) { + .chain(root_mod.weak_deps.iter()) + { let m = match context.find(soft_mod) { None => { dout!("{:?} soft / weak kernel dep not found", soft_mod); continue; - }, + } Some(m) if m.status == ModuleStatus::Builtin => continue, Some(m) => m, }; @@ -616,14 +606,14 @@ fn gather_archive_kmod_and_deps( &kmod_dst, paths_seen, cpio_state, - &mut cpio_writer + &mut cpio_writer, ) { Err(e) if e.kind() == io::ErrorKind::NotFound => { dout!("{:?} soft / weak kernel dep missing", &kmod_src); continue; - }, + } Err(e) => return Err(e), - Ok(_) => {}, + Ok(_) => {} } } else { dout!("skipping duplicate kmod {:?}", &kmod_dst); @@ -649,13 +639,9 @@ impl GatherKmods { let kmod_ctx = match conf.get("KERNEL_INSTALL_MOD_PATH") { // should assert that KERNEL_SRC is set? Some(kmp) if !kmp.is_empty() => { - KmodContext::new( - &PathBuf::from(kmp).join(format!("lib/modules/{krel}")) - ) - } - None | Some(_) if kmod_dst_root.exists() => { - KmodContext::new(&kmod_dst_root) + KmodContext::new(&PathBuf::from(kmp).join(format!("lib/modules/{krel}"))) } + None | Some(_) if kmod_dst_root.exists() => KmodContext::new(&kmod_dst_root), None | Some(_) => { // assume that we have a non-Tumbleweed system KmodContext::new(&PathBuf::from("/lib/modules/").join(&krel)) @@ -678,19 +664,19 @@ impl GatherKmods { &data_dst_path, paths_seen, cpio_state, - &mut cpio_writer + &mut cpio_writer, ) { Err(e) if e.kind() == io::ErrorKind::NotFound => { dout!("Module data path {:?} missing", data_src_path); // TODO: only install required, and return error if missing - }, + } Err(e) => return Err(e), - Ok(_) => {}, + Ok(_) => {} } } } - Ok(GatherKmods{ + Ok(GatherKmods { kmod_dst_root, kmod_ctx, }) @@ -707,9 +693,12 @@ fn gather_archive_kmods( mut cpio_writer: W, ) -> io::Result<()> { if gk.is_none() { - *gk = Some( - GatherKmods::init(conf, paths_seen, cpio_state, &mut cpio_writer)? - ); + *gk = Some(GatherKmods::init( + conf, + paths_seen, + cpio_state, + &mut cpio_writer, + )?); } let kmod_dst_root = &gk.as_ref().unwrap().kmod_dst_root; let kmod_ctx = &gk.as_ref().unwrap().kmod_ctx; @@ -721,7 +710,7 @@ fn gather_archive_kmods( &kmod_ctx, paths_seen, cpio_state, - &mut cpio_writer + &mut cpio_writer, ) { Err(e) if e.kind() == io::ErrorKind::NotFound => { if ignore_missing { @@ -730,11 +719,11 @@ fn gather_archive_kmods( } return Err(io::Error::new( io::ErrorKind::NotFound, - format!("{} missing from: {:?}", name, kmod_ctx.module_root) + format!("{} missing from: {:?}", name, kmod_ctx.module_root), )); } Err(e) => return Err(e), - Ok(_) => {}, + Ok(_) => {} }; } @@ -754,7 +743,7 @@ fn gather_archive_data( Err(e) if e.kind() == io::ErrorKind::NotFound => { return Err(io::Error::new( io::ErrorKind::NotFound, - format!("{:?} missing", item.src) + format!("{:?} missing", item.src), )); } Err(e) => return Err(e), @@ -771,19 +760,14 @@ fn gather_archive_data( &src_amd, paths_seen, cpio_state, - &mut cpio_writer + &mut cpio_writer, )?; } match src_amd.mode & cpio::S_IFMT { // add any subdirs to gather list cpio::S_IFDIR => { - cpio::archive_path( - cpio_state, - &item.dst, - &src_amd, - &mut cpio_writer - )?; + cpio::archive_path(cpio_state, &item.dst, &src_amd, &mut cpio_writer)?; dout!("archived data dir: {:?}→{:?}", item.src, item.dst); let mut entries = fs::read_dir(&item.src)? @@ -802,40 +786,23 @@ fn gather_archive_data( flags: GATHER_ITEM_IGNORE_PARENT, }); } - }, + } // dataless files can use archive_path cpio::S_IFREG if src_amd.len > 0 => { let f = fs::OpenOptions::new().read(true).open(&item.src)?; - cpio::archive_file( - cpio_state, - &item.dst, - &src_amd, - &f, - &mut cpio_writer - )?; + cpio::archive_file(cpio_state, &item.dst, &src_amd, &f, &mut cpio_writer)?; dout!("archived data file: {:?}→{:?}", item.src, item.dst); - }, + } cpio::S_IFLNK => { let tgt = fs::read_link(&item.src)?; // XXX don't follow data symlinks to archive their targets - cpio::archive_symlink( - cpio_state, - &item.dst, - &src_amd, - &tgt, - &mut cpio_writer - )?; + cpio::archive_symlink(cpio_state, &item.dst, &src_amd, &tgt, &mut cpio_writer)?; dout!("archived data symlink: {:?}→{:?}", item.src, item.dst); - }, + } _ => { - cpio::archive_path( - cpio_state, - &item.dst, - &src_amd, - &mut cpio_writer - )?; + cpio::archive_path(cpio_state, &item.dst, &src_amd, &mut cpio_writer)?; dout!("archived data path: {:?}→{:?}", item.src, item.dst); - }, + } }; } @@ -847,15 +814,15 @@ fn populate_default_symlinks( cpio_state: &mut cpio::ArchiveState, mut cpio_writer: W, ) -> io::Result<()> { - let amd = cpio::ArchiveMd{ - nlink: 1, - mode: cpio::S_IFLNK | 0o777, - uid: 0, - gid: 0, - mtime: 0, - rmajor: 0, - rminor: 0, - len: 0, + let amd = cpio::ArchiveMd { + nlink: 1, + mode: cpio::S_IFLNK | 0o777, + uid: 0, + gid: 0, + mtime: 0, + rmajor: 0, + rminor: 0, + len: 0, }; // on Tumbleweed, bash won't start without this symlink. /lib64 may already @@ -927,7 +894,11 @@ fn args_usage(params: &[Argument]) { print!("{}", MANIFEST_FORMAT); } -fn args_process_one(name: &str, value: Option<&str>, state: &mut ArgsState) -> argument::Result<()> { +fn args_process_one( + name: &str, + value: Option<&str>, + state: &mut ArgsState, +) -> argument::Result<()> { // unwrap: callers ensure value is Some if arg requires one match name { "output" => state.cpio_output_arg = Some(PathBuf::from(value.unwrap())), @@ -935,22 +906,18 @@ fn args_process_one(name: &str, value: Option<&str>, state: &mut ArgsState) -> a // TODO: avoid to_string() match path_stat(&GatherEnt::Manifest(value.unwrap().to_string())) { Err(e) => { - return Err( - argument::Error::InvalidValue { - value: value.unwrap().to_string(), - expected: format!("failed to stat: {:?}", e), - } - ); + return Err(argument::Error::InvalidValue { + value: value.unwrap().to_string(), + expected: format!("failed to stat: {:?}", e), + }); } Ok(fs) => { match fs::OpenOptions::new().read(true).open(&fs.path) { Err(e) => { - return Err( - argument::Error::InvalidValue { - value: value.unwrap().to_string(), - expected: format!("failed to open: {:?}", e), - } - ); + return Err(argument::Error::InvalidValue { + value: value.unwrap().to_string(), + expected: format!("failed to open: {:?}", e), + }); } Ok(f) => state.manifests.push(io::BufReader::new(f)), }; @@ -966,13 +933,13 @@ fn args_process_one(name: &str, value: Option<&str>, state: &mut ArgsState) -> a fn args_process() -> argument::Result { let mut state = ArgsState { cpio_output_arg: None, - manifests: vec!(), + manifests: vec![], }; let params = &[ Argument::value( "output", "INITRAMFS", - "Write initramfs archive to this file path." + "Write initramfs archive to this file path.", ), Argument::value( "manifest", @@ -1000,7 +967,7 @@ fn args_process() -> argument::Result { // args_out will always be Some(cpio_output_path) on success. fn cpio_out_open( args_out: &mut Option, - conf: &HashMap + conf: &HashMap, ) -> io::Result> { let mut fops = fs::OpenOptions::new(); // for rapido we normally want to truncate any existing output file @@ -1027,7 +994,7 @@ fn cpio_out_open( } fn main() -> io::Result<()> { - let mut cpio_state = cpio::ArchiveState::new(cpio::ArchiveProperties{ + let mut cpio_state = cpio::ArchiveState::new(cpio::ArchiveProperties { // Attempt 4K file data alignment within archive for Btrfs/XFS reflinks data_align: 4096, ..cpio::ArchiveProperties::default() @@ -1038,9 +1005,7 @@ fn main() -> io::Result<()> { Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidInput, e.to_string())), }; - let (conf, mut cpio_writer) = match rapido::host_rapido_conf_open( - rapido::RAPIDO_CONF_PATH - ) { + let (conf, mut cpio_writer) = match rapido::host_rapido_conf_open(rapido::RAPIDO_CONF_PATH) { Err(e) if e.kind() == io::ErrorKind::NotFound => { eprintln!("no rapido.conf, using defaults"); let c = rapido::conf_defaults(); @@ -1050,7 +1015,7 @@ fn main() -> io::Result<()> { &mut cpio_state, &Path::new("/rapido.conf"), &CPIO_AMD_DEFAULT, - &mut w + &mut w, )?; (c, w) } @@ -1076,7 +1041,7 @@ fn main() -> io::Result<()> { &Path::new("/rapido.conf"), &cf_amd, &mut cf_rd, - &mut w + &mut w, )?; (c, w) } @@ -1092,7 +1057,7 @@ fn main() -> io::Result<()> { &mut paths_seen, &mut cpio_state, &mut cpio_writer, - &mut args_state.manifests + &mut args_state.manifests, )?; match fs::OpenOptions::new().read(true).open(RAPIDO_BASH_RC_PATH) { @@ -1108,12 +1073,12 @@ fn main() -> io::Result<()> { &Path::new("/rapido.rc"), &f_amd, &f, - &mut cpio_writer + &mut cpio_writer, )?; } }; - let core_elfs = vec!( + let core_elfs = vec![ // this will only install if /rdinit isn't already provided by manifest GatherEnt::NameDst(RAPIDO_INIT_PATH, Path::new("/rdinit")), // rapido-init core deps @@ -1122,14 +1087,14 @@ fn main() -> io::Result<()> { GatherEnt::NameStatic("bash"), GatherEnt::NameStatic("stty"), // TODO only install if we have non-builtin kmods! - GatherEnt::NameStatic("modprobe") - ); + GatherEnt::NameStatic("modprobe"), + ]; gather_archive_elfs( core_elfs, &mut libs_seen, &mut paths_seen, &mut cpio_state, - &mut cpio_writer + &mut cpio_writer, )?; // TODO avoid stringify @@ -1144,7 +1109,7 @@ fn main() -> io::Result<()> { false, &mut paths_seen, &mut cpio_state, - &mut cpio_writer + &mut cpio_writer, )?; populate_default_symlinks(&paths_seen, &mut cpio_state, &mut cpio_writer)?; @@ -1162,10 +1127,7 @@ fn main() -> io::Result<()> { } // var replacement mostly copied from kv-conf -fn sub_path_vars( - conf: &HashMap, - p: &str -) -> Result { +fn sub_path_vars(conf: &HashMap, p: &str) -> Result { // TODO: fastpath if no '$' let mut unquoted_val = String::new(); let mut var_next = false; @@ -1217,13 +1179,13 @@ fn manifest_name_sub(conf: &HashMap, name: Option<&str>) -> io:: Err(io::Error::from(io::ErrorKind::InvalidData)) } Ok(p) => Ok(p), - } + }, } } fn manifest_name_sub_abs_path( conf: &HashMap, - name: Option<&str> + name: Option<&str>, ) -> io::Result { let p = manifest_name_sub(conf, name)?; match path::absolute(p) { @@ -1241,21 +1203,18 @@ fn manifest_dir( paths_seen: &mut HashSet, cpio_state: &mut cpio::ArchiveState, mut cpio_writer: W, - name: Option<&str> + name: Option<&str>, ) -> io::Result<()> { let p = manifest_name_sub_abs_path(conf, name)?; if paths_seen.contains(&p) { dout!("ignoring seen dir: {:?}", &p); return Ok(()); } - let amd = cpio::ArchiveMd { mode: cpio::S_IFDIR | 0o755, ..CPIO_AMD_DEFAULT }; - gather_archive_dirs( - p.parent(), - &amd, - paths_seen, - cpio_state, - &mut cpio_writer - )?; + let amd = cpio::ArchiveMd { + mode: cpio::S_IFDIR | 0o755, + ..CPIO_AMD_DEFAULT + }; + gather_archive_dirs(p.parent(), &amd, paths_seen, cpio_state, &mut cpio_writer)?; cpio::archive_path(cpio_state, &p, &amd, &mut cpio_writer)?; paths_seen.insert(p); Ok(()) @@ -1267,7 +1226,7 @@ fn manifest_slink( cpio_state: &mut cpio::ArchiveState, mut cpio_writer: W, name: Option<&str>, - slink_tgt: Option<&str> + slink_tgt: Option<&str>, ) -> io::Result<()> { let p = manifest_name_sub_abs_path(conf, name)?; if paths_seen.contains(&p) { @@ -1275,14 +1234,11 @@ fn manifest_slink( return Ok(()); } let tgt = manifest_name_sub(conf, slink_tgt)?; - let amd = cpio::ArchiveMd { mode: cpio::S_IFLNK | 0o777, ..CPIO_AMD_DEFAULT }; - gather_archive_dirs( - p.parent(), - &amd, - paths_seen, - cpio_state, - &mut cpio_writer - )?; + let amd = cpio::ArchiveMd { + mode: cpio::S_IFLNK | 0o777, + ..CPIO_AMD_DEFAULT + }; + gather_archive_dirs(p.parent(), &amd, paths_seen, cpio_state, &mut cpio_writer)?; cpio::archive_symlink(cpio_state, &p, &amd, Path::new(&tgt), &mut cpio_writer)?; paths_seen.insert(p); Ok(()) @@ -1294,7 +1250,7 @@ fn manifest_file( cpio_state: &mut cpio::ArchiveState, mut cpio_writer: W, name: Option<&str>, - src: Option<&str> + src: Option<&str>, ) -> io::Result<()> { let p = manifest_name_sub_abs_path(conf, name)?; if paths_seen.contains(&p) { @@ -1307,23 +1263,11 @@ fn manifest_file( let src_md = f.metadata()?; // XXX unlike others, amd is based on the src file. let amd = cpio::ArchiveMd::from(cpio_state, &src_md)?; - gather_archive_dirs( - p.parent(), - &amd, - paths_seen, - cpio_state, - &mut cpio_writer - )?; + gather_archive_dirs(p.parent(), &amd, paths_seen, cpio_state, &mut cpio_writer)?; cpio::archive_file(cpio_state, &p, &amd, &f, &mut cpio_writer)?; } else { let amd = CPIO_AMD_DEFAULT; - gather_archive_dirs( - p.parent(), - &amd, - paths_seen, - cpio_state, - &mut cpio_writer - )?; + gather_archive_dirs(p.parent(), &amd, paths_seen, cpio_state, &mut cpio_writer)?; cpio::archive_path(cpio_state, &p, &amd, &mut cpio_writer)?; } @@ -1337,7 +1281,7 @@ fn manifest_autorun( autorun_idx: &mut u32, cpio_state: &mut cpio::ArchiveState, mut cpio_writer: W, - name: Option<&str> + name: Option<&str>, ) -> io::Result<()> { let src = manifest_name_sub_abs_path(conf, name)?; let dst = match src.file_name() { @@ -1378,7 +1322,7 @@ fn manifest_tree( cpio_state: &mut cpio::ArchiveState, mut cpio_writer: W, name: Option<&str>, - src: Option<&str> + src: Option<&str>, ) -> io::Result<()> { let dst = manifest_name_sub_abs_path(conf, name)?; if !dst.is_absolute() { @@ -1387,10 +1331,14 @@ fn manifest_tree( } let src = manifest_name_sub(conf, src)?; gather_archive_data( - vec!(GatherItem { src: PathBuf::from(src), dst, flags: 0, }), + vec![GatherItem { + src: PathBuf::from(src), + dst, + flags: 0, + }], paths_seen, cpio_state, - &mut cpio_writer + &mut cpio_writer, ) } @@ -1403,7 +1351,7 @@ fn manifest_parse_one( cpio_state: &mut cpio::ArchiveState, mut cpio_writer: W, line: &mut String, - fests: &mut Vec> + fests: &mut Vec>, ) -> io::Result<()> { let mut iter = line.split_whitespace(); let etype = match iter.next() { @@ -1413,24 +1361,15 @@ fn manifest_parse_one( }; match etype { - "dir" => { - manifest_dir(conf, - paths_seen, - cpio_state, - cpio_writer, - iter.next() - ) - } - "slink" => { - manifest_slink( - conf, - paths_seen, - cpio_state, - cpio_writer, - iter.next(), - iter.next() - ) - } + "dir" => manifest_dir(conf, paths_seen, cpio_state, cpio_writer, iter.next()), + "slink" => manifest_slink( + conf, + paths_seen, + cpio_state, + cpio_writer, + iter.next(), + iter.next(), + ), "include" => { let p = manifest_name_sub(conf, iter.next())?; // should we skip already-seen manifests completely? @@ -1439,16 +1378,14 @@ fn manifest_parse_one( fests.push(io::BufReader::new(f)); Ok(()) } - "file" => { - manifest_file( - conf, - paths_seen, - cpio_state, - cpio_writer, - iter.next(), - iter.next() - ) - } + "file" => manifest_file( + conf, + paths_seen, + cpio_state, + cpio_writer, + iter.next(), + iter.next(), + ), // bin // is used for source and archive destination path. // paths not containing a '/' are searched for under BIN_PATHS, @@ -1461,39 +1398,35 @@ fn manifest_parse_one( "bin" => { let src = manifest_name_sub(conf, iter.next())?; gather_archive_elfs( - vec!(GatherEnt::Name(src)), + vec![GatherEnt::Name(src)], libs_seen, paths_seen, cpio_state, - cpio_writer + cpio_writer, ) } "try-bin" => { let src = manifest_name_sub(conf, iter.next())?; gather_archive_elfs( - vec!(GatherEnt::NameTry(src)), + vec![GatherEnt::NameTry(src)], libs_seen, paths_seen, cpio_state, - cpio_writer + cpio_writer, ) } - "kmod" => { - match iter.next() { - None => Err(io::Error::from(io::ErrorKind::InvalidData)), - Some(kmod) => { - gather_archive_kmods( - conf, - gk, - &mut vec!(kmod.to_string()), - false, - paths_seen, - cpio_state, - cpio_writer - ) - } - } - } + "kmod" => match iter.next() { + None => Err(io::Error::from(io::ErrorKind::InvalidData)), + Some(kmod) => gather_archive_kmods( + conf, + gk, + &mut vec![kmod.to_string()], + false, + paths_seen, + cpio_state, + cpio_writer, + ), + }, "try-kmod" => { match iter.next() { None => Err(io::Error::from(io::ErrorKind::InvalidData)), @@ -1501,12 +1434,12 @@ fn manifest_parse_one( gather_archive_kmods( conf, gk, - &mut vec!(kmod.to_string()), + &mut vec![kmod.to_string()], // ignore_missing: true, paths_seen, cpio_state, - cpio_writer + cpio_writer, ) } } @@ -1521,7 +1454,7 @@ fn manifest_parse_one( autorun_idx, cpio_state, &mut cpio_writer, - Some(autorun_path) + Some(autorun_path), )?; } match autorun_idx_before == *autorun_idx { @@ -1529,16 +1462,14 @@ fn manifest_parse_one( false => Ok(()), } } - "tree" => { - manifest_tree( - conf, - paths_seen, - cpio_state, - cpio_writer, - iter.next(), - iter.next() - ) - } + "tree" => manifest_tree( + conf, + paths_seen, + cpio_state, + cpio_writer, + iter.next(), + iter.next(), + ), "filter" => { let p = manifest_name_sub_abs_path(conf, iter.next())?; paths_seen.insert(p); @@ -1591,7 +1522,7 @@ fn manifest_parse( fests.pop(); continue; } - Ok(_) => {}, + Ok(_) => {} } if let Err(e) = manifest_parse_one( @@ -1603,7 +1534,7 @@ fn manifest_parse( cpio_state, &mut cpio_out, &mut lbuf, - fests + fests, ) { eprintln!("failed to parse manifest line: {}", lbuf); return Err(e); @@ -1615,8 +1546,8 @@ fn manifest_parse( #[cfg(test)] mod tests { use super::*; - use std::io::Read; use std::ffi::OsString; + use std::io::Read; use std::os::unix::fs::OpenOptionsExt; struct TempDir { @@ -1627,12 +1558,18 @@ mod tests { pub fn new() -> TempDir { let mut b = [0u8; 16]; let mut dirname = String::from("test-rapido-cut-"); - fs::File::open("/dev/urandom").unwrap().read_exact(&mut b).unwrap(); + fs::File::open("/dev/urandom") + .unwrap() + .read_exact(&mut b) + .unwrap(); for i in &b { dirname.push_str(&format!("{:02x}", i)); } fs::create_dir(&dirname).unwrap(); - TempDir { dir: PathBuf::from(&dirname), dirname } + TempDir { + dir: PathBuf::from(&dirname), + dirname, + } } } @@ -1652,7 +1589,7 @@ mod tests { let mut libs_seen: HashSet = HashSet::new(); let mut paths_seen: HashSet = HashSet::new(); let mut gk: Option = None; - let mut fests = vec!(fest_rdr); + let mut fests = vec![fest_rdr]; manifest_parse( conf, &mut gk, @@ -1674,22 +1611,20 @@ mod tests { let basefest = format!("{}/base.fest", td.dirname); fs::write( &basefest, - format!("dir /basefirst\ninclude {}\ndir /baseafter", ifest) - ).unwrap(); + format!("dir /basefirst\ninclude {}\ndir /baseafter", ifest), + ) + .unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&basefest).unwrap() - ); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&basefest).unwrap()); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -1709,18 +1644,15 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, "dir /a\nslink /b /a").unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -1744,18 +1676,15 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, "dir /a${KEY1}x\nslink /${KEY2} /a${KEY1}x").unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -1766,7 +1695,7 @@ mod tests { let ae = aw.next().unwrap().unwrap(); assert_eq!(ae.name_str(), "VAL2"); assert_eq!(ae.md.mode & cpio::S_IFMT, cpio::S_IFLNK); - assert_eq!(ae.md.len, 7); // /aVAL1x + assert_eq!(ae.md.len, 7); // /aVAL1x } #[test] @@ -1779,18 +1708,15 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, format!("file /a/b {}\nfile /c", file)).unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -1817,18 +1743,15 @@ mod tests { // unlike "bin", "try-bin" ignores missing files fs::write(&fest, "bin bash\ntry-bin th1s-doe5-not-ex1st").unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -1861,12 +1784,12 @@ mod tests { // copy bash into our directory tree let src = path_stat(&GatherEnt::NameStatic("bash")).unwrap(); - let mut inf = fs::OpenOptions::new().read(true).open(&src.path) - .unwrap(); + let mut inf = fs::OpenOptions::new().read(true).open(&src.path).unwrap(); let mut outf_ops = fs::OpenOptions::new(); // need to set exec mode to trigger ELF parsing... outf_ops.write(true).create(true).mode(0o777); - let mut outf = outf_ops.open(&format!("{}/this/is/bash", td.dirname)) + let mut outf = outf_ops + .open(&format!("{}/this/is/bash", td.dirname)) .unwrap(); io::copy(&mut inf, &mut outf).expect("copy failed"); @@ -1879,18 +1802,15 @@ mod tests { // TODO: fix this by using a bin-tree manifest directive instead? fs::write(&fest, format!("bin ./{}", td.dirname)).unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -1902,7 +1822,7 @@ mod tests { // + bash ELF dependencies // // check for files only - let mut got_files: Vec = vec!(); + let mut got_files: Vec = vec![]; while let Some(ae) = aw.next() { assert!(ae.is_ok()); @@ -1912,8 +1832,8 @@ mod tests { cpio::S_IFREG | cpio::S_IFLNK => { let p = Path::new(an).file_name().unwrap().to_os_string(); got_files.push(p); - }, - _ => {}, + } + _ => {} }; } @@ -1921,21 +1841,30 @@ mod tests { OsString::from("file"), OsString::from("bash"), OsString::from("test.fest"), - ].iter().for_each( - |e| assert!( - got_files.contains(&e), "{:?} missing from {:?}", e, got_files - ) - ); - deps.iter().filter_map(|e| match e { - GatherEnt::LibRunPath(n, _) => Some(OsString::from(n)), - GatherEnt::Lib(n) => Some(OsString::from(n)), - _ => panic!("got non lib in ELF deps"), - }) - .for_each( - |e| assert!( - got_files.contains(&e), "{:?} missing from {:?}", e, got_files + ] + .iter() + .for_each(|e| { + assert!( + got_files.contains(&e), + "{:?} missing from {:?}", + e, + got_files ) - ); + }); + deps.iter() + .filter_map(|e| match e { + GatherEnt::LibRunPath(n, _) => Some(OsString::from(n)), + GatherEnt::Lib(n) => Some(OsString::from(n)), + _ => panic!("got non lib in ELF deps"), + }) + .for_each(|e| { + assert!( + got_files.contains(&e), + "{:?} missing from {:?}", + e, + got_files + ) + }); } // based on test_kmod_context_full_load() @@ -1952,27 +1881,32 @@ mod tests { concat!( "kernel/mod_a.ko: kernel/mod_b.ko.xz kernel/mod_c.ko\n", "kernel/mod_b.ko.xz:\n" - ) - ).unwrap(); + ), + ) + .unwrap(); fs::write( &format!("{kmods_root}/modules.softdep"), - "softdep mod_a pre: mod_d post: mod_e mod_f\n" - ).unwrap(); + "softdep mod_a pre: mod_d post: mod_e mod_f\n", + ) + .unwrap(); fs::write( &format!("{kmods_root}/modules.weakdep"), concat!( "weakdep mod_a mod_g\nweakdep mod_a mod_h\n", "weakdep mod_b mod_i\nweakdep mod_b mod_j\n" - ) - ).unwrap(); + ), + ) + .unwrap(); fs::write( &format!("{kmods_root}/modules.builtin"), - "kernel/mod_builtin.ko\n" - ).unwrap(); + "kernel/mod_builtin.ko\n", + ) + .unwrap(); fs::write( &format!("{kmods_root}/modules.alias"), - "alias alias_for_b mod_b\nalias mod-b mod_b\nalias mod-intel-b mod_b\n" - ).unwrap(); + "alias alias_for_b mod_b\nalias mod-b mod_b\nalias mod-intel-b mod_b\n", + ) + .unwrap(); } #[test] @@ -1980,7 +1914,10 @@ mod tests { let td = TempDir::new(); let conf = HashMap::from([ - ("KERNEL_INSTALL_MOD_PATH".to_string(), format!("{}/mods", td.dirname)), + ( + "KERNEL_INSTALL_MOD_PATH".to_string(), + format!("{}/mods", td.dirname), + ), ("KERNEL_RELEASE".to_string(), "6.66".to_string()), ]); @@ -1990,18 +1927,15 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, "kmod mod_a\nkmod mod-builtin\ntry-kmod mod-no").unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -2052,8 +1986,9 @@ mod tests { let inc_fest = format!("{}/included.fest", td.dirname); fs::write( &inc_fest, - format!("autorun {} {}\n", inc_autorun, last_autorun) - ).unwrap(); + format!("autorun {} {}\n", inc_autorun, last_autorun), + ) + .unwrap(); let autorun = format!("{}/autorun.sh", td.dirname); fs::write(&autorun, "echo first\n").unwrap(); @@ -2061,21 +1996,19 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write( &fest, - format!("autorun {}\ninclude {}\ndir /baseafter", autorun, inc_fest) - ).unwrap(); + format!("autorun {}\ninclude {}\ndir /baseafter", autorun, inc_fest), + ) + .unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -2127,23 +2060,20 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, format!("tree / {}", td.dirname)).unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); - let mut got_paths: Vec = vec!(); + let mut got_paths: Vec = vec![]; while let Some(ae) = aw.next() { assert!(ae.is_ok()); @@ -2177,11 +2107,9 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, format!("bin bash\ntree / {}", td.dirname)).unwrap(); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; @@ -2197,8 +2125,9 @@ mod tests { &mut paths_seen, &mut cpio_state, &mut cpio_out, - &mut vec!(rdr), - ).expect("bad manifest"); + &mut vec![rdr], + ) + .expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -2216,7 +2145,10 @@ mod tests { assert_eq!( paths_seen.contains(&p), true, - "{:?} missing from paths_seen: {:?}", p, paths_seen); + "{:?} missing from paths_seen: {:?}", + p, + paths_seen + ); } } @@ -2229,14 +2161,17 @@ mod tests { fs::create_dir_all(&format!("{}/this/isnt/filtered", td.dirname)).unwrap(); let fest = format!("{}/test.fest", td.dirname); - fs::write(&fest, - format!("filter /this/is\nfilter /this/filter\ntree / {}", td.dirname) - ).unwrap(); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); + fs::write( + &fest, + format!( + "filter /this/is\nfilter /this/filter\ntree / {}", + td.dirname + ), + ) + .unwrap(); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; @@ -2252,13 +2187,14 @@ mod tests { &mut paths_seen, &mut cpio_state, &mut cpio_out, - &mut vec!(rdr), - ).expect("bad manifest"); + &mut vec![rdr], + ) + .expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); - let mut got_paths: Vec = vec!(); + let mut got_paths: Vec = vec![]; while let Some(ae) = aw.next() { assert!(ae.is_ok()); let ae = ae.unwrap(); @@ -2287,18 +2223,15 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, format!("file /a/b/.././b/../b/c {}", file)).unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); - test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr) - .expect("bad manifest"); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); + test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr).expect("bad manifest"); cpio_out.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = cpio::archive_walk(cpio_out).unwrap(); @@ -2324,16 +2257,14 @@ mod tests { let fest = format!("{}/test.fest", td.dirname); fs::write(&fest, format!("file /a/b/.././../../below")).unwrap(); - let props = cpio::ArchiveProperties{ + let props = cpio::ArchiveProperties { data_align: 4096, ..cpio::ArchiveProperties::default() }; let mut cpio_state = cpio::ArchiveState::new(props); let mut cpio_out = io::Cursor::new(Vec::new()); - let rdr = io::BufReader::new( - fs::OpenOptions::new().read(true).open(&fest).unwrap() - ); + let rdr = io::BufReader::new(fs::OpenOptions::new().read(true).open(&fest).unwrap()); let e = test_manifest_parse(&conf, &mut cpio_state, &mut cpio_out, rdr); assert_eq!(e.is_err(), true); assert_eq!(e.unwrap_err().kind(), io::ErrorKind::InvalidInput); diff --git a/src/bin/rapido-init.rs b/src/bin/rapido-init.rs index c78214a4..dc471fd4 100644 --- a/src/bin/rapido-init.rs +++ b/src/bin/rapido-init.rs @@ -1,12 +1,12 @@ // SPDX-License-Identifier: (GPL-2.0 OR GPL-3.0) // Copyright (C) 2025 SUSE LLC -use std::io::{self, Write}; +use std::collections::HashMap; use std::ffi::OsString; use std::fs; -use std::collections::HashMap; +use std::io::{self, Write}; use std::os::unix; -use std::process::Command; use std::os::unix::process::CommandExt; +use std::process::Command; use std::str; // we expect it in root on VMs @@ -64,8 +64,7 @@ fn init_mount(do_debugfs: bool, do_virtfs: bool) -> io::Result<()> { Ok(()) } -#[derive(PartialEq)] -#[derive(Debug)] +#[derive(PartialEq, Debug)] struct KcliArgs<'a> { rapido_hostname: Option<&'a str>, rapido_vm_num: Option<&'a str>, @@ -94,50 +93,48 @@ fn kcli_parse(kcmdline: &[u8]) -> io::Result> { for w in kcmdline.split(|c| *c == b' ' || *c == b'\n') { match w { // rapido.hostname - [b'r', b'a', b'p', b'i', b'd', b'o', b'.', - b'h', b'o', b's', b't', b'n', b'a', b'm', b'e', b'=', val @ ..] => { + [b'r', b'a', b'p', b'i', b'd', b'o', b'.', b'h', b'o', b's', b't', b'n', b'a', b'm', b'e', b'=', val @ ..] => + { args.rapido_hostname = match str::from_utf8(val) { Err(_) => { return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(s) => Some(s), }; - }, + } // rapido.vm_num - [b'r', b'a', b'p', b'i', b'd', b'o', b'.', - b'v', b'm', b'_', b'n', b'u', b'm', b'=', val @ ..] => { + [b'r', b'a', b'p', b'i', b'd', b'o', b'.', b'v', b'm', b'_', b'n', b'u', b'm', b'=', val @ ..] => + { args.rapido_vm_num = match str::from_utf8(val) { Err(_) => { return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(s) => Some(s), }; - - }, + } // rapido.mac.= - [b'r', b'a', b'p', b'i', b'd', b'o', b'.', - b'm', b'a', b'c', b'.', tap_mac_kv @ ..] => { + [b'r', b'a', b'p', b'i', b'd', b'o', b'.', b'm', b'a', b'c', b'.', tap_mac_kv @ ..] => { let (tap, mac) = match str::from_utf8(tap_mac_kv) { Err(_) => { return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(s) if !s.contains('=') => { return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(s) => s.split_once('=').unwrap(), }; let map = match args.rapido_tap_mac { - None => HashMap::from([ (tap, mac) ]), + None => HashMap::from([(tap, mac)]), Some(mut m) => { m.insert(tap, mac); m - }, + } }; args.rapido_tap_mac = Some(map); - }, + } // rapido.stty=, - [b'r', b'a', b'p', b'i', b'd', b'o', b'.', - b's', b't', b't', b'y', b'=', rows_cols @ ..] => { + [b'r', b'a', b'p', b'i', b'd', b'o', b'.', b's', b't', b't', b'y', b'=', rows_cols @ ..] => + { args.stty_rc = match str::from_utf8(rows_cols) { Err(_) => Err(io::Error::from(io::ErrorKind::InvalidData)), Ok(rs_cs) => match rs_cs.split_once(',') { @@ -148,31 +145,30 @@ fn kcli_parse(kcmdline: &[u8]) -> io::Result> { } else { Ok(Some(rs_cs)) } - }, - } + } + }, }?; - }, + } // systemd.machine_id - [b's', b'y', b's', b't', b'e', b'm', b'd', b'.', - b'm', b'a', b'c', b'h', b'i', b'n', b'e', b'_', b'i', b'd', b'=', - val @ ..] => { + [b's', b'y', b's', b't', b'e', b'm', b'd', b'.', b'm', b'a', b'c', b'h', b'i', b'n', b'e', b'_', b'i', b'd', b'=', val @ ..] => + { args.systemd_machine_id = match str::from_utf8(val) { Err(_) => { return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(s) => Some(s), }; - }, + } // console [b'c', b'o', b'n', b's', b'o', b'l', b'e', b'=', val @ ..] => { args.console = match str::from_utf8(val) { Err(_) => { return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(s) => Some(s), }; - }, - [ _unused @ .. ] => {}, + } + [_unused @ ..] => {} }; } @@ -186,16 +182,17 @@ fn kmods_load(kmods: &Vec<&str>) -> io::Result<()> { .env("PATH", "/usr/sbin:/usr/bin:/sbin:/bin") .arg("-a") .args(kmods) - .status() { + .status() + { Err(e) => { eprintln!("modprobe error: {:?}", e); return Err(io::Error::from(io::ErrorKind::BrokenPipe)); - }, + } Ok(status) if !status.success() => { println!("modprobe failed: {:?}", status); return Err(io::Error::from(io::ErrorKind::BrokenPipe)); - }, - Ok(_) => {}, + } + Ok(_) => {} }; } @@ -208,15 +205,13 @@ fn init_hostname(kcli_args: &KcliArgs) -> io::Result { let mut h = String::from("rapido"); h.push_str(kcli_args.rapido_vm_num.unwrap()); h - }, - Some(hd) => { - match hd.split_once('.') { - Some((h, d)) => { - fs::write("/proc/sys/kernel/domainname", d)?; - h.to_string() - }, - None => hd.to_string(), + } + Some(hd) => match hd.split_once('.') { + Some((h, d)) => { + fs::write("/proc/sys/kernel/domainname", d)?; + h.to_string() } + None => hd.to_string(), }, }; @@ -236,19 +231,22 @@ fn init_net_conf(kcli_args: &KcliArgs) -> io::Result<()> { } match &kcli_args.rapido_tap_mac { - Some(map) => for (tap, mac) in map { - let mut f = match fs::OpenOptions::new() - .write(true) - .append(true) - .create(false) - .open(format!("{}/{}.network", vm_netdir, tap)) { + Some(map) => { + for (tap, mac) in map { + let mut f = match fs::OpenOptions::new() + .write(true) + .append(true) + .create(false) + .open(format!("{}/{}.network", vm_netdir, tap)) + { Err(_) => continue, Ok(f) => f, - }; + }; - write!(f, "\n[Match]\nMACAddress={}", mac)?; - }, - None => {}, + write!(f, "\n[Match]\nMACAddress={}", mac)?; + } + } + None => {} } let mut f = fs::OpenOptions::new() @@ -277,7 +275,7 @@ fn init_net_service(systemd_machine_id: Option<&str>) -> io::Result<()> { None => { eprintln!("systemd.machine_id missing from kcli"); Err(io::Error::from(io::ErrorKind::InvalidInput)) - }, + } Some(mid) => fs::write("/etc/machine-id", mid), }?; @@ -294,7 +292,7 @@ fn init_net_service(systemd_machine_id: Option<&str>) -> io::Result<()> { .map(|res| res.map(|e| e.path().into_os_string())) .collect::, io::Error>>()?; - let mut udevadm_args = vec!(OsString::from("trigger")); + let mut udevadm_args = vec![OsString::from("trigger")]; udevadm_args.append(&mut entries); let status = Command::new("udevadm") .args(udevadm_args) @@ -348,8 +346,8 @@ fn init_shell(envs: [(&str, &str); 6]) -> io::Result<()> { Err(e) => { eprintln!("bash error {:?}", e); return Err(io::Error::from(io::ErrorKind::BrokenPipe)); - }, - Ok(status) if status.success() => {}, + } + Ok(status) if status.success() => {} Ok(status) => eprintln!("bash ended with status {}", status), } Ok(()) @@ -370,7 +368,7 @@ fn init_main() -> io::Result<()> { Err(e) => { println!("failed to open {}: {}", RAPIDO_CONF, e); return Err(e); - }, + } }; let mut reader = io::BufReader::new(f); let conf = match kv_conf::kv_conf_process(&mut reader) { @@ -378,14 +376,15 @@ fn init_main() -> io::Result<()> { Err(e) => { println!("failed to process {}: {:?}", RAPIDO_CONF, e); return Err(e); - }, + } }; let has_net = match fs::symlink_metadata("/rapido-rsc/net") { Err(_) => false, Ok(md) => md.is_dir(), }; - let has_dyn_debug = conf.contains_key("DYN_DEBUG_MODULES") || conf.contains_key("DYN_DEBUG_FILES"); + let has_dyn_debug = + conf.contains_key("DYN_DEBUG_MODULES") || conf.contains_key("DYN_DEBUG_FILES"); let has_virtfs = conf.contains_key("VIRTFS_SHARE_PATH"); let has_systemd = match fs::symlink_metadata(SYSTEMD_BIN_PATH) { Err(_) => false, @@ -443,10 +442,10 @@ fn main() -> io::Result<()> { match init_main() { Err(e) => { eprintln!("init failed: {:?}", e); - }, + } Ok(_) => { eprintln!("rapido-init completed, shutting down..."); - }, + } } if let Err(e) = init_shutdown() { @@ -494,8 +493,8 @@ mod tests { rapido_vm_num: None, rapido_hostname: None, rapido_tap_mac: Some(HashMap::from([ - ("tap1", "b8:ac:24:45:c5:01"), - ("tap2", "b8:ac:24:45:c5:02"), + ("tap1", "b8:ac:24:45:c5:01"), + ("tap2", "b8:ac:24:45:c5:02"), ])), stty_rc: Some("120,12"), systemd_machine_id: None, diff --git a/src/bin/rapido-vm.rs b/src/bin/rapido-vm.rs index 335e59d8..57f53043 100644 --- a/src/bin/rapido-vm.rs +++ b/src/bin/rapido-vm.rs @@ -39,9 +39,14 @@ fn vm_mac_gen(vm_num: u64, vm_tap: &str) -> String { hasher.write_u64(vm_num); hasher.write(vm_tap.as_bytes()); let h: u64 = hasher.finish(); - format!("b8:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", - h & 0xff, (h >> 8) & 0xff, (h >> 16) & 0xff, (h >> 24) & 0xff, - (h >> 32) & 0xff) + format!( + "b8:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + h & 0xff, + (h >> 8) & 0xff, + (h >> 16) & 0xff, + (h >> 24) & 0xff, + (h >> 32) & 0xff + ) } struct VmResources { @@ -58,8 +63,8 @@ fn vm_resource_line_process(line: &[u8], rscs: &mut VmResources) -> io::Result { + [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', b'/', b'c', b'p', b'u', b'/', val @ ..] => + { rscs.cpus = match str::from_utf8(val) { Ok(s) => match s.parse::() { Err(_) => Err(io::Error::from(io::ErrorKind::InvalidData)), @@ -69,38 +74,30 @@ fn vm_resource_line_process(line: &[u8], rscs: &mut VmResources) -> io::Result { + [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', b'/', b'm', b'e', b'm', b'/', val @ ..] => + { rscs.mem = match str::from_utf8(val) { Err(_) => Err(io::Error::from(io::ErrorKind::InvalidData)), - Ok(s) => { - match s.rsplit_once(['m', 'M', 'g', 'G']) { - None if s.parse::().is_ok() => Ok(s.to_string()), - Some((n, u)) if n.parse::().is_ok() && u == "" => { - Ok(s.to_string()) - }, - None | Some((_, _)) => { - Err(io::Error::from(io::ErrorKind::InvalidData)) - }, - } + Ok(s) => match s.rsplit_once(['m', 'M', 'g', 'G']) { + None if s.parse::().is_ok() => Ok(s.to_string()), + Some((n, u)) if n.parse::().is_ok() && u == "" => Ok(s.to_string()), + None | Some((_, _)) => Err(io::Error::from(io::ErrorKind::InvalidData)), }, }?; - }, + } // rapido-rsc/qemu/custom_args - [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', b'/', - b'q', b'e', b'm', b'u', b'/', - b'c', b'u', b's', b't', b'o', b'm', b'_', b'a', b'r', b'g', b's'] => { + [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', b'/', b'q', b'e', b'm', b'u', b'/', b'c', b'u', b's', b't', b'o', b'm', b'_', b'a', b'r', b'g', b's'] => + { // obsolete way for images to inject their own qemu params. // cut scripts should instead assert that the args required are set. eprintln!("ignoring qemu custom_args presence"); - }, + } // rapido-rsc/net - [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', b'/', - b'n', b'e', b't'] => { + [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', b'/', b'n', b'e', b't'] => { rscs.net = true; - }, + } // catch any unprocessed rapido-rsc path, so we return true. - [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', .. ] => {}, + [b'r', b'a', b'p', b'i', b'd', b'o', b'-', b'r', b's', b'c', ..] => {} // not a rapido-rsc path. _ => return Ok(false), } @@ -111,7 +108,7 @@ fn vm_resource_line_process(line: &[u8], rscs: &mut VmResources) -> io::Result io::Result { // rapido defaults - let mut rscs = VmResources{ + let mut rscs = VmResources { cpus: 2, mem: "512M".to_string(), net: false, @@ -128,28 +125,28 @@ fn vm_resources_get(initramfs_img: &str) -> io::Result { Err(e) => { eprintln!("archive traversal failed"); return Err(e); - }, + } Ok(ent) => ent, }; match vm_resource_line_process( // namesize includes nul. cpio ensures 0< namesize < PATH_MAX+1 - &ent.name[0 .. (ent.namesize as usize) - 1], - &mut rscs + &ent.name[0..(ent.namesize as usize) - 1], + &mut rscs, )? { true => in_rapido_rsc_path = true, // optimization: break loop when leaving rapido-rsc/ paths. // rsc entries must be placed together in the archive and can be // placed at the start to minimise traversal. false if in_rapido_rsc_path => break, - false => {}, + false => {} } } Ok(rscs) } -struct QemuArgs<'a> { +struct QemuArgs<'a> { qemu_bin: &'a str, kernel_img: String, console: &'a str, @@ -157,7 +154,7 @@ struct QemuArgs<'a> { } fn vm_qemu_args_get(conf: &HashMap) -> io::Result { - let mut params = vec!(); + let mut params = vec![]; let mut qemu_args: Option = None; //let (kconfig: String, krel: Option<&str>) = match conf.get("KERNEL_SRC") { @@ -168,15 +165,13 @@ fn vm_qemu_args_get(conf: &HashMap) -> io::Result { None => { let rel = host_kernel_vers()?; (format!("/boot/config-{rel}"), Some(rel.to_string())) - }, + } }, }; match fs::symlink_metadata("/dev/kvm") { - Ok(md) if md.file_type().is_char_device() => { - params.extend(["-machine", "accel=kvm"]) - }, - Err(_) | Ok(_) => {}, + Ok(md) if md.file_type().is_char_device() => params.extend(["-machine", "accel=kvm"]), + Err(_) | Ok(_) => {} }; let ksrc = conf.get("KERNEL_SRC"); @@ -184,7 +179,7 @@ fn vm_qemu_args_get(conf: &HashMap) -> io::Result { let f = fs::OpenOptions::new().read(true).open(&kconfig)?; for line in io::BufReader::new(f).lines().map_while(Result::ok) { if line == "CONFIG_X86_64=y" { - qemu_args = Some(QemuArgs{ + qemu_args = Some(QemuArgs { kernel_img: match ksrc { Some(ks) if !ks.is_empty() => format!("{ks}/arch/x86/boot/bzImage"), // krel always set without KERNEL_SRC @@ -196,11 +191,8 @@ fn vm_qemu_args_get(conf: &HashMap) -> io::Result { }); break; } else if line == "CONFIG_ARM64=y" { - params.extend([ - "-machine", "virt,gic-version=host", - "-cpu", "host" - ]); - qemu_args = Some(QemuArgs{ + params.extend(["-machine", "virt,gic-version=host", "-cpu", "host"]); + qemu_args = Some(QemuArgs { kernel_img: match ksrc { Some(ks) => format!("{ks}/arch/arm64/boot/Image"), None => format!("/boot/Image-{}", krel.unwrap()), @@ -210,8 +202,8 @@ fn vm_qemu_args_get(conf: &HashMap) -> io::Result { params, }); break; - } else if line == "CONFIG_PPC64=y" { - qemu_args = Some(QemuArgs{ + } else if line == "CONFIG_PPC64=y" { + qemu_args = Some(QemuArgs { kernel_img: match ksrc { Some(ks) => format!("{ks}/arch/powerpc/boot/zImage"), None => format!("/boot/vmlinux-{}", krel.unwrap()), @@ -221,8 +213,8 @@ fn vm_qemu_args_get(conf: &HashMap) -> io::Result { params, }); break; - } else if line == "CONFIG_S390=y" { - qemu_args = Some(QemuArgs{ + } else if line == "CONFIG_S390=y" { + qemu_args = Some(QemuArgs { kernel_img: match ksrc { Some(ks) => format!("{ks}/arch/s390/boot/bzImage"), None => format!("/boot/bzImage-{}", krel.unwrap()), @@ -257,13 +249,14 @@ fn host_stty_size(kcmdline: &mut String, kparam: &'static str) -> Option<()> { let out = match process::Command::new("stty") .args(&["size"]) .stdout(process::Stdio::piped()) - .spawn() { + .spawn() + { Err(_) => return None, Ok(p) => match p.wait_with_output() { Err(_) => return None, Ok(o) if !o.status.success() => return None, Ok(o) => o.stdout, - } + }, }; let mut iter = out.split(|c| !matches!(*c, b'0'..=b'9')); @@ -285,14 +278,17 @@ fn host_stty_size(kcmdline: &mut String, kparam: &'static str) -> Option<()> { Some(()) } -fn vm_start(vm_num: u64, vm_pid_file: &str, initramfs_img: &str, conf: &HashMap) -> io::Result<()> { +fn vm_start( + vm_num: u64, + vm_pid_file: &str, + initramfs_img: &str, + conf: &HashMap, +) -> io::Result<()> { let mut qemu_args = vm_qemu_args_get(conf)?; // systemd (incl. networkd) needs a 32-char hex ID for dhcp leases, etc. let mut kcmdline = format!( "rdinit=/rdinit console={} rapido.vm_num={} systemd.machine_id={:032x}", - qemu_args.console, - vm_num, - vm_num + qemu_args.console, vm_num, vm_num ); host_stty_size(&mut kcmdline, " rapido.stty="); let net_conf_dir = format!( @@ -302,33 +298,38 @@ fn vm_start(vm_num: u64, vm_pid_file: &str, initramfs_img: &str, conf: &HashMap< ); match fs::read_to_string(format!("{net_conf_dir}/hostname")) { - Err(e) if e.kind() == io::ErrorKind::NotFound => {}, + Err(e) if e.kind() == io::ErrorKind::NotFound => {} Err(e) => return Err(e), Ok(hn) => { kcmdline.push_str(&format!(" rapido.hostname={}", hn.trim_end())); - }, + } } let rscs = match vm_resources_get(&initramfs_img) { Err(e) if e.kind() == io::ErrorKind::NotFound => { eprintln!("no initramfs image at {initramfs_img}. Run \"cut_X\" script?"); return Err(e); - }, + } Err(e) => return Err(e), Ok(r) => r, }; let cpus = format!("{},sockets={},cores=1,threads=1", rscs.cpus, rscs.cpus); qemu_args.params.extend([ - "-smp", &cpus, - "-m", &rscs.mem, - "-kernel", &qemu_args.kernel_img, - "-initrd", initramfs_img, - "-pidfile", vm_pid_file, + "-smp", + &cpus, + "-m", + &rscs.mem, + "-kernel", + &qemu_args.kernel_img, + "-initrd", + initramfs_img, + "-pidfile", + vm_pid_file, ]); // params is Vec<&str>, so stash generated net Strings elsewhere - let mut net_params_stash: Vec = vec!(); + let mut net_params_stash: Vec = vec![]; if !rscs.net { qemu_args.params.extend(["-net", "none"]); @@ -336,13 +337,13 @@ fn vm_start(vm_num: u64, vm_pid_file: &str, initramfs_img: &str, conf: &HashMap< kcmdline.push_str(" net.ifnames=0"); let mut i = 0; - for entry in fs::read_dir(&net_conf_dir)? { + for entry in fs::read_dir(&net_conf_dir)? { let entry = entry?; let path = entry.path(); match path.extension() { None => continue, Some(e) if e.as_encoded_bytes() != b"network" => continue, - Some(_) => {}, + Some(_) => {} } let vm_tap = match path.file_stem() { None => continue, @@ -369,16 +370,16 @@ fn vm_start(vm_num: u64, vm_pid_file: &str, initramfs_img: &str, conf: &HashMap< eprintln!("{:?} missing expected 0x flags prefix", tp); return Err(io::Error::from(io::ErrorKind::InvalidData)); } - }, + } }, }; match tun_flags { Err(_) => { eprintln!("unexpected tun_flags at {:?}", tp); return Err(io::Error::from(io::ErrorKind::InvalidData)); - }, + } Ok(flags_val) if flags_val & IFF_TAP != IFF_TAP => continue, - Ok(_) => {}, + Ok(_) => {} } let tap_mac = vm_mac_gen(vm_num, vm_tap); @@ -388,10 +389,10 @@ fn vm_start(vm_num: u64, vm_pid_file: &str, initramfs_img: &str, conf: &HashMap< kcmdline.push_str(&format!(" rapido.mac.{vm_tap}={tap_mac}")); net_params_stash.extend([ - "-device".to_string(), - format!("virtio-net,netdev=if{i},mac={tap_mac}"), - "-netdev".to_string(), - format!("tap,id=if{i},script=no,downscript=no,ifname={vm_tap}"), + "-device".to_string(), + format!("virtio-net,netdev=if{i},mac={tap_mac}"), + "-netdev".to_string(), + format!("tap,id=if{i},script=no,downscript=no,ifname={vm_tap}"), ]); i += 1; } @@ -426,32 +427,27 @@ fn vm_start(vm_num: u64, vm_pid_file: &str, initramfs_img: &str, conf: &HashMap< // TODO stdout / stderr lost here? eprintln!("{} failed: {:?}", qemu_args.qemu_bin, e); Err(io::Error::from(io::ErrorKind::BrokenPipe)) - }, + } Ok(status) if !status.success() => { eprintln!("{} exited with status: {}", qemu_args.qemu_bin, status); Ok(()) - }, + } Ok(_) => Ok(()), } } fn main() -> io::Result<()> { let conf = match rapido::host_rapido_conf_open(rapido::RAPIDO_CONF_PATH) { - Err(e) if e.kind() == io::ErrorKind::NotFound => { - rapido::conf_defaults() - }, + Err(e) if e.kind() == io::ErrorKind::NotFound => rapido::conf_defaults(), Err(e) => return Err(e), Ok((f, p)) => { let mut conf = rapido::conf_defaults(); - if let Err(e) = kv_conf::kv_conf_process_append( - io::BufReader::new(f), - &mut conf - ) { + if let Err(e) = kv_conf::kv_conf_process_append(io::BufReader::new(f), &mut conf) { eprintln!("failed to process {:?}: {:?}", p, e); return Err(e); } conf - }, + } }; // unwrap: both keys have defaults set let pid_dir = conf.get("QEMU_PID_DIR").unwrap(); @@ -475,7 +471,7 @@ mod tests { #[test] fn test_vm_resources_parse() { let line = b"rapido-rsc/cpu/5"; - let mut rscs = VmResources{ + let mut rscs = VmResources { cpus: 0, mem: String::new(), net: false, diff --git a/src/cpio/src/lib.rs b/src/cpio/src/lib.rs index 84058ba4..8b8dbc4c 100644 --- a/src/cpio/src/lib.rs +++ b/src/cpio/src/lib.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: (GPL-2.0 OR GPL-3.0) // Copyright (C) 2021-2025 SUSE S.A. -use std::convert::TryInto; use std::convert::TryFrom; +use std::convert::TryInto; use std::fs; use std::io; use std::io::prelude::*; @@ -122,12 +122,14 @@ impl ArchiveMd { Some(t) => t, None => match u32::try_from(md.mtime()) { // check for 2106 epoch overflow - Err(_) => return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "mtime too large for cpio", - )), + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "mtime too large for cpio", + )) + } Ok(m) => m, - } + }, }; let mode = md.mode(); @@ -138,11 +140,11 @@ impl ArchiveMd { // uses 64-bit MMMM Mmmm mmmM MMmm, which is compatible. let rd = md.rdev(); ( - u32::try_from(md.nlink()).ok(), - (((rd >> 32) & 0xfffff000) | ((rd >> 8) & 0x00000fff)) as u32, - (((rd >> 12) & 0xffffff00) | (rd & 0x000000ff)) as u32, + u32::try_from(md.nlink()).ok(), + (((rd >> 32) & 0xfffff000) | ((rd >> 8) & 0x00000fff)) as u32, + (((rd >> 12) & 0xffffff00) | (rd & 0x000000ff)) as u32, ) - }, + } S_IFREG => { if md.nlink() > 1 { // For simplicity's sake, hardlinks are archived like @@ -156,15 +158,17 @@ impl ArchiveMd { ); } (Some(1), 0, 0) - }, + } _ => (u32::try_from(md.nlink()).ok(), 0, 0), }; let len = match u32::try_from(md.len()) { - Err(_) => return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "file too large for newc", - )), + Err(_) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "file too large for newc", + )) + } Ok(l) => l, }; @@ -461,10 +465,7 @@ pub fn archive_padlen(off: u64, alignment: u64) -> u64 { (alignment - (off & (alignment - 1))) % alignment } -pub fn archive_trailer( - state: &mut ArchiveState, - mut writer: W -) -> io::Result { +pub fn archive_trailer(state: &mut ArchiveState, mut writer: W) -> io::Result { const FNAME: &str = "TRAILER!!!"; const FNAME_LEN: usize = FNAME.len() + 1; @@ -514,8 +515,7 @@ pub struct ArchiveEnt { impl ArchiveEnt { // panics if not valid utf-8. Check @name beforehand if undesired. pub fn name_str(&self) -> &str { - let s = str::from_utf8(&self.name[0 .. (self.namesize as usize) - 1]) - .unwrap(); + let s = str::from_utf8(&self.name[0..(self.namesize as usize) - 1]).unwrap(); // data alignment optimization may leave trailing zeros. strip them... match s.split_once('\0') { None => s, @@ -528,13 +528,9 @@ pub struct ArchiveWalker { reader: R, } -pub fn archive_walk( - reader: R, -) -> io::Result> { +pub fn archive_walk(reader: R) -> io::Result> { // kernel extraction skips zeros until header. we don't. - Ok(ArchiveWalker{ - reader, - }) + Ok(ArchiveWalker { reader }) } fn archive_read_newc_md(hdr_md: &[u8]) -> io::Result<(ArchiveMd, u32)> { @@ -545,11 +541,14 @@ fn archive_read_newc_md(hdr_md: &[u8]) -> io::Result<(ArchiveMd, u32)> { return Ok(u); } } - Err(io::Error::new(io::ErrorKind::InvalidData, "invalid hdr field")) + Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid hdr field", + )) }); // unwrap here because successfully read NEWC_HDR_LEN bytes - let md = ArchiveMd{ + let md = ArchiveMd { // skip ino mode: md_iter.nth(1).unwrap()?, uid: md_iter.next().unwrap()?, @@ -563,9 +562,10 @@ fn archive_read_newc_md(hdr_md: &[u8]) -> io::Result<(ArchiveMd, u32)> { }; let namesize = md_iter.next().unwrap()?; if namesize == 0 || namesize > (PATH_MAX + 1) as u32 { - return Err( - io::Error::new(io::ErrorKind::InvalidData, "invalid namesize") - ); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid namesize", + )); } Ok((md, namesize)) @@ -580,7 +580,7 @@ impl Iterator for ArchiveWalker { return match e.kind() { io::ErrorKind::UnexpectedEof => None, _ => Some(Err(e)), - } + }; } match hdr_buf { // we only support newc @@ -590,7 +590,7 @@ impl Iterator for ArchiveWalker { Ok((md, ns)) => (md, ns), }; let mut buf = [0u8; (PATH_MAX + 1) as usize]; - let mut fbuf = &mut buf[0 .. namesize as usize]; + let mut fbuf = &mut buf[0..namesize as usize]; if let Err(e) = self.reader.read_exact(&mut fbuf) { return Some(Err(e)); } @@ -602,23 +602,24 @@ impl Iterator for ArchiveWalker { if let Err(e) = self.reader.seek(io::SeekFrom::Current(seeklen)) { return Some(Err(e)); } - if &buf[0 .. (namesize as usize) - 1] == b"TRAILER!!!" { + if &buf[0..(namesize as usize) - 1] == b"TRAILER!!!" { // cpio trailer treated the same as EOF return None; } - let ae = ArchiveEnt{ + let ae = ArchiveEnt { md, namesize, name: buf, // provide data offset here, to allow callers to grab it? }; Some(Ok(ae)) - }, - [ _bad_hdr @ .. ] => { - return Some(Err( - io::Error::new(io::ErrorKind::InvalidInput, "invalid newc hdr") - )); - }, + } + [_bad_hdr @ ..] => { + return Some(Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid newc hdr", + ))); + } } } } @@ -632,7 +633,7 @@ mod tests { fn test_archive_iter() { let mut c = io::Cursor::new(Vec::new()); let props = ArchiveProperties::default(); - let amd = ArchiveMd{ + let amd = ArchiveMd { nlink: 1, mode: S_IFDIR | 0o777, uid: 1, @@ -654,12 +655,12 @@ mod tests { assert_eq!( ae, - ArchiveEnt{ + ArchiveEnt { md: amd, namesize: ("hello".len() + 1).try_into().unwrap(), name: { let mut buf = [0u8; (PATH_MAX + 1) as usize]; - let mut fbuf = &mut buf[0 .. (PATH_MAX + 1) as usize]; + let mut fbuf = &mut buf[0..(PATH_MAX + 1) as usize]; fbuf.write_all("hello".as_bytes()).unwrap(); buf }, @@ -694,13 +695,15 @@ mod tests { namesize = 2, chksum = 0, fname = "A", - ).unwrap(); + ) + .unwrap(); // namesize=2 is 4-byte aligned: (110 + 2) c.seek(io::SeekFrom::Start(0)).unwrap(); let mut aw = archive_walk(c).unwrap(); assert_eq!( aw.next().unwrap().unwrap_err().kind(), - io::ErrorKind::InvalidInput); + io::ErrorKind::InvalidInput + ); // good magic, corrupt each field with non-hex for corrupt_field in 0..12 { @@ -724,11 +727,12 @@ mod tests { namesize = 2, chksum = 0, fname = "A", - ).unwrap(); + ) + .unwrap(); // namesize=2 is 4-byte aligned: (110 + 2) // matches above - let amd = ArchiveMd{ + let amd = ArchiveMd { mode: 1, uid: 2, gid: 3, @@ -748,24 +752,21 @@ mod tests { let ent = aw.next().unwrap(); // ino, major, minor and chksum are ignored, so won't cause an error - match corrupt_field { + match corrupt_field { 0 | 7 | 8 | 12 => assert_eq!( ent.unwrap(), - ArchiveEnt{ + ArchiveEnt { md: amd, namesize: 2, name: { let mut buf = [0u8; (PATH_MAX + 1) as usize]; - let mut fbuf = &mut buf[0 .. (PATH_MAX + 1) as usize]; + let mut fbuf = &mut buf[0..(PATH_MAX + 1) as usize]; fbuf.write_all("A".as_bytes()).unwrap(); buf }, } ), - _ => assert_eq!( - ent.unwrap_err().kind(), - io::ErrorKind::InvalidData - ), + _ => assert_eq!(ent.unwrap_err().kind(), io::ErrorKind::InvalidData), } } @@ -775,18 +776,9 @@ mod tests { #[test] fn test_archive_path_trim() { - assert_eq!( - b"hello", - path_trim_prefixes(Path::new("hello")).unwrap() - ); - assert_eq!( - b"hello", - path_trim_prefixes(Path::new("./hello")).unwrap() - ); - assert_eq!( - b"hello", - path_trim_prefixes(Path::new("//hello")).unwrap() - ); + assert_eq!(b"hello", path_trim_prefixes(Path::new("hello")).unwrap()); + assert_eq!(b"hello", path_trim_prefixes(Path::new("./hello")).unwrap()); + assert_eq!(b"hello", path_trim_prefixes(Path::new("//hello")).unwrap()); assert_eq!(b"/", path_trim_prefixes(Path::new("/")).unwrap()); // should prob return a single '/' for this... assert_eq!(b"//", path_trim_prefixes(Path::new("//")).unwrap()); @@ -797,7 +789,7 @@ mod tests { fn test_archive_file() { let mut c = io::Cursor::new(Vec::new()); let data = b"this is some file data"; - let amd1 = ArchiveMd{ + let amd1 = ArchiveMd { nlink: 1, mode: S_IFREG | 0o777, uid: 1, @@ -807,7 +799,7 @@ mod tests { rminor: 5, len: 0, }; - let amd2 = ArchiveMd{ + let amd2 = ArchiveMd { len: data.len() as u32, ..amd1 }; @@ -826,12 +818,12 @@ mod tests { assert_eq!( aw.next().unwrap().unwrap(), - ArchiveEnt{ + ArchiveEnt { md: amd1, namesize: (p1.as_os_str().len() + 1).try_into().unwrap(), name: { let mut buf = [0u8; (PATH_MAX + 1) as usize]; - let mut fbuf = &mut buf[0 .. (PATH_MAX + 1) as usize]; + let mut fbuf = &mut buf[0..(PATH_MAX + 1) as usize]; fbuf.write_all(p1.as_os_str().as_encoded_bytes()).unwrap(); buf }, @@ -840,12 +832,12 @@ mod tests { assert_eq!( aw.next().unwrap().unwrap(), - ArchiveEnt{ + ArchiveEnt { md: amd2, namesize: (p2.as_os_str().len() + 1).try_into().unwrap(), name: { let mut buf = [0u8; (PATH_MAX + 1) as usize]; - let mut fbuf = &mut buf[0 .. (PATH_MAX + 1) as usize]; + let mut fbuf = &mut buf[0..(PATH_MAX + 1) as usize]; fbuf.write_all(p2.as_os_str().as_encoded_bytes()).unwrap(); buf }, @@ -861,7 +853,7 @@ mod tests { let mut c = io::Cursor::new(Vec::new()); // datalen > 16-byte alignment to trigger name padding let data = b"this is some file data"; - let amd1 = ArchiveMd{ + let amd1 = ArchiveMd { nlink: 1, mode: S_IFREG | 0o777, uid: 1, @@ -871,14 +863,14 @@ mod tests { rminor: 5, len: 0, }; - let amd2 = ArchiveMd{ + let amd2 = ArchiveMd { len: data.len() as u32, ..amd1 }; let p1 = Path::new("hello"); let p2 = Path::new("bye"); - let props = ArchiveProperties{ + let props = ArchiveProperties { data_align: 16, ..ArchiveProperties::default() }; diff --git a/src/lib.rs b/src/lib.rs index 9256523e..f78e3438 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0 OR GPL-3.0) // Copyright (C) 2025 SUSE LLC +use std::collections::HashMap; use std::env; use std::fs; use std::io; -use std::collections::HashMap; use std::path::PathBuf; use std::str; @@ -39,9 +39,7 @@ pub fn host_kernel_vers() -> io::Result { host_kernel_vers_parse(&kvers) } -pub fn conf_src_or_host_kernel_vers( - conf: &HashMap -) -> io::Result { +pub fn conf_src_or_host_kernel_vers(conf: &HashMap) -> io::Result { match conf.get("KERNEL_SRC") { Some(ksrc) if !ksrc.is_empty() => { let b = fs::read(format!("{ksrc}/include/config/kernel.release"))?; @@ -50,7 +48,7 @@ pub fn conf_src_or_host_kernel_vers( None => &b, }; Ok(String::from_utf8_lossy(btrimmed).to_string()) - }, + } None | Some(_) => match conf.get("KERNEL_RELEASE") { Some(krel) => Ok(krel.clone()), None => host_kernel_vers(), @@ -61,8 +59,8 @@ pub fn conf_src_or_host_kernel_vers( // return kmod dependencies based on rapido @conf qemu parameters pub fn conf_kmod_deps(conf: &HashMap) -> Vec<&str> { let mut deps = match conf.get("QEMU_EXTRA_ARGS") { - Some(v) if v.contains("virtio-rng-pci") => vec!("virtio_rng"), - Some(_) | None => vec!(), + Some(v) if v.contains("virtio-rng-pci") => vec!["virtio_rng"], + Some(_) | None => vec![], }; if conf.get("VIRTFS_SHARE_PATH").is_some() { @@ -74,30 +72,22 @@ pub fn conf_kmod_deps(conf: &HashMap) -> Vec<&str> { // return an open file handle and path for rapido.conf, which may // be @rapido_conf_path or overridden by RAPIDO_CONF env -pub fn host_rapido_conf_open( - rapido_conf_path: &str, -) -> io::Result<(fs::File, PathBuf)> { +pub fn host_rapido_conf_open(rapido_conf_path: &str) -> io::Result<(fs::File, PathBuf)> { // env file takes precedence match env::var("RAPIDO_CONF") { - Ok(c) => { - match fs::File::open(&c) { - Err(e) if e.kind() == io::ErrorKind::NotFound => { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!("RAPIDO_CONF missing at {}", c) - )) - }, - Err(e) => Err(e), - Ok(f) => Ok((f, PathBuf::from(c))), - } + Ok(c) => match fs::File::open(&c) { + Err(e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("RAPIDO_CONF missing at {}", c), + )), + Err(e) => Err(e), + Ok(f) => Ok((f, PathBuf::from(c))), }, Err(env::VarError::NotPresent) => { let f = fs::File::open(rapido_conf_path)?; Ok((f, PathBuf::from(rapido_conf_path))) - }, - Err(env::VarError::NotUnicode(_)) => { - Err(io::Error::from(io::ErrorKind::InvalidInput)) - }, + } + Err(env::VarError::NotUnicode(_)) => Err(io::Error::from(io::ErrorKind::InvalidInput)), } } @@ -106,7 +96,10 @@ pub fn conf_defaults() -> HashMap { ("DRACUT_OUT".to_string(), RAPIDO_DRACUT_OUT.to_string()), ("QEMU_PID_DIR".to_string(), RAPIDO_QEMU_PID_DIR.to_string()), ("VM_NET_CONF".to_string(), RAPIDO_NET_CONF_PATH.to_string()), - ("QEMU_EXTRA_ARGS".to_string(), RAPIDO_QEMU_EXTRA_ARGS.to_string()), + ( + "QEMU_EXTRA_ARGS".to_string(), + RAPIDO_QEMU_EXTRA_ARGS.to_string(), + ), ]) } @@ -125,14 +118,20 @@ mod tests { pub fn new() -> TempDir { let mut b = [0u8; 16]; let mut dirname = String::from("test-rapido-lib-"); - fs::File::open("/dev/urandom").unwrap().read_exact(&mut b).unwrap(); + fs::File::open("/dev/urandom") + .unwrap() + .read_exact(&mut b) + .unwrap(); for i in &b { dirname.push_str(&format!("{:02x}", i)); } fs::create_dir(&dirname).unwrap(); eprintln!("created tmp dir: {}", dirname); - TempDir { dir: PathBuf::from(&dirname), dirname } + TempDir { + dir: PathBuf::from(&dirname), + dirname, + } } } @@ -153,9 +152,10 @@ mod tests { #[test] fn test_conf_kmod_deps() { - let conf: HashMap = HashMap::from([ - ("QEMU_EXTRA_ARGS".to_string(), "-device virtio-rng-pci".to_string()) - ]); + let conf: HashMap = HashMap::from([( + "QEMU_EXTRA_ARGS".to_string(), + "-device virtio-rng-pci".to_string(), + )]); let kmods = conf_kmod_deps(&conf); assert!(kmods.contains(&"virtio_rng")); } From ec7f4c5762fa49961224f51c3186c97000a83724 Mon Sep 17 00:00:00 2001 From: Sangeetha Thackarajan Date: Mon, 25 May 2026 11:30:05 +0530 Subject: [PATCH 3/3] Add lint job with shellcheck, rustfmt, and clippy to CI Signed-off-by: Sangeetha Thackarajan --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0d522b9..139a5a78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,19 @@ on: # chmod /dev/kvm to allow non-root QEMU to use it # if building distro config install: libdw-dev libssl-dev jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install shellcheck + run: sudo apt-get update -y && sudo apt-get install -y shellcheck + - name: Run shellcheck + run: shellcheck --severity=error autorun/*.sh cut/*.sh selftest/*.sh rapido + - name: Run Rust format check and clippy lint + run: | + cargo --version + cargo fmt --check --all + cargo clippy --offline -- -A warnings -A clippy::byte_char_slices test: # aarch64 github runners don't provide kvm runs-on: ubuntu-latest