blob: 0edb650df9e34ab0982e259c1ee961402e9d793a [file] [log] [blame]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Append bootconfig to initrd image
use anyhow::Result;
use clap::Parser;
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
const FOOTER_ALIGNMENT: usize = 4;
const ZEROS: [u8; 4] = [0u8; 4_usize];
#[derive(Parser, Debug)]
struct Args {
/// Initrd (without bootconfig)
initrd: PathBuf,
/// Bootconfig
bootconfigs: Vec<PathBuf>,
/// Output
#[clap(long = "output")]
output: PathBuf,
}
fn get_checksum(file_path: &PathBuf) -> Result<u32> {
File::open(file_path)?.bytes().map(|x| Ok(x? as u32)).sum()
}
// Bootconfig is attached to the initrd in the following way:
// [initrd][bootconfig][padding][size(le32)][checksum(le32)][#BOOTCONFIG\n]
fn attach_bootconfig(initrd: PathBuf, bootconfigs: Vec<PathBuf>, output: PathBuf) -> Result<()> {
let mut output_file = File::create(&output)?;
let mut initrd_file = File::open(&initrd)?;
let initrd_size: usize = initrd_file.metadata()?.len().try_into()?;
let mut bootconfig_size: usize = 0;
let mut checksum: u32 = 0;
std::io::copy(&mut initrd_file, &mut output_file)?;
for bootconfig in bootconfigs {
let mut bootconfig_file = File::open(&bootconfig)?;
std::io::copy(&mut bootconfig_file, &mut output_file)?;
bootconfig_size += bootconfig_file.metadata()?.len() as usize;
checksum += get_checksum(&bootconfig)?;
}
let padding_size: usize = FOOTER_ALIGNMENT - (initrd_size + bootconfig_size) % FOOTER_ALIGNMENT;
output_file.write_all(&ZEROS[..padding_size])?;
output_file.write_all(&((padding_size + bootconfig_size) as u32).to_le_bytes())?;
output_file.write_all(&checksum.to_le_bytes())?;
output_file.write_all(b"#BOOTCONFIG\n")?;
output_file.flush()?;
Ok(())
}
fn try_main() -> Result<()> {
let args = Args::parse();
attach_bootconfig(args.initrd, args.bootconfigs, args.output)?;
Ok(())
}
fn main() {
try_main().unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_args() {
// Check that the command parsing has been configured in a valid way.
Args::command().debug_assert();
}
}