mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-10-19 08:03:09 +00:00
Properly support streamable input
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
use mem::MaybeUninit;
|
||||
use std::cmp::min;
|
||||
use std::ffi::CStr;
|
||||
use std::io;
|
||||
use std::io::{BufRead, Write};
|
||||
use std::io::{BufRead, Read, Seek, SeekFrom, Write};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||
use std::{io, mem};
|
||||
|
||||
use libc::{c_char, c_uint, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH};
|
||||
|
||||
@@ -135,6 +136,37 @@ pub extern "C" fn mkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReadExt {
|
||||
fn skip(&mut self, len: usize) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Read> ReadExt for T {
|
||||
fn skip(&mut self, mut len: usize) -> io::Result<()> {
|
||||
let mut buf = MaybeUninit::<[u8; 4096]>::uninit();
|
||||
let buf = unsafe { buf.assume_init_mut() };
|
||||
while len > 0 {
|
||||
let l = min(buf.len(), len);
|
||||
self.read_exact(&mut buf[..l])?;
|
||||
len -= l;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReadSeekExt {
|
||||
fn skip(&mut self, len: usize) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Read + Seek> ReadSeekExt for T {
|
||||
fn skip(&mut self, len: usize) -> io::Result<()> {
|
||||
if self.seek(SeekFrom::Current(len as i64)).is_err() {
|
||||
// If the file is not actually seekable, fallback to read
|
||||
ReadExt::skip(self, len)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BufReadExt {
|
||||
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, f: F);
|
||||
fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
|
||||
|
@@ -51,7 +51,7 @@ Supported actions:
|
||||
If [partition] is not specified, then attempt to extract either
|
||||
'init_boot' or 'boot'. Which partition was chosen can be determined
|
||||
by whichever 'init_boot.img' or 'boot.img' exists.
|
||||
<payload.bin>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
<payload.bin> can be '-' to be STDIN.
|
||||
|
||||
hexpatch <file> <hexpattern1> <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace it with <hexpattern2>
|
||||
|
@@ -7,7 +7,7 @@ use byteorder::{BigEndian, ReadBytesExt};
|
||||
use protobuf::{EnumFull, Message};
|
||||
|
||||
use base::libc::c_char;
|
||||
use base::{ptr_to_str_result, StrErr};
|
||||
use base::{ptr_to_str_result, ReadSeekExt, StrErr};
|
||||
use base::{ResultExt, WriteExt};
|
||||
|
||||
use crate::ffi;
|
||||
@@ -27,7 +27,7 @@ const PAYLOAD_MAGIC: &str = "CrAU";
|
||||
|
||||
fn do_extract_boot_from_payload(
|
||||
in_path: &str,
|
||||
partition: Option<&str>,
|
||||
partition_name: Option<&str>,
|
||||
out_path: Option<&str>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut reader = BufReader::new(if in_path == "-" {
|
||||
@@ -74,46 +74,50 @@ fn do_extract_boot_from_payload(
|
||||
|
||||
let block_size = manifest.block_size() as u64;
|
||||
|
||||
let part = match partition {
|
||||
let partition = match partition_name {
|
||||
None => {
|
||||
let boot = manifest
|
||||
.partitions
|
||||
.iter()
|
||||
.find(|partition| partition.partition_name() == "init_boot");
|
||||
.find(|p| p.partition_name() == "init_boot");
|
||||
let boot = match boot {
|
||||
Some(boot) => Some(boot),
|
||||
None => manifest
|
||||
.partitions
|
||||
.iter()
|
||||
.find(|partition| partition.partition_name() == "boot"),
|
||||
.find(|p| p.partition_name() == "boot"),
|
||||
};
|
||||
boot.ok_or(anyhow!("boot partition not found"))?
|
||||
}
|
||||
Some(partition) => manifest
|
||||
Some(name) => manifest
|
||||
.partitions
|
||||
.iter()
|
||||
.find(|p| p.partition_name() == partition)
|
||||
.ok_or(anyhow!("partition '{partition}' not found"))?,
|
||||
.find(|p| p.partition_name() == name)
|
||||
.ok_or(anyhow!("partition '{name}' not found"))?,
|
||||
};
|
||||
|
||||
let out_str: String;
|
||||
let out_path = match out_path {
|
||||
None => {
|
||||
out_str = format!("{}.img", part.partition_name());
|
||||
out_str = format!("{}.img", partition.partition_name());
|
||||
out_str.as_str()
|
||||
}
|
||||
Some(p) => p,
|
||||
Some(s) => s,
|
||||
};
|
||||
|
||||
let mut out_file = if out_path == "-" {
|
||||
unsafe { File::from_raw_fd(1) }
|
||||
} else {
|
||||
File::create(out_path).with_context(|| format!("cannot write to '{out_path}'"))?
|
||||
};
|
||||
let mut out_file =
|
||||
File::create(out_path).with_context(|| format!("cannot write to '{out_path}'"))?;
|
||||
|
||||
let base_offset = reader.stream_position()? + manifest_sig_len as u64;
|
||||
// Skip the manifest signature
|
||||
reader.skip(manifest_sig_len as usize)?;
|
||||
|
||||
for operation in part.operations.iter() {
|
||||
// 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;
|
||||
|
||||
for operation in operations.iter() {
|
||||
let data_len = operation
|
||||
.data_length
|
||||
.ok_or(bad_payload!("data length not found"))? as usize;
|
||||
@@ -131,8 +135,11 @@ fn do_extract_boot_from_payload(
|
||||
buf.resize(data_len, 0u8);
|
||||
let data = &mut buf[..data_len];
|
||||
|
||||
reader.seek(SeekFrom::Start(base_offset + data_offset))?;
|
||||
// Skip to the next offset and read data
|
||||
let skip = data_offset - curr_data_offset;
|
||||
reader.skip(skip as usize)?;
|
||||
reader.read_exact(data)?;
|
||||
curr_data_offset = data_offset + data_len as u64;
|
||||
|
||||
let out_offset = operation
|
||||
.dst_extents
|
||||
|
Reference in New Issue
Block a user