mod manifest;
pub mod pe;
pub use manifest::{Manifest, Package, UninstallOption};
pub const MAGIC: [u8; 8] = *b"EMBALASU";
pub const FORMAT_VERSION: u32 = 2;
pub const SECTION_COUNT: usize = 6;
pub const TRAILER_LEN: usize = 8 + 4 + SECTION_COUNT * 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Section {
pub offset: u64,
pub len: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Trailer {
pub version: u32,
pub stub: Section,
pub payload_zip: Section,
pub install_lua: Section,
pub uninstall_lua: Section,
pub manifest: Section,
pub signed_stub: Section,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrailerError {
BadMagic,
TooShort,
UnsupportedVersion(u32),
}
impl Trailer {
pub fn parse(bytes: &[u8]) -> Result<Trailer, TrailerError> {
if bytes.len() != TRAILER_LEN {
return Err(TrailerError::TooShort);
}
if bytes[0..8] != MAGIC {
return Err(TrailerError::BadMagic);
}
let version = read_u32(&bytes[8..12]);
if version != FORMAT_VERSION {
return Err(TrailerError::UnsupportedVersion(version));
}
let mut off = 12;
let mut section = || {
let s = Section {
offset: read_u64(&bytes[off..off + 8]),
len: read_u64(&bytes[off + 8..off + 16]),
};
off += 16;
s
};
Ok(Trailer {
version,
stub: section(),
payload_zip: section(),
install_lua: section(),
uninstall_lua: section(),
manifest: section(),
signed_stub: section(),
})
}
pub fn to_bytes(self) -> [u8; TRAILER_LEN] {
let mut out = [0u8; TRAILER_LEN];
out[0..8].copy_from_slice(&MAGIC);
out[8..12].copy_from_slice(&self.version.to_le_bytes());
let mut off = 12;
for s in [
self.stub,
self.payload_zip,
self.install_lua,
self.uninstall_lua,
self.manifest,
self.signed_stub,
] {
out[off..off + 8].copy_from_slice(&s.offset.to_le_bytes());
out[off + 8..off + 16].copy_from_slice(&s.len.to_le_bytes());
off += 16;
}
out
}
}
fn read_u32(b: &[u8]) -> u32 {
u32::from_le_bytes([b[0], b[1], b[2], b[3]])
}
fn read_u64(b: &[u8]) -> u64 {
u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
}
#[cfg(test)]
mod tests {
use super::*;
fn sample() -> Trailer {
Trailer {
version: FORMAT_VERSION,
stub: Section {
offset: 0,
len: 1024,
},
payload_zip: Section {
offset: 1024,
len: 42,
},
install_lua: Section {
offset: 1066,
len: 7,
},
uninstall_lua: Section {
offset: 1073,
len: 0,
},
manifest: Section {
offset: 1073,
len: 99,
},
signed_stub: Section {
offset: 1172,
len: 512,
},
}
}
#[test]
fn trailer_len_is_fixed() {
assert_eq!(TRAILER_LEN, 108);
assert_eq!(sample().to_bytes().len(), TRAILER_LEN);
}
#[test]
fn round_trips_every_section() {
let t = sample();
let parsed = Trailer::parse(&t.to_bytes()).expect("valid trailer");
assert_eq!(parsed, t);
}
#[test]
fn absent_magic_reports_no_overlay() {
let mut bytes = sample().to_bytes();
bytes[0] ^= 0xFF;
assert_eq!(Trailer::parse(&bytes), Err(TrailerError::BadMagic));
}
#[test]
fn wrong_length_is_rejected() {
assert_eq!(Trailer::parse(&[0u8; 8]), Err(TrailerError::TooShort));
}
#[test]
fn unknown_version_is_surfaced() {
let mut bytes = sample().to_bytes();
bytes[8..12].copy_from_slice(&99u32.to_le_bytes());
assert_eq!(
Trailer::parse(&bytes),
Err(TrailerError::UnsupportedVersion(99))
);
}
}