diff --git a/native/src/base/include/stream.hpp b/native/src/base/include/stream.hpp index 474208510..09b34f3c6 100644 --- a/native/src/base/include/stream.hpp +++ b/native/src/base/include/stream.hpp @@ -78,6 +78,21 @@ private: void resize(size_t new_sz, bool zero = false); }; +class rust_vec_channel : public channel { +public: + rust_vec_channel(rust::Vec &data) : _data(data) {} + + ssize_t read(void *buf, size_t len) override; + bool write(const void *buf, size_t len) override; + off_t seek(off_t off, int whence) override; + +private: + rust::Vec &_data; + size_t _pos = 0; + + void ensure_size(size_t sz); +}; + class file_channel : public channel { public: bool write(const void *buf, size_t len) final; diff --git a/native/src/base/lib.rs b/native/src/base/lib.rs index 37b34ef76..e05dc3f5e 100644 --- a/native/src/base/lib.rs +++ b/native/src/base/lib.rs @@ -44,6 +44,7 @@ pub mod ffi { fn set_log_level_state_cxx(level: LogLevelCxx, enabled: bool); fn exit_on_error(b: bool); fn cmdline_logging(); + fn resize_vec(vec: &mut Vec, size: usize); } #[namespace = "rust"] @@ -63,3 +64,7 @@ fn set_log_level_state_cxx(level: ffi::LogLevelCxx, enabled: bool) { set_log_level_state(level, enabled) } } + +fn resize_vec(vec: &mut Vec, size: usize) { + vec.resize(size, 0); +} diff --git a/native/src/base/stream.cpp b/native/src/base/stream.cpp index b7a775c73..3628b14e0 100644 --- a/native/src/base/stream.cpp +++ b/native/src/base/stream.cpp @@ -180,6 +180,47 @@ void byte_channel::resize(size_t new_sz, bool zero) { } } +ssize_t rust_vec_channel::read(void *buf, size_t len) { + len = std::min(len, _data.size() - _pos); + memcpy(buf, _data.data() + _pos, len); + _pos += len; + return len; +} + +bool rust_vec_channel::write(const void *buf, size_t len) { + ensure_size(_pos + len); + memcpy(_data.data() + _pos, buf, len); + _pos += len; + return true; +} + +off_t rust_vec_channel::seek(off_t off, int whence) { + off_t np; + switch (whence) { + case SEEK_CUR: + np = _pos + off; + break; + case SEEK_END: + np = _data.size() + off; + break; + case SEEK_SET: + np = off; + break; + default: + return -1; + } + ensure_size(np); + _pos = np; + return np; +} + +void rust_vec_channel::ensure_size(size_t sz) { + size_t old_sz = _data.size(); + if (sz > old_sz) { + resize_vec(_data, sz); + } +} + ssize_t fd_channel::read(void *buf, size_t len) { return ::read(fd, buf, len); } diff --git a/native/src/boot/compress.cpp b/native/src/boot/compress.cpp index 3e145a107..f656bc6b6 100644 --- a/native/src/boot/compress.cpp +++ b/native/src/boot/compress.cpp @@ -732,3 +732,24 @@ bool decompress(rust::Slice buf, int fd) { } return true; } + +bool xz(rust::Slice buf, rust::Vec &out) { + auto strm = get_encoder(XZ, make_unique(out)); + if (!strm->write(buf.data(), buf.length())) { + return false; + } + return true; +} + +bool unxz(rust::Slice buf, rust::Vec &out) { + format_t type = check_fmt(buf.data(), buf.length()); + if (type != XZ) { + LOGE("Input file is not in xz format!\n"); + return false; + } + auto strm = get_decoder(XZ, make_unique(out)); + if (!strm->write(buf.data(), buf.length())) { + return false; + } + return true; +} diff --git a/native/src/boot/compress.hpp b/native/src/boot/compress.hpp index 804c6f741..bcb32e59f 100644 --- a/native/src/boot/compress.hpp +++ b/native/src/boot/compress.hpp @@ -10,3 +10,5 @@ out_strm_ptr get_decoder(format_t type, out_strm_ptr &&base); void compress(const char *method, const char *infile, const char *outfile); void decompress(char *infile, const char *outfile); bool decompress(rust::Slice buf, int fd); +bool xz(rust::Slice buf, rust::Vec &out); +bool unxz(rust::Slice buf, rust::Vec &out); diff --git a/native/src/boot/cpio.rs b/native/src/boot/cpio.rs index 36e012d77..cfb8db411 100644 --- a/native/src/boot/cpio.rs +++ b/native/src/boot/cpio.rs @@ -15,6 +15,7 @@ use bytemuck::{from_bytes, Pod, Zeroable}; use num_traits::cast::AsPrimitive; use size::{Base, Size, Style}; +use crate::ffi::{unxz, xz}; use base::libc::{ c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, 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, @@ -81,6 +82,8 @@ struct Exists { struct Backup { #[argh(positional, arg_name = "orig")] origin: String, + #[argh(switch, short = 'n')] + skip_compress: bool, } #[derive(FromArgs)] @@ -177,8 +180,8 @@ Supported commands: patch Apply ramdisk patches Configure with env variables: KEEPVERITY KEEPFORCEENCRYPT - backup ORIG - Create ramdisk backups from ORIG + backup ORIG [-n] + Create ramdisk backups from ORIG, specify [-n] to skip compression restore Restore ramdisk from ramdisk backup stored within incpio "# @@ -495,6 +498,26 @@ impl Cpio { } } +impl CpioEntry { + pub(crate) fn compress(&mut self) -> LoggedResult<()> { + let mut compressed = Vec::new(); + if !xz(&self.data, &mut compressed) { + return Err(log_err!("xz compression failed")); + } + self.data = compressed; + Ok(()) + } + + pub(crate) fn decompress(&mut self) -> LoggedResult<()> { + let mut decompressed = Vec::new(); + if !unxz(&self.data, &mut decompressed) { + return Err(log_err!("xz decompression failed")); + } + self.data = decompressed; + Ok(()) + } +} + impl Display for CpioEntry { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( @@ -572,9 +595,10 @@ pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool { exit(1); } } - CpioSubCommand::Backup(Backup { origin }) => { - cpio.backup(Utf8CStr::from_string(origin))? - } + CpioSubCommand::Backup(Backup { + origin, + skip_compress, + }) => cpio.backup(Utf8CStr::from_string(origin), *skip_compress)?, CpioSubCommand::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive), CpioSubCommand::Move(Move { from, to }) => cpio.mv(from, to)?, CpioSubCommand::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(mode, dir), diff --git a/native/src/boot/lib.rs b/native/src/boot/lib.rs index 578bde6ba..6e2d4df4d 100644 --- a/native/src/boot/lib.rs +++ b/native/src/boot/lib.rs @@ -25,6 +25,8 @@ pub mod ffi { unsafe extern "C++" { include!("compress.hpp"); fn decompress(buf: &[u8], fd: i32) -> bool; + fn xz(buf: &[u8], out: &mut Vec) -> bool; + fn unxz(buf: &[u8], out: &mut Vec) -> bool; include!("bootimg.hpp"); #[cxx_name = "boot_img"] diff --git a/native/src/boot/ramdisk.rs b/native/src/boot/ramdisk.rs index e3f1f541c..1014860a5 100644 --- a/native/src/boot/ramdisk.rs +++ b/native/src/boot/ramdisk.rs @@ -13,7 +13,7 @@ pub trait MagiskCpio { fn patch(&mut self); fn test(&self) -> i32; fn restore(&mut self) -> LoggedResult<()>; - fn backup(&mut self, origin: &Utf8CStr) -> LoggedResult<()>; + fn backup(&mut self, origin: &Utf8CStr, skip_compress: bool) -> LoggedResult<()>; } const MAGISK_PATCHED: i32 = 1 << 0; @@ -89,13 +89,17 @@ impl MagiskCpio for Cpio { let mut rm_list = String::new(); self.entries .extract_if(|name, _| name.starts_with(".backup/")) - .for_each(|(name, entry)| { + .for_each(|(name, mut entry)| { if name == ".backup/.rmlist" { if let Ok(data) = from_utf8(&entry.data) { rm_list.push_str(data); } } else if name != ".backup/.magisk" { - let new_name = &name[8..]; + let new_name = if name.ends_with(".xz") && entry.decompress().is_ok() { + &name[8..name.len() - 3] + } else { + &name[8..] + }; eprintln!("Restore [{}] -> [{}]", name, new_name); backups.insert(new_name.to_string(), entry); } @@ -115,7 +119,7 @@ impl MagiskCpio for Cpio { Ok(()) } - fn backup(&mut self, origin: &Utf8CStr) -> LoggedResult<()> { + fn backup(&mut self, origin: &Utf8CStr, skip_compress: bool) -> LoggedResult<()> { let mut backups = HashMap::>::new(); let mut rm_list = String::new(); backups.insert( @@ -170,8 +174,12 @@ impl MagiskCpio for Cpio { } }; match action { - Action::Backup(name, entry) => { - let backup = format!(".backup/{}", name); + Action::Backup(name, mut entry) => { + let backup = if !skip_compress && entry.compress().is_ok() { + format!(".backup/{}.xz", name) + } else { + format!(".backup/{}", name) + }; eprintln!("Backup [{}] -> [{}]", name, backup); backups.insert(backup, entry); } diff --git a/native/src/init/getinfo.cpp b/native/src/init/getinfo.cpp index e79fc68e4..b72e63f8c 100644 --- a/native/src/init/getinfo.cpp +++ b/native/src/init/getinfo.cpp @@ -217,8 +217,24 @@ bool check_two_stage() { return init.contains("selinux_setup"); } +void unxz_init(const char *init_xz, const char *init) { + LOGD("unxz %s -> %s\n", init_xz, init); + int fd = xopen(init, O_WRONLY | O_CREAT, 0777); + fd_channel ch(fd); + unxz(ch, mmap_data{init_xz}); + close(fd); + clone_attr(init_xz, init); + unlink(init_xz); +} + const char *backup_init() { if (access("/.backup/init.real", F_OK) == 0) return "/.backup/init.real"; + if (access("/.backup/init.real.xz", F_OK) == 0) { + unxz_init("/.backup/init.real.xz", "/.backup/init.real"); + return "/.backup/init.real"; + } + if (access("/.backup/init.xz", F_OK) == 0) + unxz_init("/.backup/init.xz", "/.backup/init"); return "/.backup/init"; }