2023-05-05 01:49:33 +00:00
|
|
|
use std::fs::File;
|
2023-05-26 06:45:38 +00:00
|
|
|
use std::io::{BufReader, Read, Seek, SeekFrom, Write};
|
2022-12-20 13:17:55 +00:00
|
|
|
use std::os::fd::{AsRawFd, FromRawFd};
|
2023-05-05 01:49:33 +00:00
|
|
|
|
|
|
|
use byteorder::{BigEndian, ReadBytesExt};
|
2023-06-20 16:22:48 +00:00
|
|
|
use quick_protobuf::{BytesReader, MessageRead};
|
2023-05-05 01:49:33 +00:00
|
|
|
|
|
|
|
use base::libc::c_char;
|
2023-06-29 23:44:44 +00:00
|
|
|
use base::{error, LoggedError, LoggedResult, ReadSeekExt, StrErr, Utf8CStr};
|
2023-05-06 06:57:34 +00:00
|
|
|
use base::{ResultExt, WriteExt};
|
2023-05-05 01:49:33 +00:00
|
|
|
|
|
|
|
use crate::ffi;
|
2023-06-20 16:22:48 +00:00
|
|
|
use crate::proto::update_metadata::mod_InstallOperation::Type;
|
|
|
|
use crate::proto::update_metadata::DeltaArchiveManifest;
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
macro_rules! bad_payload {
|
2023-06-29 23:44:44 +00:00
|
|
|
($msg:literal) => {{
|
|
|
|
error!(concat!("Invalid payload: ", $msg));
|
|
|
|
LoggedError::default()
|
|
|
|
}};
|
|
|
|
($($args:tt)*) => {{
|
|
|
|
error!("Invalid payload: {}", format_args!($($args)*));
|
|
|
|
LoggedError::default()
|
|
|
|
}};
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
const PAYLOAD_MAGIC: &str = "CrAU";
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
fn do_extract_boot_from_payload(
|
2023-06-12 12:59:50 +00:00
|
|
|
in_path: &Utf8CStr,
|
|
|
|
partition_name: Option<&Utf8CStr>,
|
|
|
|
out_path: Option<&Utf8CStr>,
|
2023-06-29 23:44:44 +00:00
|
|
|
) -> LoggedResult<()> {
|
2022-12-20 13:17:55 +00:00
|
|
|
let mut reader = BufReader::new(if in_path == "-" {
|
|
|
|
unsafe { File::from_raw_fd(0) }
|
|
|
|
} else {
|
2023-06-29 23:44:44 +00:00
|
|
|
File::open(in_path).log_with_msg(|w| write!(w, "Cannot open '{}'", in_path))?
|
2022-12-20 13:17:55 +00:00
|
|
|
});
|
2023-05-05 01:49:33 +00:00
|
|
|
|
|
|
|
let buf = &mut [0u8; 4];
|
|
|
|
reader.read_exact(buf)?;
|
|
|
|
|
|
|
|
if buf != PAYLOAD_MAGIC.as_bytes() {
|
2023-05-26 06:45:38 +00:00
|
|
|
return Err(bad_payload!("invalid magic"));
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let version = reader.read_u64::<BigEndian>()?;
|
|
|
|
if version != 2 {
|
2023-05-26 06:45:38 +00:00
|
|
|
return Err(bad_payload!("unsupported version: {}", version));
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
let manifest_len = reader.read_u64::<BigEndian>()? as usize;
|
2023-05-05 01:49:33 +00:00
|
|
|
if manifest_len == 0 {
|
2023-05-26 06:45:38 +00:00
|
|
|
return Err(bad_payload!("manifest length is zero"));
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let manifest_sig_len = reader.read_u32::<BigEndian>()?;
|
|
|
|
if manifest_sig_len == 0 {
|
2023-05-26 06:45:38 +00:00
|
|
|
return Err(bad_payload!("manifest signature length is zero"));
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|
|
|
|
|
2023-09-07 04:45:12 +00:00
|
|
|
let mut buf = vec![0; manifest_len];
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
let manifest = {
|
|
|
|
let manifest = &mut buf[..manifest_len];
|
|
|
|
reader.read_exact(manifest)?;
|
2023-06-20 16:22:48 +00:00
|
|
|
let mut br = BytesReader::from_bytes(manifest);
|
|
|
|
DeltaArchiveManifest::from_reader(&mut br, manifest)?
|
2023-05-26 06:45:38 +00:00
|
|
|
};
|
2023-06-20 16:22:48 +00:00
|
|
|
if manifest.get_minor_version() != 0 {
|
2023-05-26 06:45:38 +00:00
|
|
|
return Err(bad_payload!(
|
2023-05-05 01:49:33 +00:00
|
|
|
"delta payloads are not supported, please use a full payload file"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2023-06-20 16:22:48 +00:00
|
|
|
let block_size = manifest.get_block_size() as u64;
|
2023-05-26 06:45:38 +00:00
|
|
|
|
2023-05-26 21:07:11 +00:00
|
|
|
let partition = match partition_name {
|
2023-05-26 06:45:38 +00:00
|
|
|
None => {
|
|
|
|
let boot = manifest
|
|
|
|
.partitions
|
|
|
|
.iter()
|
2023-06-20 16:22:48 +00:00
|
|
|
.find(|p| p.partition_name == "init_boot");
|
2023-05-26 06:45:38 +00:00
|
|
|
let boot = match boot {
|
|
|
|
Some(boot) => Some(boot),
|
|
|
|
None => manifest
|
|
|
|
.partitions
|
|
|
|
.iter()
|
2023-06-20 16:22:48 +00:00
|
|
|
.find(|p| p.partition_name == "boot"),
|
2023-05-26 06:45:38 +00:00
|
|
|
};
|
2023-06-29 23:44:44 +00:00
|
|
|
boot.ok_or_else(|| bad_payload!("boot partition not found"))?
|
2023-05-26 06:45:38 +00:00
|
|
|
}
|
2023-05-26 21:07:11 +00:00
|
|
|
Some(name) => manifest
|
2023-05-26 06:45:38 +00:00
|
|
|
.partitions
|
|
|
|
.iter()
|
2023-06-20 22:09:16 +00:00
|
|
|
.find(|p| p.partition_name.as_str() == name)
|
2023-06-29 23:44:44 +00:00
|
|
|
.ok_or_else(|| bad_payload!("partition '{}' not found", name))?,
|
2023-05-05 01:49:33 +00:00
|
|
|
};
|
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
let out_str: String;
|
|
|
|
let out_path = match out_path {
|
|
|
|
None => {
|
2023-06-20 16:22:48 +00:00
|
|
|
out_str = format!("{}.img", partition.partition_name);
|
2023-05-26 06:45:38 +00:00
|
|
|
out_str.as_str()
|
|
|
|
}
|
2023-05-26 21:07:11 +00:00
|
|
|
Some(s) => s,
|
2023-05-26 06:45:38 +00:00
|
|
|
};
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-05-26 21:07:11 +00:00
|
|
|
let mut out_file =
|
2023-06-29 23:44:44 +00:00
|
|
|
File::create(out_path).log_with_msg(|w| write!(w, "Cannot write to '{}'", out_path))?;
|
2023-05-26 21:07:11 +00:00
|
|
|
|
|
|
|
// Skip the manifest signature
|
|
|
|
reader.skip(manifest_sig_len as usize)?;
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-05-26 21:07:11 +00:00
|
|
|
// Sort the install operations with data_offset so we will only ever need to seek forward
|
|
|
|
// This makes it possible to support non-seekable input file descriptors
|
|
|
|
let mut operations = partition.operations.clone();
|
|
|
|
operations.sort_by_key(|e| e.data_offset.unwrap_or(0));
|
|
|
|
let mut curr_data_offset: u64 = 0;
|
2023-05-26 06:45:38 +00:00
|
|
|
|
2023-05-26 21:07:11 +00:00
|
|
|
for operation in operations.iter() {
|
2023-05-05 01:49:33 +00:00
|
|
|
let data_len = operation
|
|
|
|
.data_length
|
2023-06-29 23:44:44 +00:00
|
|
|
.ok_or_else(|| bad_payload!("data length not found"))? as usize;
|
2023-05-05 01:49:33 +00:00
|
|
|
|
|
|
|
let data_offset = operation
|
|
|
|
.data_offset
|
2023-06-29 23:44:44 +00:00
|
|
|
.ok_or_else(|| bad_payload!("data offset not found"))?;
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-06-20 16:22:48 +00:00
|
|
|
let data_type = operation.type_pb;
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
buf.resize(data_len, 0u8);
|
|
|
|
let data = &mut buf[..data_len];
|
2023-05-05 01:49:33 +00:00
|
|
|
|
2023-05-26 21:07:11 +00:00
|
|
|
// Skip to the next offset and read data
|
|
|
|
let skip = data_offset - curr_data_offset;
|
|
|
|
reader.skip(skip as usize)?;
|
2023-05-26 06:45:38 +00:00
|
|
|
reader.read_exact(data)?;
|
2023-05-26 21:07:11 +00:00
|
|
|
curr_data_offset = data_offset + data_len as u64;
|
2023-05-05 01:49:33 +00:00
|
|
|
|
|
|
|
let out_offset = operation
|
|
|
|
.dst_extents
|
2024-02-28 05:03:34 +00:00
|
|
|
.first()
|
2023-06-29 23:44:44 +00:00
|
|
|
.ok_or_else(|| bad_payload!("dst extents not found"))?
|
2023-05-05 01:49:33 +00:00
|
|
|
.start_block
|
2023-06-29 23:44:44 +00:00
|
|
|
.ok_or_else(|| bad_payload!("start block not found"))?
|
2023-05-05 01:49:33 +00:00
|
|
|
* block_size;
|
|
|
|
|
|
|
|
match data_type {
|
|
|
|
Type::REPLACE => {
|
|
|
|
out_file.seek(SeekFrom::Start(out_offset))?;
|
2023-06-09 13:43:26 +00:00
|
|
|
out_file.write_all(data)?;
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|
|
|
|
Type::ZERO => {
|
|
|
|
for ext in operation.dst_extents.iter() {
|
2023-05-26 06:45:38 +00:00
|
|
|
let out_seek = ext
|
|
|
|
.start_block
|
2023-07-06 17:01:11 +00:00
|
|
|
.ok_or_else(|| bad_payload!("start block not found"))?
|
2023-05-26 06:45:38 +00:00
|
|
|
* block_size;
|
2023-07-06 17:01:11 +00:00
|
|
|
let num_blocks = ext
|
|
|
|
.num_blocks
|
|
|
|
.ok_or_else(|| bad_payload!("num blocks not found"))?;
|
2023-05-05 01:49:33 +00:00
|
|
|
out_file.seek(SeekFrom::Start(out_seek))?;
|
|
|
|
out_file.write_zeros(num_blocks as usize)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Type::REPLACE_BZ | Type::REPLACE_XZ => {
|
|
|
|
out_file.seek(SeekFrom::Start(out_offset))?;
|
2023-05-26 06:45:38 +00:00
|
|
|
if !ffi::decompress(data, out_file.as_raw_fd()) {
|
|
|
|
return Err(bad_payload!("decompression failed"));
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|
2023-09-07 04:45:12 +00:00
|
|
|
}
|
2023-06-20 16:22:48 +00:00
|
|
|
_ => return Err(bad_payload!("unsupported operation type")),
|
2023-05-05 01:49:33 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-05-26 06:45:38 +00:00
|
|
|
pub fn extract_boot_from_payload(
|
|
|
|
in_path: *const c_char,
|
|
|
|
partition: *const c_char,
|
|
|
|
out_path: *const c_char,
|
|
|
|
) -> bool {
|
|
|
|
fn inner(
|
|
|
|
in_path: *const c_char,
|
|
|
|
partition: *const c_char,
|
|
|
|
out_path: *const c_char,
|
2023-06-29 23:44:44 +00:00
|
|
|
) -> LoggedResult<()> {
|
2023-06-12 12:59:50 +00:00
|
|
|
let in_path = unsafe { Utf8CStr::from_ptr(in_path) }?;
|
|
|
|
let partition = match unsafe { Utf8CStr::from_ptr(partition) } {
|
2023-05-26 06:45:38 +00:00
|
|
|
Ok(s) => Some(s),
|
2023-06-12 08:07:43 +00:00
|
|
|
Err(StrErr::NullPointerError) => None,
|
2023-05-26 06:45:38 +00:00
|
|
|
Err(e) => Err(e)?,
|
|
|
|
};
|
2023-06-12 12:59:50 +00:00
|
|
|
let out_path = match unsafe { Utf8CStr::from_ptr(out_path) } {
|
2023-05-26 06:45:38 +00:00
|
|
|
Ok(s) => Some(s),
|
2023-06-12 08:07:43 +00:00
|
|
|
Err(StrErr::NullPointerError) => None,
|
2023-05-26 06:45:38 +00:00
|
|
|
Err(e) => Err(e)?,
|
|
|
|
};
|
|
|
|
do_extract_boot_from_payload(in_path, partition, out_path)
|
2023-06-29 23:44:44 +00:00
|
|
|
.log_with_msg(|w| w.write_str("Failed to extract from payload"))
|
2023-05-26 06:45:38 +00:00
|
|
|
}
|
2023-06-29 23:44:44 +00:00
|
|
|
inner(in_path, partition, out_path).is_ok()
|
2023-05-05 01:49:33 +00:00
|
|
|
}
|