From db8d83270793043aca085b2eaf4b02e4fee37e1c Mon Sep 17 00:00:00 2001 From: LoveSy Date: Tue, 19 Aug 2025 22:46:09 +0800 Subject: [PATCH] Move magiskboot cli to argh --- native/src/Android.mk | 1 - native/src/boot/bootimg.cpp | 32 +-- native/src/boot/cli.rs | 430 +++++++++++++++++++++++++++++++++ native/src/boot/compress.rs | 122 +++++++++- native/src/boot/cpio.rs | 132 +++++----- native/src/boot/dtb.rs | 62 ++--- native/src/boot/format.cpp | 39 --- native/src/boot/format.hpp | 7 - native/src/boot/format.rs | 85 +++++++ native/src/boot/lib.rs | 36 +-- native/src/boot/magiskboot.hpp | 23 +- native/src/boot/main.cpp | 305 ----------------------- native/src/boot/patch.rs | 8 +- native/src/boot/payload.rs | 34 +-- 14 files changed, 762 insertions(+), 554 deletions(-) create mode 100644 native/src/boot/cli.rs create mode 100644 native/src/boot/format.rs delete mode 100644 native/src/boot/main.cpp diff --git a/native/src/Android.mk b/native/src/Android.mk index b16b275e9..6e0e00ea1 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -86,7 +86,6 @@ LOCAL_STATIC_LIBRARIES := \ libboot-rs LOCAL_SRC_FILES := \ - boot/main.cpp \ boot/bootimg.cpp \ boot/format.cpp \ boot/boot-rs.cpp diff --git a/native/src/boot/bootimg.cpp b/native/src/boot/bootimg.cpp index c25d75182..cdf5ebe9c 100644 --- a/native/src/boot/bootimg.cpp +++ b/native/src/boot/bootimg.cpp @@ -551,8 +551,8 @@ bool boot_img::verify(const char *cert) const { return rust::verify_boot_image(*this, cert); } -int split_image_dtb(const char *filename, bool skip_decomp) { - mmap_data img(filename); +int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) { + mmap_data img(filename.data()); if (size_t off = find_dtb_offset(img.buf(), img.sz()); off > 0) { FileFormat fmt = check_fmt_lg(img.buf(), img.sz()); @@ -566,13 +566,13 @@ int split_image_dtb(const char *filename, bool skip_decomp) { dump(img.buf() + off, img.sz() - off, KER_DTB_FILE); return 0; } else { - fprintf(stderr, "Cannot find DTB in %s\n", filename); + fprintf(stderr, "Cannot find DTB in %s\n", filename.data()); return 1; } } -int unpack(const char *image, bool skip_decomp, bool hdr) { - const boot_img boot(image); +int unpack(rust::Utf8CStr image, bool skip_decomp, bool hdr) { + const boot_img boot(image.data()); if (hdr) boot.hdr->dump_hdr_file(); @@ -656,9 +656,9 @@ write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR) - off.header, page_size)) #define file_align() file_align_with(boot.hdr->page_size()) -void repack(const char *src_img, const char *out_img, bool skip_comp) { - const boot_img boot(src_img); - fprintf(stderr, "Repack to boot image: [%s]\n", out_img); +void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) { + const boot_img boot(src_img.data()); + fprintf(stderr, "Repack to boot image: [%s]\n", out_img.data()); struct { uint32_t header; @@ -687,7 +687,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) { ***************/ // Create new image - int fd = creat(out_img, 0644); + int fd = creat(out_img.data(), 0644); if (boot.flags[DHTB_FLAG]) { // Skip DHTB header @@ -899,7 +899,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) { ******************/ // Map output image as rw - mmap_data out(out_img, true); + mmap_data out(out_img.data(), true); // MTK headers if (boot.flags[MTK_KERNEL]) { @@ -997,8 +997,8 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) { close(fd); } -int verify(const char *image, const char *cert) { - const boot_img boot(image); +int verify(rust::Utf8CStr image, const char *cert) { + const boot_img boot(image.data()); if (cert == nullptr) { // Boot image parsing already checks if the image is signed return boot.flags[AVB1_SIGNED_FLAG] ? 0 : 1; @@ -1008,14 +1008,14 @@ int verify(const char *image, const char *cert) { } } -int sign(const char *image, const char *name, const char *cert, const char *key) { - const boot_img boot(image); - auto sig = rust::sign_boot_image(boot.payload, name, cert, key); +int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key) { + const boot_img boot(image.data()); + auto sig = rust::sign_boot_image(boot.payload, name.data(), cert, key); if (sig.empty()) return 1; auto eof = boot.tail.buf() - boot.map.buf(); - int fd = xopen(image, O_WRONLY | O_CLOEXEC); + int fd = xopen(image.data(), O_WRONLY | O_CLOEXEC); if (lseek(fd, eof, SEEK_SET) != eof || xwrite(fd, sig.data(), sig.size()) != sig.size()) { close(fd); return 1; diff --git a/native/src/boot/cli.rs b/native/src/boot/cli.rs new file mode 100644 index 000000000..fb58c0663 --- /dev/null +++ b/native/src/boot/cli.rs @@ -0,0 +1,430 @@ +use crate::compress::{compress, decompress}; +use crate::cpio::{cpio_commands, print_cpio_usage}; +use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage}; +use crate::ffi::{FileFormat, cleanup, repack, sign, split_image_dtb, unpack, verify}; +use crate::patch::hexpatch; +use crate::payload::extract_boot_from_payload; +use crate::sign::sha1_hash; +use argh::FromArgs; +use base::{ + EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, cmdline_logging, cstr, + libc::umask, log_err, map_args, +}; +use std::ffi::c_char; +use std::str::FromStr; + +#[derive(FromArgs)] +struct Cli { + #[argh(subcommand)] + action: Action, +} + +#[derive(FromArgs)] +#[argh(subcommand)] +enum Action { + Unpack(Unpack), + Repack(Repack), + Verify(Verify), + Sign(Sign), + Extract(Extract), + HexPatch(HexPatch), + Cpio(Cpio), + Dtb(Dtb), + Split(Split), + Sha1(Sha1), + Cleanup(Cleanup), + Compress(Compress), + Decompress(Decompress), +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "unpack")] +struct Unpack { + #[argh(switch, short = 'n')] + no_decompress: bool, + #[argh(switch, short = 'h')] + dump_header: bool, + #[argh(positional)] + img: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "repack")] +struct Repack { + #[argh(switch, short = 'n')] + no_compress: bool, + #[argh(positional)] + img: String, + #[argh(positional, default = r#""new-boot.img".to_string()"#)] + out: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "verify")] +struct Verify { + #[argh(positional)] + img: String, + #[argh(positional)] + cert: Option, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "sign")] +struct Sign { + #[argh(positional)] + img: String, + #[argh(positional)] + args: Vec, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "extract")] +struct Extract { + #[argh(positional)] + payload: String, + #[argh(positional)] + args: Vec, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "hexpatch")] +struct HexPatch { + #[argh(positional)] + file: String, + #[argh(positional)] + src: String, + #[argh(positional)] + dest: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "cpio")] +struct Cpio { + #[argh(positional)] + file: String, + #[argh(positional)] + cmds: Vec, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "dtb")] +struct Dtb { + #[argh(positional)] + file: String, + #[argh(subcommand)] + action: DtbAction, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "split")] +struct Split { + #[argh(switch, short = 'n')] + no_decompress: bool, + #[argh(positional)] + file: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "sha1")] +struct Sha1 { + #[argh(positional)] + file: String, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "cleanup")] +struct Cleanup {} + +#[derive(FromArgs)] +#[argh(subcommand, name = "compress")] +struct Compress { + #[argh(option, short = 'f', default = r#""gzip".to_string()"#)] + format: String, + #[argh(positional)] + file: String, + #[argh(positional)] + out: Option, +} + +#[derive(FromArgs)] +#[argh(subcommand, name = "decompress")] +struct Decompress { + #[argh(positional)] + file: String, + #[argh(positional)] + out: Option, +} + +fn print_usage(cmd: &str) { + eprintln!( + r#"MagiskBoot - Boot Image Modification Tool + +Usage: {} [args...] + +Supported actions: + unpack [-n] [-h] + Unpack to its individual components, each component to + a file with its corresponding file name in the current directory. + Supported components: kernel, kernel_dtb, ramdisk.cpio, second, + dtb, extra, and recovery_dtbo. + By default, each component will be decompressed on-the-fly. + If '-n' is provided, all decompression operations will be skipped; + each component will remain untouched, dumped in its original format. + If '-h' is provided, the boot image header information will be + dumped to the file 'header', which can be used to modify header + configurations during repacking. + Return values: + 0:valid 1:error 2:chromeos + + repack [-n] [outbootimg] + Repack boot image components using files from the current directory + to [outbootimg], or 'new-boot.img' if not specified. Current directory + should only contain required files for [outbootimg], or incorrect + [outbootimg] may be produced. + is the original boot image used to unpack the components. + By default, each component will be automatically compressed using its + corresponding format detected in . If a component file + in the current directory is already compressed, then no addition + compression will be performed for that specific component. + If '-n' is provided, all compression operations will be skipped. + If env variable PATCHVBMETAFLAG is set to true, all disable flags in + the boot image's vbmeta header will be set. + + verify [x509.pem] + Check whether the boot image is signed with AVB 1.0 signature. + Optionally provide a certificate to verify whether the image is + signed by the public key certificate. + Return value: + 0:valid 1:error + + sign [name] [x509.pem pk8] + Sign with AVB 1.0 signature. + Optionally provide the name of the image (default: '/boot'). + Optionally provide the certificate/private key pair for signing. + If the certificate/private key pair is not provided, the AOSP + verity key bundled in the executable will be used. + + extract [partition] [outfile] + Extract [partition] from to [outfile]. + If [outfile] is not specified, then output to '[partition].img'. + 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. + can be '-' to be STDIN. + + hexpatch + Search in , and replace it with + + cpio [commands...] + Do cpio commands to (modifications are done in-place). + Each command is a single argument; add quotes for each command. + See "cpio --help" for supported commands. + + dtb [args...] + Do dtb related actions to . + See "dtb --help" for supported actions. + + split [-n] + Split image.*-dtb into kernel + kernel_dtb. + If '-n' is provided, decompression operations will be skipped; + the kernel will remain untouched, split in its original format. + + sha1 + Print the SHA1 checksum for + + cleanup + Cleanup the current working directory + + compress[=format] [outfile] + Compress with [format] to [outfile]. + /[outfile] can be '-' to be STDIN/STDOUT. + If [format] is not specified, then gzip will be used. + If [outfile] is not specified, then will be replaced + with another file suffixed with a matching file extension. + Supported formats: + + {1} + + decompress [outfile] + Detect format and decompress to [outfile]. + /[outfile] can be '-' to be STDIN/STDOUT. + If [outfile] is not specified, then will be replaced + with another file removing its archive format file extension. + Supported formats: + + {1} +"#, + cmd, + FileFormat::formats() + ); +} + +#[unsafe(no_mangle)] +pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 { + cmdline_logging(); + unsafe { umask(0) }; + let res: LoggedResult<()> = try { + let mut cmds = map_args(argc, argv)?; + if argc < 2 { + print_usage(cmds.first().unwrap_or(&"magiskboot")); + return 1; + } + + if cmds[1].starts_with("--") { + cmds[1] = &cmds[1][2..]; + } + + if let Some(fmt) = str::strip_prefix(cmds[1], "compress=") { + cmds.insert(1, "compress"); + cmds.insert(2, "-f"); + cmds[3] = fmt; + } + + let mut cli = Cli::from_args(&[cmds[0]], &cmds[1..]).on_early_exit(|| match cmds.get(1) { + Some(&"dtb") => print_dtb_usage(), + Some(&"cpio") => print_cpio_usage(), + _ => print_usage(cmds[0]), + }); + match cli.action { + Action::Unpack(Unpack { + no_decompress, + dump_header, + ref mut img, + }) => return unpack(Utf8CStr::from_string(img), no_decompress, dump_header), + Action::Repack(Repack { + no_compress, + ref mut img, + ref mut out, + }) => { + repack( + Utf8CStr::from_string(img), + Utf8CStr::from_string(out), + no_compress, + ); + } + Action::Verify(Verify { + ref mut img, + ref mut cert, + }) => { + return unsafe { + verify( + Utf8CStr::from_string(img), + cert.as_mut() + .map(|x| Utf8CStr::from_string(x).as_ptr()) + .unwrap_or(std::ptr::null()), + ) + }; + } + Action::Sign(Sign { + ref mut img, + ref mut args, + }) => { + let (pem, pk8) = match args.get_mut(1..=2) { + Some([pem, pk8]) => ( + Utf8CStr::from_string(pem).as_ptr(), + Utf8CStr::from_string(pk8).as_ptr(), + ), + _ => (std::ptr::null(), std::ptr::null()), + }; + return unsafe { + sign( + Utf8CStr::from_string(img), + args.first_mut() + .map(Utf8CStr::from_string) + .unwrap_or(cstr!("/boot")), + pem, + pk8, + ) + }; + } + Action::Extract(Extract { + ref payload, + ref args, + }) => { + if args.len() > 2 { + Err(log_err!("Too many arguments"))?; + } + extract_boot_from_payload( + payload, + args.first().map(|x| x.as_str()), + args.get(1).map(|x| x.as_str()), + ) + .log_with_msg(|w| w.write_str("Failed to extract from payload"))?; + } + Action::HexPatch(HexPatch { + ref mut file, + ref mut src, + ref mut dest, + }) => { + if !hexpatch( + file, + Utf8CStr::from_string(src), + Utf8CStr::from_string(dest), + ) { + Err(log_err!("Failed to patch"))?; + } + } + Action::Cpio(Cpio { + ref mut file, + ref mut cmds, + }) => { + return if cpio_commands(file, cmds) + .log_with_msg(|w| w.write_str("Failed to process cpio"))? + { + 0 + } else { + 1 + }; + } + Action::Dtb(Dtb { + ref mut file, + ref action, + }) => { + return if dtb_commands(file, action) + .log_with_msg(|w| w.write_str("Failed to process dtb"))? + { + 0 + } else { + 1 + }; + } + Action::Split(Split { + no_decompress, + ref mut file, + }) => { + return split_image_dtb(Utf8CStr::from_string(file), no_decompress); + } + Action::Sha1(Sha1 { ref mut file }) => { + let file = MappedFile::open(Utf8CStr::from_string(file))?; + let mut sha1 = [0u8; 20]; + sha1_hash(file.as_ref(), &mut sha1); + for byte in &sha1 { + print!("{byte:02x}"); + } + println!(); + } + Action::Cleanup(_) => { + eprintln!("Cleaning up..."); + cleanup(); + } + Action::Decompress(Decompress { + ref mut file, + ref mut out, + }) => { + decompress(file, out.as_mut())?; + } + Action::Compress(Compress { + ref mut file, + ref format, + ref mut out, + }) => { + compress( + FileFormat::from_str(format).unwrap_or(FileFormat::UNKNOWN), + file, + out.as_mut(), + )?; + } + } + }; + if res.is_ok() { 0 } else { 1 } +} diff --git a/native/src/boot/compress.rs b/native/src/boot/compress.rs index 597154c04..6699a0cf4 100644 --- a/native/src/boot/compress.rs +++ b/native/src/boot/compress.rs @@ -1,5 +1,6 @@ -use crate::ffi::FileFormat; -use base::{Chunker, LoggedResult, WriteExt}; +use crate::ffi::{FileFormat, check_fmt}; +use base::libc::{O_RDONLY, O_TRUNC, O_WRONLY}; +use base::{Chunker, LoggedResult, Utf8CStr, WriteExt, error, log_err}; use bytemuck::bytes_of_mut; use bzip2::{Compression as BzCompression, write::BzDecoder, write::BzEncoder}; use flate2::{Compression as GzCompression, write::GzEncoder, write::MultiGzDecoder}; @@ -9,11 +10,11 @@ use lz4::{ }; use std::cell::Cell; use std::fs::File; -use std::io::{BufWriter, Read, Write}; +use std::io::{BufWriter, Read, Stdin, Stdout, Write, stdin, stdout}; use std::mem::ManuallyDrop; use std::num::NonZeroU64; use std::ops::DerefMut; -use std::os::fd::{FromRawFd, RawFd}; +use std::os::fd::{AsRawFd, FromRawFd, RawFd}; use xz2::{ stream::{Check as LzmaCheck, Filters as LzmaFilters, LzmaOptions, Stream as LzmaStream}, write::{XzDecoder, XzEncoder}, @@ -424,3 +425,116 @@ pub fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) { decoder.finish()?; }; } + +enum AsRawFdFile { + Stdin(Stdin), + Stdout(Stdout), + File(File), +} + +impl AsRawFd for AsRawFdFile { + fn as_raw_fd(&self) -> RawFd { + match self { + AsRawFdFile::Stdin(stdin) => stdin.as_raw_fd(), + AsRawFdFile::Stdout(stdout) => stdout.as_raw_fd(), + AsRawFdFile::File(file) => file.as_raw_fd(), + } + } +} + +pub(crate) fn decompress(infile: &mut String, outfile: Option<&mut String>) -> LoggedResult<()> { + let in_std = infile == "-"; + let mut rm_in = false; + + let raw_in = if in_std { + AsRawFdFile::Stdin(stdin()) + } else { + AsRawFdFile::File(Utf8CStr::from_string(infile).open(O_RDONLY)?) + }; + + let mut buf = [0u8; 4096]; + + let mut in_file = unsafe { File::from_raw_fd(raw_in.as_raw_fd()) }; + let _ = in_file.read(&mut buf)?; + + let format = check_fmt(&buf); + + eprintln!("Detected format: {format}"); + + if !format.is_compressed() { + return Err(log_err!("Input file is not a supported type!")); + } + + let raw_out = if let Some(outfile) = outfile { + if outfile == "-" { + AsRawFdFile::Stdout(stdout()) + } else { + AsRawFdFile::File(Utf8CStr::from_string(outfile).create(O_WRONLY | O_TRUNC, 0o644)?) + } + } else if in_std { + AsRawFdFile::Stdout(stdout()) + } else { + // strip the extension + rm_in = true; + let mut outfile = if let Some((outfile, ext)) = infile.rsplit_once('.') { + if ext != format.ext() { + Err(log_err!("Input file is not a supported type!"))?; + } + outfile.to_owned() + } else { + infile.clone() + }; + eprintln!("Decompressing to [{outfile}]"); + + AsRawFdFile::File(Utf8CStr::from_string(&mut outfile).create(O_WRONLY | O_TRUNC, 0o644)?) + }; + + decompress_bytes_fd(format, &buf, raw_in.as_raw_fd(), raw_out.as_raw_fd()); + + if rm_in { + Utf8CStr::from_string(infile).remove()?; + } + + Ok(()) +} + +pub(crate) fn compress( + method: FileFormat, + infile: &mut String, + outfile: Option<&mut String>, +) -> LoggedResult<()> { + if method == FileFormat::UNKNOWN { + error!("Unsupported compression format"); + } + + let in_std = infile == "-"; + let mut rm_in = false; + + let raw_in = if in_std { + AsRawFdFile::Stdin(stdin()) + } else { + AsRawFdFile::File(Utf8CStr::from_string(infile).open(O_RDONLY)?) + }; + + let raw_out = if let Some(outfile) = outfile { + if outfile == "-" { + AsRawFdFile::Stdout(stdout()) + } else { + AsRawFdFile::File(Utf8CStr::from_string(outfile).create(O_WRONLY | O_TRUNC, 0o644)?) + } + } else if in_std { + AsRawFdFile::Stdout(stdout()) + } else { + let mut outfile = format!("{infile}.{}", method.ext()); + eprintln!("Compressing to [{outfile}]"); + rm_in = true; + AsRawFdFile::File(Utf8CStr::from_string(&mut outfile).create(O_WRONLY | O_TRUNC, 0o644)?) + }; + + compress_fd(method, raw_in.as_raw_fd(), raw_out.as_raw_fd()); + + if rm_in { + Utf8CStr::from_string(infile).remove()?; + } + Ok(()) +} diff --git a/native/src/boot/cpio.rs b/native/src/boot/cpio.rs index bfca2701a..d84c7d07c 100644 --- a/native/src/boot/cpio.rs +++ b/native/src/boot/cpio.rs @@ -17,11 +17,11 @@ use size::{Base, Size, Style}; use base::libc::{ O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR, - c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, + dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, }; use base::{ BytesExt, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt, - cstr, log_err, map_args, + cstr, log_err, }; use crate::check_env; @@ -29,14 +29,6 @@ use crate::compress::{get_decoder, get_encoder}; use crate::ffi::FileFormat; use crate::patch::{patch_encryption, patch_verity}; -#[derive(FromArgs)] -struct CpioCli { - #[argh(positional)] - file: String, - #[argh(positional)] - commands: Vec, -} - #[derive(FromArgs)] struct CpioCommand { #[argh(subcommand)] @@ -151,7 +143,7 @@ struct List { recursive: bool, } -fn print_cpio_usage() { +pub(crate) fn print_cpio_usage() { eprintln!( r#"Usage: magiskboot cpio [commands...] @@ -761,74 +753,58 @@ impl Display for CpioEntry { } } -pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool { - let res: LoggedResult<()> = try { - if argc < 1 { - Err(log_err!("No arguments"))?; - } - - let cmds = map_args(argc, argv)?; - - let mut cli = - CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage); - - let file = Utf8CStr::from_string(&mut cli.file); - let mut cpio = if file.exists() { - Cpio::load_from_file(file)? - } else { - Cpio::new() - }; - - for cmd in cli.commands { - if cmd.starts_with('#') { - continue; - } - let mut cli = CpioCommand::from_args( - &["magiskboot", "cpio", file], - cmd.split(' ') - .filter(|x| !x.is_empty()) - .collect::>() - .as_slice(), - ) - .on_early_exit(print_cpio_usage); - - match &mut cli.action { - CpioAction::Test(_) => exit(cpio.test()), - CpioAction::Restore(_) => cpio.restore()?, - CpioAction::Patch(_) => cpio.patch(), - CpioAction::Exists(Exists { path }) => { - if cpio.exists(path) { - exit(0); - } else { - exit(1); - } - } - CpioAction::Backup(Backup { - origin, - skip_compress, - }) => cpio.backup(origin, *skip_compress)?, - CpioAction::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive), - CpioAction::Move(Move { from, to }) => cpio.mv(from, to)?, - CpioAction::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(*mode, dir), - CpioAction::Link(Link { src, dst }) => cpio.ln(src, dst), - CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?, - CpioAction::Extract(Extract { paths }) => { - if !paths.is_empty() && paths.len() != 2 { - Err(log_err!("invalid arguments"))?; - } - let mut it = paths.iter_mut(); - cpio.extract(it.next(), it.next())?; - } - CpioAction::List(List { path, recursive }) => { - cpio.ls(path.as_str(), *recursive); - exit(0); - } - }; - } - cpio.dump(file)?; +pub(crate) fn cpio_commands(file: &mut String, cmds: &mut Vec) -> LoggedResult { + let file = Utf8CStr::from_string(file); + let mut cpio = if file.exists() { + Cpio::load_from_file(file)? + } else { + Cpio::new() }; - res.log_with_msg(|w| w.write_str("Failed to process cpio")) - .is_ok() + + for cmd in cmds { + if cmd.starts_with('#') { + continue; + } + let mut cmd = CpioCommand::from_args( + &["magiskboot", "cpio", file], + cmd.split(' ') + .filter(|x| !x.is_empty()) + .collect::>() + .as_slice(), + ) + .on_early_exit(print_cpio_usage); + + match &mut cmd.action { + CpioAction::Test(_) => exit(cpio.test()), + CpioAction::Restore(_) => cpio.restore()?, + CpioAction::Patch(_) => cpio.patch(), + CpioAction::Exists(Exists { path }) => { + return Ok(cpio.exists(path)); + } + CpioAction::Backup(Backup { + origin, + skip_compress, + }) => cpio.backup(origin, *skip_compress)?, + CpioAction::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive), + CpioAction::Move(Move { from, to }) => cpio.mv(from, to)?, + CpioAction::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(*mode, dir), + CpioAction::Link(Link { src, dst }) => cpio.ln(src, dst), + CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?, + CpioAction::Extract(Extract { paths }) => { + if !paths.is_empty() && paths.len() != 2 { + Err(log_err!("invalid arguments"))?; + } + let mut it = paths.iter_mut(); + cpio.extract(it.next(), it.next())?; + } + CpioAction::List(List { path, recursive }) => { + cpio.ls(path.as_str(), *recursive); + return Ok(true); + } + }; + } + cpio.dump(file)?; + Ok(true) } fn x8u(x: &[u8; 8]) -> LoggedResult { diff --git a/native/src/boot/dtb.rs b/native/src/boot/dtb.rs index 8a0050b24..51c420b81 100644 --- a/native/src/boot/dtb.rs +++ b/native/src/boot/dtb.rs @@ -1,4 +1,4 @@ -use std::{cell::UnsafeCell, process::exit}; +use std::cell::UnsafeCell; use argh::FromArgs; use fdt::{ @@ -6,23 +6,13 @@ use fdt::{ node::{FdtNode, NodeProperty}, }; -use base::{ - EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, libc::c_char, log_err, map_args, -}; +use base::{LoggedResult, MappedFile, Utf8CStr}; use crate::{check_env, patch::patch_verity}; -#[derive(FromArgs)] -struct DtbCli { - #[argh(positional)] - file: String, - #[argh(subcommand)] - action: DtbAction, -} - #[derive(FromArgs)] #[argh(subcommand)] -enum DtbAction { +pub(crate) enum DtbAction { Print(Print), Patch(Patch), Test(Test), @@ -30,20 +20,20 @@ enum DtbAction { #[derive(FromArgs)] #[argh(subcommand, name = "print")] -struct Print { +pub(crate) struct Print { #[argh(switch, short = 'f')] fstab: bool, } #[derive(FromArgs)] #[argh(subcommand, name = "patch")] -struct Patch {} +pub(crate) struct Patch {} #[derive(FromArgs)] #[argh(subcommand, name = "test")] -struct Test {} +pub(crate) struct Test {} -fn print_dtb_usage() { +pub(crate) fn print_dtb_usage() { eprintln!( r#"Usage: magiskboot dtb [args...] Do dtb related actions to . @@ -274,34 +264,14 @@ fn dtb_patch(file: &Utf8CStr) -> LoggedResult { Ok(patched) } -pub fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool { - let res: LoggedResult<()> = try { - if argc < 1 { - Err(log_err!("No arguments"))?; +pub(crate) fn dtb_commands(file: &mut String, action: &DtbAction) -> LoggedResult { + let file = Utf8CStr::from_string(file); + match action { + DtbAction::Print(Print { fstab }) => { + dtb_print(file, *fstab)?; + Ok(true) } - let cmds = map_args(argc, argv)?; - - let mut cli = - DtbCli::from_args(&["magiskboot", "dtb"], &cmds).on_early_exit(print_dtb_usage); - - let file = Utf8CStr::from_string(&mut cli.file); - - match cli.action { - DtbAction::Print(Print { fstab }) => { - dtb_print(file, fstab)?; - } - DtbAction::Test(_) => { - if !dtb_test(file)? { - exit(1); - } - } - DtbAction::Patch(_) => { - if !dtb_patch(file)? { - exit(1); - } - } - } - }; - res.log_with_msg(|w| w.write_str("Failed to process dtb")) - .is_ok() + DtbAction::Test(_) => Ok(dtb_test(file)?), + DtbAction::Patch(_) => Ok(dtb_patch(file)?), + } } diff --git a/native/src/boot/format.cpp b/native/src/boot/format.cpp index b6f656f7b..5f1e5fb7e 100644 --- a/native/src/boot/format.cpp +++ b/native/src/boot/format.cpp @@ -1,9 +1,7 @@ #include "boot-rs.hpp" #include "format.hpp" -Name2Fmt name2fmt; Fmt2Name fmt2name; -Fmt2Ext fmt2ext; #define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s)) @@ -72,40 +70,3 @@ const char *Fmt2Name::operator[](FileFormat fmt) { return "raw"; } } - -const char *Fmt2Ext::operator[](FileFormat fmt) { - switch (fmt) { - case FileFormat::GZIP: - case FileFormat::ZOPFLI: - return ".gz"; - case FileFormat::LZOP: - return ".lzo"; - case FileFormat::XZ: - return ".xz"; - case FileFormat::LZMA: - return ".lzma"; - case FileFormat::BZIP2: - return ".bz2"; - case FileFormat::LZ4: - case FileFormat::LZ4_LEGACY: - case FileFormat::LZ4_LG: - return ".lz4"; - default: - return ""; - } -} - -#define CHECK(s, f) else if (name == s) return f; - -FileFormat Name2Fmt::operator[](std::string_view name) { - if (0) {} - CHECK("gzip", FileFormat::GZIP) - CHECK("zopfli", FileFormat::ZOPFLI) - CHECK("xz", FileFormat::XZ) - CHECK("lzma", FileFormat::LZMA) - CHECK("bzip2", FileFormat::BZIP2) - CHECK("lz4", FileFormat::LZ4) - CHECK("lz4_legacy", FileFormat::LZ4_LEGACY) - CHECK("lz4_lg", FileFormat::LZ4_LG) - else return FileFormat::UNKNOWN; -} diff --git a/native/src/boot/format.hpp b/native/src/boot/format.hpp index cd78bdbe7..c48ff91f0 100644 --- a/native/src/boot/format.hpp +++ b/native/src/boot/format.hpp @@ -46,11 +46,6 @@ public: const char *operator[](FileFormat fmt); }; -class Fmt2Ext { -public: - const char *operator[](FileFormat fmt); -}; - class Name2Fmt { public: FileFormat operator[](std::string_view name); @@ -62,6 +57,4 @@ static inline FileFormat check_fmt(rust::Slice bytes) { return check_fmt(bytes.data(), bytes.size()); } -extern Name2Fmt name2fmt; extern Fmt2Name fmt2name; -extern Fmt2Ext fmt2ext; diff --git a/native/src/boot/format.rs b/native/src/boot/format.rs new file mode 100644 index 000000000..bdb7f80ba --- /dev/null +++ b/native/src/boot/format.rs @@ -0,0 +1,85 @@ +use crate::ffi::FileFormat; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + +impl FromStr for FileFormat { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "gzip" => Ok(Self::GZIP), + "zopfli" => Ok(Self::ZOPFLI), + "xz" => Ok(Self::XZ), + "lzma" => Ok(Self::LZMA), + "bzip2" => Ok(Self::BZIP2), + "lz4" => Ok(Self::LZ4), + "lz4_legacy" => Ok(Self::LZ4_LEGACY), + "lz4_lg" => Ok(Self::LZ4_LG), + _ => Err(()), + } + } +} + +impl Display for FileFormat { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match *self { + Self::GZIP => write!(f, "gzip"), + Self::ZOPFLI => write!(f, "zopfli"), + Self::LZOP => write!(f, "lzop"), + Self::XZ => write!(f, "xz"), + Self::LZMA => write!(f, "lzma"), + Self::BZIP2 => write!(f, "bzip2"), + Self::LZ4 => write!(f, "lz4"), + Self::LZ4_LEGACY => write!(f, "lz4_legacy"), + Self::LZ4_LG => write!(f, "lz4_lg"), + Self::DTB => write!(f, "dtb"), + Self::ZIMAGE => write!(f, "zimage"), + _ => write!(f, "raw"), + } + } +} + +impl FileFormat { + pub fn ext(&self) -> &'static str { + match *self { + Self::GZIP | Self::ZOPFLI => ".gz", + Self::LZOP => ".lzo", + Self::XZ => ".xz", + Self::LZMA => ".lzma", + Self::BZIP2 => ".bz2", + Self::LZ4 | Self::LZ4_LEGACY | Self::LZ4_LG => ".lz4", + _ => "", + } + } + + pub fn is_compressed(&self) -> bool { + matches!( + *self, + Self::GZIP + | Self::ZOPFLI + | Self::LZOP + | Self::XZ + | Self::LZMA + | Self::BZIP2 + | Self::LZ4 + | Self::LZ4_LEGACY + | Self::LZ4_LG + ) + } + + pub fn formats() -> String { + [ + Self::GZIP, + Self::ZOPFLI, + Self::XZ, + Self::LZMA, + Self::BZIP2, + Self::LZ4, + Self::LZ4_LEGACY, + Self::LZ4_LG, + Self::LZOP, + ] + .map(|f| f.to_string()) + .join(" ") + } +} diff --git a/native/src/boot/lib.rs b/native/src/boot/lib.rs index 6265dec00..61d22b746 100644 --- a/native/src/boot/lib.rs +++ b/native/src/boot/lib.rs @@ -4,12 +4,8 @@ #![feature(try_blocks)] pub use base; -use compress::{compress_bytes, compress_fd, decompress_bytes, decompress_bytes_fd}; -use cpio::cpio_commands; -use dtb::dtb_commands; -use patch::hexpatch; -use payload::extract_boot_from_payload; -use sign::{SHA, get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image}; +use compress::{compress_bytes, decompress_bytes}; +use sign::{SHA, get_sha, sha256_hash, sign_boot_image, verify_boot_image}; use std::env; mod compress; @@ -18,6 +14,8 @@ mod dtb; mod patch; mod payload; // Suppress warnings in generated code +mod cli; +mod format; #[allow(warnings)] mod proto; mod sign; @@ -68,6 +66,19 @@ pub mod ffi { fn payload(self: &BootImage) -> &[u8]; #[cxx_name = "get_tail"] fn tail(self: &BootImage) -> &[u8]; + + include!("magiskboot.hpp"); + fn cleanup(); + fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32; + fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool); + unsafe fn verify(image: Utf8CStrRef, cert: *const c_char) -> i32; + unsafe fn sign( + image: Utf8CStrRef, + name: Utf8CStrRef, + cert: *const c_char, + key: *const c_char, + ) -> i32; + fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32; } extern "Rust" { @@ -76,33 +87,22 @@ pub mod ffi { fn update(self: &mut SHA, data: &[u8]); fn finalize_into(self: &mut SHA, out: &mut [u8]); fn output_size(self: &SHA) -> usize; - fn sha1_hash(data: &[u8], out: &mut [u8]); fn sha256_hash(data: &[u8], out: &mut [u8]); - fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool; - fn compress_fd(format: FileFormat, in_fd: i32, out_fd: i32); fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32); fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32); - fn decompress_bytes_fd(format: FileFormat, in_bytes: &[u8], in_fd: i32, out_fd: i32); } #[namespace = "rust"] #[allow(unused_unsafe)] extern "Rust" { - fn extract_boot_from_payload( - partition: Utf8CStrRef, - in_path: Utf8CStrRef, - out_path: Utf8CStrRef, - ) -> bool; - unsafe fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool; - unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool; unsafe fn sign_boot_image( payload: &[u8], name: *const c_char, cert: *const c_char, key: *const c_char, ) -> Vec; - unsafe fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool; + unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool; } } diff --git a/native/src/boot/magiskboot.hpp b/native/src/boot/magiskboot.hpp index 7bd8e43f9..ba850dee8 100644 --- a/native/src/boot/magiskboot.hpp +++ b/native/src/boot/magiskboot.hpp @@ -12,8 +12,21 @@ #define BOOTCONFIG_FILE "bootconfig" #define NEW_BOOT "new-boot.img" -int unpack(const char *image, bool skip_decomp = false, bool hdr = false); -void repack(const char *src_img, const char *out_img, bool skip_comp = false); -int verify(const char *image, const char *cert); -int sign(const char *image, const char *name, const char *cert, const char *key); -int split_image_dtb(const char *filename, bool skip_decomp = false); +int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false); +void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false); +int verify(rust::Utf8CStr image, const char *cert); +int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key); +int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false); + +inline void cleanup() { + unlink(HEADER_FILE); + unlink(KERNEL_FILE); + unlink(RAMDISK_FILE); + unlink(SECOND_FILE); + unlink(KER_DTB_FILE); + unlink(EXTRA_FILE); + unlink(RECV_DTBO_FILE); + unlink(DTB_FILE); + unlink(BOOTCONFIG_FILE); + rm_rf(VND_RAMDISK_DIR); +} diff --git a/native/src/boot/main.cpp b/native/src/boot/main.cpp deleted file mode 100644 index 21771cfa8..000000000 --- a/native/src/boot/main.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include - -#include "boot-rs.hpp" -#include "magiskboot.hpp" - -using namespace std; - -static void print_formats() { - for (int fmt = +FileFormat::GZIP; fmt < +FileFormat::LZOP; ++fmt) { - fprintf(stderr, "%s ", fmt2name[(FileFormat) fmt]); - } -} - -static void usage(char *arg0) { - fprintf(stderr, -R"EOF(MagiskBoot - Boot Image Modification Tool - -Usage: %s [args...] - -Supported actions: - unpack [-n] [-h] - Unpack to its individual components, each component to - a file with its corresponding file name in the current directory. - Supported components: kernel, kernel_dtb, ramdisk.cpio, second, - dtb, extra, and recovery_dtbo. - By default, each component will be decompressed on-the-fly. - If '-n' is provided, all decompression operations will be skipped; - each component will remain untouched, dumped in its original format. - If '-h' is provided, the boot image header information will be - dumped to the file 'header', which can be used to modify header - configurations during repacking. - Return values: - 0:valid 1:error 2:chromeos - - repack [-n] [outbootimg] - Repack boot image components using files from the current directory - to [outbootimg], or 'new-boot.img' if not specified. Current directory - should only contain required files for [outbootimg], or incorrect - [outbootimg] may be produced. - is the original boot image used to unpack the components. - By default, each component will be automatically compressed using its - corresponding format detected in . If a component file - in the current directory is already compressed, then no addition - compression will be performed for that specific component. - If '-n' is provided, all compression operations will be skipped. - If env variable PATCHVBMETAFLAG is set to true, all disable flags in - the boot image's vbmeta header will be set. - - verify [x509.pem] - Check whether the boot image is signed with AVB 1.0 signature. - Optionally provide a certificate to verify whether the image is - signed by the public key certificate. - Return value: - 0:valid 1:error - - sign [name] [x509.pem pk8] - Sign with AVB 1.0 signature. - Optionally provide the name of the image (default: '/boot'). - Optionally provide the certificate/private key pair for signing. - If the certificate/private key pair is not provided, the AOSP - verity key bundled in the executable will be used. - - extract [partition] [outfile] - Extract [partition] from to [outfile]. - If [outfile] is not specified, then output to '[partition].img'. - 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. - can be '-' to be STDIN. - - hexpatch - Search in , and replace it with - - cpio [commands...] - Do cpio commands to (modifications are done in-place). - Each command is a single argument; add quotes for each command. - See "cpio --help" for supported commands. - - dtb [args...] - Do dtb related actions to . - See "dtb --help" for supported actions. - - split [-n] - Split image.*-dtb into kernel + kernel_dtb. - If '-n' is provided, decompression operations will be skipped; - the kernel will remain untouched, split in its original format. - - sha1 - Print the SHA1 checksum for - - cleanup - Cleanup the current working directory - - compress[=format] [outfile] - Compress with [format] to [outfile]. - /[outfile] can be '-' to be STDIN/STDOUT. - If [format] is not specified, then gzip will be used. - If [outfile] is not specified, then will be replaced - with another file suffixed with a matching file extension. - Supported formats: )EOF", arg0); - - print_formats(); - - fprintf(stderr, R"EOF( - - decompress [outfile] - Detect format and decompress to [outfile]. - /[outfile] can be '-' to be STDIN/STDOUT. - If [outfile] is not specified, then will be replaced - with another file removing its archive format file extension. - Supported formats: )EOF"); - - print_formats(); - - fprintf(stderr, "\n\n"); - exit(1); -} - -static void decompress(char *infile, const char *outfile) { - bool in_std = infile == "-"sv; - bool rm_in = false; - - int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY); - int out_fd = -1; - - uint8_t buf[4096]; - size_t len = read(in_fd, buf, sizeof(buf)); - FileFormat type = check_fmt(buf, len); - - fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]); - - if (!COMPRESSED(type)) - LOGE("Input file is not a supported compressed type!\n"); - - // If user does not provide outfile, infile has to be either - // .[ext], or '-'. Outfile will be either or '-'. - // If the input does not have proper format, abort. - - char *ext = nullptr; - if (outfile == nullptr) { - outfile = infile; - if (!in_std) { - ext = strrchr(infile, '.'); - if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0) - LOGE("Input file is not a supported type!\n"); - - // Strip out extension and remove input - *ext = '\0'; - rm_in = true; - fprintf(stderr, "Decompressing to [%s]\n", outfile); - } - } - - out_fd = outfile == "-"sv ? - STDOUT_FILENO : - xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (ext) *ext = '.'; - - decompress_bytes_fd(type, byte_view{ buf, len }, in_fd, out_fd); - - if (in_fd != STDIN_FILENO) close(in_fd); - if (out_fd != STDOUT_FILENO) close(out_fd); - - if (rm_in) - unlink(infile); -} - -static void compress(const char *method, const char *infile, const char *outfile) { - FileFormat fmt = name2fmt[method]; - if (fmt == FileFormat::UNKNOWN) - LOGE("Unknown compression method: [%s]\n", method); - - bool in_std = infile == "-"sv; - bool rm_in = false; - - int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY); - int out_fd = -1; - - if (outfile == nullptr) { - if (in_std) { - out_fd = STDOUT_FILENO; - } else { - // If user does not provide outfile and infile is not - // STDIN, output to .[ext] - string tmp(infile); - tmp += fmt2ext[fmt]; - out_fd = xopen(tmp.data(), O_WRONLY | O_CREAT | O_TRUNC, 0644); - fprintf(stderr, "Compressing to [%s]\n", tmp.data()); - rm_in = true; - } - } else { - out_fd = outfile == "-"sv ? - STDOUT_FILENO : - xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); - } - - compress_fd(fmt, in_fd, out_fd); - - if (in_fd != STDIN_FILENO) close(in_fd); - if (out_fd != STDOUT_FILENO) close(out_fd); - - if (rm_in) - unlink(infile); -} - -int main(int argc, char *argv[]) { - cmdline_logging(); - umask(0); - - if (argc < 2) - usage(argv[0]); - - // Skip '--' for backwards compatibility - string_view action(argv[1]); - if (str_starts(action, "--")) - action = argv[1] + 2; - - if (action == "cleanup") { - fprintf(stderr, "Cleaning up...\n"); - unlink(HEADER_FILE); - unlink(KERNEL_FILE); - unlink(RAMDISK_FILE); - unlink(SECOND_FILE); - unlink(KER_DTB_FILE); - unlink(EXTRA_FILE); - unlink(RECV_DTBO_FILE); - unlink(DTB_FILE); - unlink(BOOTCONFIG_FILE); - rm_rf(VND_RAMDISK_DIR); - } else if (argc > 2 && action == "sha1") { - uint8_t sha1[20]; - { - mmap_data m(argv[2]); - sha1_hash(m, byte_data(sha1, sizeof(sha1))); - } - for (uint8_t i : sha1) - printf("%02x", i); - printf("\n"); - } else if (argc > 2 && action == "split") { - if (argv[2] == "-n"sv) { - if (argc == 3) - usage(argv[0]); - return split_image_dtb(argv[3], true); - } else { - return split_image_dtb(argv[2]); - } - } else if (argc > 2 && action == "unpack") { - int idx = 2; - bool nodecomp = false; - bool hdr = false; - for (;;) { - if (idx >= argc) - usage(argv[0]); - if (argv[idx][0] != '-') - break; - for (char *flag = &argv[idx][1]; *flag; ++flag) { - if (*flag == 'n') - nodecomp = true; - else if (*flag == 'h') - hdr = true; - else - usage(argv[0]); - } - ++idx; - } - return unpack(argv[idx], nodecomp, hdr); - } else if (argc > 2 && action == "repack") { - if (argv[2] == "-n"sv) { - if (argc == 3) - usage(argv[0]); - repack(argv[3], argv[4] ? argv[4] : NEW_BOOT, true); - } else { - repack(argv[2], argv[3] ? argv[3] : NEW_BOOT); - } - } else if (argc > 2 && action == "verify") { - return verify(argv[2], argv[3]); - } else if (argc > 2 && action == "sign") { - if (argc == 5) usage(argv[0]); - return sign( - argv[2], - argc > 3 ? argv[3] : "/boot", - argc > 5 ? argv[4] : nullptr, - argc > 5 ? argv[5] : nullptr); - } else if (argc > 2 && action == "decompress") { - decompress(argv[2], argv[3]); - } else if (argc > 2 && str_starts(action, "compress")) { - compress(action[8] == '=' ? &action[9] : "gzip", argv[2], argv[3]); - } else if (argc > 4 && action == "hexpatch") { - return hexpatch(byte_view(argv[2]), byte_view(argv[3]), byte_view(argv[4])) ? 0 : 1; - } else if (argc > 2 && action == "cpio") { - return rust::cpio_commands(argc - 2, argv + 2) ? 0 : 1; - } else if (argc > 2 && action == "dtb") { - return rust::dtb_commands(argc - 2, argv + 2) ? 0 : 1; - } else if (argc > 2 && action == "extract") { - return rust::extract_boot_from_payload( - argv[2], - argc > 3 ? argv[3] : "", - argc > 4 ? argv[4] : "" - ) ? 0 : 1; - } else { - usage(argv[0]); - } - - return 0; -} diff --git a/native/src/boot/patch.rs b/native/src/boot/patch.rs index fc5ee660c..5535217f0 100644 --- a/native/src/boot/patch.rs +++ b/native/src/boot/patch.rs @@ -102,13 +102,9 @@ fn hex2byte(hex: &[u8]) -> Vec { v } -pub fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool { +pub fn hexpatch(file: &mut String, from: &Utf8CStr, to: &Utf8CStr) -> bool { let res: LoggedResult = try { - let file = Utf8CStr::from_bytes(file)?; - let from = Utf8CStr::from_bytes(from)?; - let to = Utf8CStr::from_bytes(to)?; - - let mut map = MappedFile::open_rw(file)?; + let mut map = MappedFile::open_rw(Utf8CStr::from_string(file))?; let pattern = hex2byte(from.as_bytes()); let patch = hex2byte(to.as_bytes()); diff --git a/native/src/boot/payload.rs b/native/src/boot/payload.rs index e6cf7c423..ff4d25b17 100644 --- a/native/src/boot/payload.rs +++ b/native/src/boot/payload.rs @@ -9,9 +9,7 @@ use std::{ use crate::compress::get_decoder; use crate::ffi::check_fmt; use crate::proto::update_metadata::{DeltaArchiveManifest, mod_InstallOperation::Type}; -use base::{ - LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt, error, ffi::Utf8CStrRef, -}; +use base::{LoggedError, LoggedResult, ReadSeekExt, ResultExt, WriteExt, error}; macro_rules! bad_payload { ($msg:literal) => {{ @@ -26,10 +24,10 @@ macro_rules! bad_payload { const PAYLOAD_MAGIC: &str = "CrAU"; -fn do_extract_boot_from_payload( - in_path: &Utf8CStr, - partition_name: Option<&Utf8CStr>, - out_path: Option<&Utf8CStr>, +pub fn extract_boot_from_payload( + in_path: &str, + partition_name: Option<&str>, + out_path: Option<&str>, ) -> LoggedResult<()> { let mut reader = BufReader::new(if in_path == "-" { unsafe { File::from_raw_fd(0) } @@ -179,25 +177,3 @@ fn do_extract_boot_from_payload( Ok(()) } - -pub fn extract_boot_from_payload( - in_path: Utf8CStrRef, - partition: Utf8CStrRef, - out_path: Utf8CStrRef, -) -> bool { - let res: LoggedResult<()> = try { - let partition = if partition.is_empty() { - None - } else { - Some(partition) - }; - let out_path = if out_path.is_empty() { - None - } else { - Some(out_path) - }; - do_extract_boot_from_payload(in_path, partition, out_path)? - }; - res.log_with_msg(|w| w.write_str("Failed to extract from payload")) - .is_ok() -}