use Section;
use byteorder::{LittleEndian, ReadBytesExt};
use quick_error::ResultExt;
use std::io::{Error as IoError, Read, Seek, SeekFrom};
pub const MAGIC_STR: &'static str = "ANDROID!";
const MAGIC_SIZE: usize = 8;
const PRODUCT_NAME_SIZE: usize = 24;
const BOOT_ARGUMENTS_SIZE: usize = 512;
const UNIQUE_ID_SIZE: usize = 32;
const HEADER_SIZE: usize = 616;
const SECTIONS: &'static [Section] = &[
Section::Header,
Section::Kernel,
Section::Ramdisk,
Section::Second,
Section::DeviceTree,
];
#[derive(Debug)]
pub struct MagicHeader {
pub magic: [u8; MAGIC_SIZE],
pub kernel_size: u32,
pub kernel_load_address: u32,
pub ramdisk_size: u32,
pub ramdisk_load_address: u32,
pub second_size: u32,
pub second_load_address: u32,
pub device_tree_size: u32,
_reserved: u32,
pub kernel_tags_address: u32,
pub page_size: u32,
pub product_name: [u8; PRODUCT_NAME_SIZE],
pub boot_arguments: [[u8; BOOT_ARGUMENTS_SIZE / 16]; 16],
pub unique_id: [u8; UNIQUE_ID_SIZE],
}
impl MagicHeader {
pub fn read_from<R: ReadBytesExt>(
source: &mut R,
check_magic: bool,
) -> Result<Self, MagicHeaderParseError> {
let header = MagicHeader {
magic: {
let mut buffer = [0; MAGIC_SIZE];
source.read_exact(&mut buffer)?;
buffer
},
kernel_size: source.read_u32::<LittleEndian>()?,
kernel_load_address: source.read_u32::<LittleEndian>()?,
ramdisk_size: source.read_u32::<LittleEndian>()?,
ramdisk_load_address: source.read_u32::<LittleEndian>()?,
second_size: source.read_u32::<LittleEndian>()?,
second_load_address: source.read_u32::<LittleEndian>()?,
device_tree_size: source.read_u32::<LittleEndian>()?,
_reserved: source.read_u32::<LittleEndian>()?,
kernel_tags_address: source.read_u32::<LittleEndian>()?,
page_size: source.read_u32::<LittleEndian>()?,
product_name: {
let mut buffer = [0; PRODUCT_NAME_SIZE];
source.read_exact(&mut buffer)?;
buffer
},
boot_arguments: unsafe {
use std::mem::transmute;
let mut buffer = [0; BOOT_ARGUMENTS_SIZE];
source.read_exact(&mut buffer)?;
transmute(buffer)
},
unique_id: {
let mut buffer = [0u8; UNIQUE_ID_SIZE];
source.read_exact(&mut buffer)?;
buffer
},
};
if check_magic && header.magic != MAGIC_STR.as_bytes() {
Err(MagicHeaderParseError::InvalidMagic(header))
} else {
Ok(header)
}
}
pub fn section_size(&self, section: Section) -> u64 {
match section {
Section::Header => HEADER_SIZE as u64,
Section::Kernel => self.kernel_size as u64,
Section::Ramdisk => self.ramdisk_size as u64,
Section::Second => self.second_size as u64,
Section::DeviceTree => self.device_tree_size as u64,
}
}
pub fn section_start(&self, section: Section) -> Result<u64, LocateSectionError> {
if self.page_size == 0 {
Err(LocateSectionError::NoPageSize)
} else {
let offset_in_pages: u64 = SECTIONS
.iter()
.cloned()
.take_while(|&i_section| i_section != section)
.map(|section| {
(self.section_size(section) + self.page_size as u64 - 1) / self.page_size as u64
})
.sum();
Ok(offset_in_pages * self.page_size as u64)
}
}
pub fn section_location(&self, section: Section) -> Result<(u64, u64), LocateSectionError> {
Ok((self.section_start(section)?, self.section_size(section)))
}
pub fn read_section_from<R: Read + Seek>(
&self,
source: &mut R,
section: Section,
) -> Result<Vec<u8>, ReadSectionError> {
let (start, size) = self.section_location(section).context(section)?;
try!(source.seek(SeekFrom::Start(start)).context(section));
let mut data = vec![0u8; size as usize];
try!(source.read_exact(&mut data).context(section));
return Ok(data);
}
pub fn sections(&self) -> Vec<Section> {
SECTIONS
.iter()
.filter(|&§ion| self.section_size(section) > 0)
.cloned()
.collect()
}
}
quick_error! {
#[derive(Debug)]
pub enum MagicHeaderParseError {
Io(error: IoError) {
description("I/O error while parsing header")
display("I/O error while parsing header.")
cause(error)
from(error: IoError) -> (error)
}
InvalidMagic(header: MagicHeader) {
description("The header did not have the valid magic prefix")
display("The header did not have the valid '{}' magic prefix.", MAGIC_STR)
}
}
}
quick_error! {
#[derive(Debug)]
pub enum LocateSectionError {
NoPageSize {
description("The header's page size is zero")
display("The header's page size is 0.")
}
NoSection(section: Section) {
description("The section does not exist")
display("The '{}' section does not exist.", section)
}
}
}
quick_error! {
#[derive(Debug)]
pub enum ReadSectionError {
LocateSection(section: Section, cause: LocateSectionError) {
context(section: Section, cause: LocateSectionError) -> (section, cause)
description("Could not locate the section")
display("Cannot locate the '{}' section.", section)
cause(cause)
}
IoError(section: Section, cause: IoError) {
context(section: Section, cause: IoError) -> (section, cause)
description("I/O error while reading the section")
display("I/O error while reading the '{}' section.", section)
cause(cause)
}
}
}