mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57:39 +00:00
Refactor cpio
This commit is contained in:
parent
2a3d34c812
commit
b22b6a4204
@ -776,6 +776,23 @@ impl FsPath {
|
|||||||
unsafe { libc::link(self.as_ptr(), path.as_ptr()).as_os_err() }
|
unsafe { libc::link(self.as_ptr(), path.as_ptr()).as_os_err() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn symlink_to(&self, path: &FsPath) -> io::Result<()> {
|
||||||
|
unsafe { libc::symlink(self.as_ptr(), path.as_ptr()).as_os_err() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent(&self, buf: &mut dyn Utf8CStrWrite) -> bool {
|
||||||
|
buf.clear();
|
||||||
|
if let Some(parent) = Path::new(self.as_str()).parent() {
|
||||||
|
let bytes = parent.as_os_str().as_bytes();
|
||||||
|
// SAFETY: all substring of self is valid UTF-8
|
||||||
|
let parent = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||||
|
buf.push_str(parent);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
#![allow(clippy::useless_conversion)]
|
#![allow(clippy::useless_conversion)]
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::fs::{DirBuilder, File};
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::unix::fs::{symlink, DirBuilderExt};
|
|
||||||
use std::path::Path;
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
@ -16,17 +15,18 @@ use num_traits::cast::AsPrimitive;
|
|||||||
use size::{Base, Size, Style};
|
use size::{Base, Size, Style};
|
||||||
|
|
||||||
use base::libc::{
|
use base::libc::{
|
||||||
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, O_CLOEXEC, O_RDONLY,
|
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, O_CLOEXEC, O_CREAT,
|
||||||
S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP,
|
O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRGRP,
|
||||||
S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
||||||
};
|
};
|
||||||
use base::{
|
use base::{
|
||||||
log_err, map_args, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile, ResultExt,
|
log_err, map_args, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile, ResultExt,
|
||||||
Utf8CStr, WriteExt,
|
Utf8CStr, Utf8CStrBufArr, Utf8CStrWrite, WriteExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::check_env;
|
||||||
use crate::ffi::{unxz, xz};
|
use crate::ffi::{unxz, xz};
|
||||||
use crate::ramdisk::MagiskCpio;
|
use crate::patch::{patch_encryption, patch_verity};
|
||||||
|
|
||||||
#[derive(FromArgs)]
|
#[derive(FromArgs)]
|
||||||
struct CpioCli {
|
struct CpioCli {
|
||||||
@ -39,12 +39,12 @@ struct CpioCli {
|
|||||||
#[derive(FromArgs)]
|
#[derive(FromArgs)]
|
||||||
struct CpioCommand {
|
struct CpioCommand {
|
||||||
#[argh(subcommand)]
|
#[argh(subcommand)]
|
||||||
command: CpioSubCommand,
|
action: CpioAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs)]
|
#[derive(FromArgs)]
|
||||||
#[argh(subcommand)]
|
#[argh(subcommand)]
|
||||||
enum CpioSubCommand {
|
enum CpioAction {
|
||||||
Test(Test),
|
Test(Test),
|
||||||
Restore(Restore),
|
Restore(Restore),
|
||||||
Patch(Patch),
|
Patch(Patch),
|
||||||
@ -208,17 +208,17 @@ struct CpioHeader {
|
|||||||
check: [u8; 8],
|
check: [u8; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Cpio {
|
struct Cpio {
|
||||||
pub(crate) entries: BTreeMap<String, Box<CpioEntry>>,
|
entries: BTreeMap<String, Box<CpioEntry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct CpioEntry {
|
struct CpioEntry {
|
||||||
pub(crate) mode: mode_t,
|
mode: mode_t,
|
||||||
pub(crate) uid: uid_t,
|
uid: uid_t,
|
||||||
pub(crate) gid: gid_t,
|
gid: gid_t,
|
||||||
pub(crate) rdevmajor: dev_t,
|
rdevmajor: dev_t,
|
||||||
pub(crate) rdevminor: dev_t,
|
rdevminor: dev_t,
|
||||||
pub(crate) data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cpio {
|
impl Cpio {
|
||||||
@ -268,7 +268,7 @@ impl Cpio {
|
|||||||
Ok(cpio)
|
Ok(cpio)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn load_from_file(path: &Utf8CStr) -> LoggedResult<Self> {
|
fn load_from_file(path: &Utf8CStr) -> LoggedResult<Self> {
|
||||||
eprintln!("Loading cpio: [{}]", path);
|
eprintln!("Loading cpio: [{}]", path);
|
||||||
let file = MappedFile::open(path)?;
|
let file = MappedFile::open(path)?;
|
||||||
Self::load_from_data(file.as_ref())
|
Self::load_from_data(file.as_ref())
|
||||||
@ -317,7 +317,7 @@ impl Cpio {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn rm(&mut self, path: &str, recursive: bool) {
|
fn rm(&mut self, path: &str, recursive: bool) {
|
||||||
let path = norm_path(path);
|
let path = norm_path(path);
|
||||||
if self.entries.remove(&path).is_some() {
|
if self.entries.remove(&path).is_some() {
|
||||||
eprintln!("Removed entry [{}]", path);
|
eprintln!("Removed entry [{}]", path);
|
||||||
@ -335,41 +335,39 @@ impl Cpio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_entry(&self, path: &str, out: &Path) -> LoggedResult<()> {
|
fn extract_entry(&self, path: &str, out: &mut String) -> LoggedResult<()> {
|
||||||
let entry = self
|
let entry = self
|
||||||
.entries
|
.entries
|
||||||
.get(path)
|
.get(path)
|
||||||
.ok_or_else(|| log_err!("No such file"))?;
|
.ok_or_else(|| log_err!("No such file"))?;
|
||||||
eprintln!("Extracting entry [{}] to [{}]", path, out.to_string_lossy());
|
eprintln!("Extracting entry [{}] to [{}]", path, out);
|
||||||
if let Some(parent) = out.parent() {
|
|
||||||
DirBuilder::new()
|
let out = Utf8CStr::from_string(out);
|
||||||
.mode(0o755)
|
let out = FsPath::from(out);
|
||||||
.recursive(true)
|
|
||||||
.create(parent)?;
|
let mut buf = Utf8CStrBufArr::default();
|
||||||
|
|
||||||
|
// Make sure its parent directories exist
|
||||||
|
if out.parent(&mut buf) {
|
||||||
|
FsPath::from(&buf).mkdirs(0o755)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mode: mode_t = (entry.mode & 0o777).into();
|
||||||
|
|
||||||
match entry.mode & S_IFMT {
|
match entry.mode & S_IFMT {
|
||||||
S_IFDIR => {
|
S_IFDIR => out.mkdir(mode)?,
|
||||||
DirBuilder::new()
|
|
||||||
.mode((entry.mode & 0o777).into())
|
|
||||||
.recursive(true) // avoid error if existing
|
|
||||||
.create(out)?;
|
|
||||||
}
|
|
||||||
S_IFREG => {
|
S_IFREG => {
|
||||||
let mut file = File::create(out)?;
|
let mut file = out.create(O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, mode)?;
|
||||||
file.write_all(&entry.data)?;
|
file.write_all(&entry.data)?;
|
||||||
}
|
}
|
||||||
S_IFLNK => {
|
S_IFLNK => {
|
||||||
symlink(Path::new(&str::from_utf8(entry.data.as_slice())?), out)?;
|
buf.clear();
|
||||||
|
buf.push_str(str::from_utf8(entry.data.as_slice())?);
|
||||||
|
FsPath::from(&buf).symlink_to(out)?;
|
||||||
}
|
}
|
||||||
S_IFBLK | S_IFCHR => {
|
S_IFBLK | S_IFCHR => {
|
||||||
let dev = makedev(entry.rdevmajor.try_into()?, entry.rdevminor.try_into()?);
|
let dev = makedev(entry.rdevmajor.try_into()?, entry.rdevminor.try_into()?);
|
||||||
unsafe {
|
unsafe { mknod(out.as_ptr().cast(), entry.mode, dev) };
|
||||||
mknod(
|
|
||||||
out.to_str().unwrap().as_ptr() as *const c_char,
|
|
||||||
entry.mode,
|
|
||||||
dev,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(log_err!("unknown entry type"));
|
return Err(log_err!("unknown entry type"));
|
||||||
@ -378,23 +376,22 @@ impl Cpio {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract(&self, path: Option<&str>, out: Option<&str>) -> LoggedResult<()> {
|
fn extract(&self, path: Option<&mut String>, out: Option<&mut String>) -> LoggedResult<()> {
|
||||||
let path = path.map(norm_path);
|
let path = path.map(|s| norm_path(s.as_str()));
|
||||||
let out = out.map(Path::new);
|
if let (Some(path), Some(out)) = (&path, out) {
|
||||||
if let (Some(path), Some(out)) = (&path, &out) {
|
|
||||||
return self.extract_entry(path, out);
|
return self.extract_entry(path, out);
|
||||||
} else {
|
} else {
|
||||||
for path in self.entries.keys() {
|
for path in self.entries.keys() {
|
||||||
if path == "." || path == ".." {
|
if path == "." || path == ".." {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
self.extract_entry(path, Path::new(path))?;
|
self.extract_entry(path, &mut path.clone())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn exists(&self, path: &str) -> bool {
|
fn exists(&self, path: &str) -> bool {
|
||||||
self.entries.contains_key(&norm_path(path))
|
self.entries.contains_key(&norm_path(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,11 +439,11 @@ impl Cpio {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mkdir(&mut self, mode: &mode_t, dir: &str) {
|
fn mkdir(&mut self, mode: mode_t, dir: &str) {
|
||||||
self.entries.insert(
|
self.entries.insert(
|
||||||
norm_path(dir),
|
norm_path(dir),
|
||||||
Box::new(CpioEntry {
|
Box::new(CpioEntry {
|
||||||
mode: *mode | S_IFDIR,
|
mode: mode | S_IFDIR,
|
||||||
uid: 0,
|
uid: 0,
|
||||||
gid: 0,
|
gid: 0,
|
||||||
rdevmajor: 0,
|
rdevmajor: 0,
|
||||||
@ -506,6 +503,196 @@ impl Cpio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAGISK_PATCHED: i32 = 1 << 0;
|
||||||
|
const UNSUPPORTED_CPIO: i32 = 1 << 1;
|
||||||
|
|
||||||
|
impl Cpio {
|
||||||
|
fn patch(&mut self) {
|
||||||
|
let keep_verity = check_env("KEEPVERITY");
|
||||||
|
let keep_force_encrypt = check_env("KEEPFORCEENCRYPT");
|
||||||
|
eprintln!(
|
||||||
|
"Patch with flag KEEPVERITY=[{}] KEEPFORCEENCRYPT=[{}]",
|
||||||
|
keep_verity, keep_force_encrypt
|
||||||
|
);
|
||||||
|
self.entries.retain(|name, entry| {
|
||||||
|
let fstab = (!keep_verity || !keep_force_encrypt)
|
||||||
|
&& entry.mode & S_IFMT == S_IFREG
|
||||||
|
&& !name.starts_with(".backup")
|
||||||
|
&& !name.starts_with("twrp")
|
||||||
|
&& !name.starts_with("recovery")
|
||||||
|
&& name.starts_with("fstab");
|
||||||
|
if !keep_verity {
|
||||||
|
if fstab {
|
||||||
|
eprintln!("Found fstab file [{}]", name);
|
||||||
|
let len = patch_verity(entry.data.as_mut_slice());
|
||||||
|
if len != entry.data.len() {
|
||||||
|
entry.data.resize(len, 0);
|
||||||
|
}
|
||||||
|
} else if name == "verity_key" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !keep_force_encrypt && fstab {
|
||||||
|
let len = patch_encryption(entry.data.as_mut_slice());
|
||||||
|
if len != entry.data.len() {
|
||||||
|
entry.data.resize(len, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test(&self) -> i32 {
|
||||||
|
let mut ret = 0;
|
||||||
|
for file in [
|
||||||
|
"sbin/launch_daemonsu.sh",
|
||||||
|
"sbin/su",
|
||||||
|
"init.xposed.rc",
|
||||||
|
"boot/sbin/launch_daemonsu.sh",
|
||||||
|
] {
|
||||||
|
if self.exists(file) {
|
||||||
|
return UNSUPPORTED_CPIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for file in [
|
||||||
|
".backup/.magisk",
|
||||||
|
"init.magisk.rc",
|
||||||
|
"overlay/init.magisk.rc",
|
||||||
|
] {
|
||||||
|
if self.exists(file) {
|
||||||
|
ret |= MAGISK_PATCHED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(&mut self) -> LoggedResult<()> {
|
||||||
|
let mut backups = HashMap::<String, Box<CpioEntry>>::new();
|
||||||
|
let mut rm_list = String::new();
|
||||||
|
self.entries
|
||||||
|
.extract_if(|name, _| name.starts_with(".backup/"))
|
||||||
|
.for_each(|(name, mut entry)| {
|
||||||
|
if name == ".backup/.rmlist" {
|
||||||
|
if let Ok(data) = str::from_utf8(&entry.data) {
|
||||||
|
rm_list.push_str(data);
|
||||||
|
}
|
||||||
|
} else if name != ".backup/.magisk" {
|
||||||
|
let new_name = if name.ends_with(".xz") && entry.decompress() {
|
||||||
|
&name[8..name.len() - 3]
|
||||||
|
} else {
|
||||||
|
&name[8..]
|
||||||
|
};
|
||||||
|
eprintln!("Restore [{}] -> [{}]", name, new_name);
|
||||||
|
backups.insert(new_name.to_string(), entry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.rm(".backup", false);
|
||||||
|
if rm_list.is_empty() && backups.is_empty() {
|
||||||
|
self.entries.clear();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
for rm in rm_list.split('\0') {
|
||||||
|
if !rm.is_empty() {
|
||||||
|
self.rm(rm, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.entries.extend(backups);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backup(&mut self, origin: &mut String, skip_compress: bool) -> LoggedResult<()> {
|
||||||
|
let mut backups = HashMap::<String, Box<CpioEntry>>::new();
|
||||||
|
let mut rm_list = String::new();
|
||||||
|
backups.insert(
|
||||||
|
".backup".to_string(),
|
||||||
|
Box::new(CpioEntry {
|
||||||
|
mode: S_IFDIR,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdevmajor: 0,
|
||||||
|
rdevminor: 0,
|
||||||
|
data: vec![],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let origin = Utf8CStr::from_string(origin);
|
||||||
|
let mut o = Cpio::load_from_file(origin)?;
|
||||||
|
o.rm(".backup", true);
|
||||||
|
self.rm(".backup", true);
|
||||||
|
|
||||||
|
let mut lhs = o.entries.into_iter().peekable();
|
||||||
|
let mut rhs = self.entries.iter().peekable();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
enum Action<'a> {
|
||||||
|
Backup(String, Box<CpioEntry>),
|
||||||
|
Record(&'a String),
|
||||||
|
Noop,
|
||||||
|
}
|
||||||
|
let action = match (lhs.peek(), rhs.peek()) {
|
||||||
|
(Some((l, _)), Some((r, re))) => match l.as_str().cmp(r.as_str()) {
|
||||||
|
Ordering::Less => {
|
||||||
|
let (l, le) = lhs.next().unwrap();
|
||||||
|
Action::Backup(l, le)
|
||||||
|
}
|
||||||
|
Ordering::Greater => Action::Record(rhs.next().unwrap().0),
|
||||||
|
Ordering::Equal => {
|
||||||
|
let (l, le) = lhs.next().unwrap();
|
||||||
|
let action = if re.data != le.data {
|
||||||
|
Action::Backup(l, le)
|
||||||
|
} else {
|
||||||
|
Action::Noop
|
||||||
|
};
|
||||||
|
rhs.next();
|
||||||
|
action
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Some(_), None) => {
|
||||||
|
let (l, le) = lhs.next().unwrap();
|
||||||
|
Action::Backup(l, le)
|
||||||
|
}
|
||||||
|
(None, Some(_)) => Action::Record(rhs.next().unwrap().0),
|
||||||
|
(None, None) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match action {
|
||||||
|
Action::Backup(name, mut entry) => {
|
||||||
|
let backup = if !skip_compress && entry.compress() {
|
||||||
|
format!(".backup/{}.xz", name)
|
||||||
|
} else {
|
||||||
|
format!(".backup/{}", name)
|
||||||
|
};
|
||||||
|
eprintln!("Backup [{}] -> [{}]", name, backup);
|
||||||
|
backups.insert(backup, entry);
|
||||||
|
}
|
||||||
|
Action::Record(name) => {
|
||||||
|
eprintln!("Record new entry: [{}] -> [.backup/.rmlist]", name);
|
||||||
|
rm_list.push_str(&format!("{}\0", name));
|
||||||
|
}
|
||||||
|
Action::Noop => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !rm_list.is_empty() {
|
||||||
|
backups.insert(
|
||||||
|
".backup/.rmlist".to_string(),
|
||||||
|
Box::new(CpioEntry {
|
||||||
|
mode: S_IFREG,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdevmajor: 0,
|
||||||
|
rdevminor: 0,
|
||||||
|
data: rm_list.as_bytes().to_vec(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.entries.extend(backups);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CpioEntry {
|
impl CpioEntry {
|
||||||
pub(crate) fn compress(&mut self) -> bool {
|
pub(crate) fn compress(&mut self) -> bool {
|
||||||
if self.mode & S_IFMT != S_IFREG {
|
if self.mode & S_IFMT != S_IFREG {
|
||||||
@ -581,7 +768,7 @@ pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
|||||||
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
|
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
|
||||||
|
|
||||||
let file = Utf8CStr::from_string(&mut cli.file);
|
let file = Utf8CStr::from_string(&mut cli.file);
|
||||||
let mut cpio = if Path::new(file).exists() {
|
let mut cpio = if FsPath::from(file).exists() {
|
||||||
Cpio::load_from_file(file)?
|
Cpio::load_from_file(file)?
|
||||||
} else {
|
} else {
|
||||||
Cpio::new()
|
Cpio::new()
|
||||||
@ -600,36 +787,34 @@ pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
|||||||
)
|
)
|
||||||
.on_early_exit(print_cpio_usage);
|
.on_early_exit(print_cpio_usage);
|
||||||
|
|
||||||
match &mut cli.command {
|
match &mut cli.action {
|
||||||
CpioSubCommand::Test(_) => exit(cpio.test()),
|
CpioAction::Test(_) => exit(cpio.test()),
|
||||||
CpioSubCommand::Restore(_) => cpio.restore()?,
|
CpioAction::Restore(_) => cpio.restore()?,
|
||||||
CpioSubCommand::Patch(_) => cpio.patch(),
|
CpioAction::Patch(_) => cpio.patch(),
|
||||||
CpioSubCommand::Exists(Exists { path }) => {
|
CpioAction::Exists(Exists { path }) => {
|
||||||
if cpio.exists(path) {
|
if cpio.exists(path) {
|
||||||
exit(0);
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CpioSubCommand::Backup(Backup {
|
CpioAction::Backup(Backup {
|
||||||
origin,
|
origin,
|
||||||
skip_compress,
|
skip_compress,
|
||||||
}) => cpio.backup(Utf8CStr::from_string(origin), *skip_compress)?,
|
}) => cpio.backup(origin, *skip_compress)?,
|
||||||
CpioSubCommand::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive),
|
CpioAction::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive),
|
||||||
CpioSubCommand::Move(Move { from, to }) => cpio.mv(from, to)?,
|
CpioAction::Move(Move { from, to }) => cpio.mv(from, to)?,
|
||||||
CpioSubCommand::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(mode, dir),
|
CpioAction::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(*mode, dir),
|
||||||
CpioSubCommand::Link(Link { src, dst }) => cpio.ln(src, dst),
|
CpioAction::Link(Link { src, dst }) => cpio.ln(src, dst),
|
||||||
CpioSubCommand::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
|
CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
|
||||||
CpioSubCommand::Extract(Extract { paths }) => {
|
CpioAction::Extract(Extract { paths }) => {
|
||||||
if !paths.is_empty() && paths.len() != 2 {
|
if !paths.is_empty() && paths.len() != 2 {
|
||||||
return Err(log_err!("invalid arguments"));
|
return Err(log_err!("invalid arguments"));
|
||||||
}
|
}
|
||||||
cpio.extract(
|
let mut it = paths.iter_mut();
|
||||||
paths.first().map(|x| x.as_str()),
|
cpio.extract(it.next(), it.next())?;
|
||||||
paths.get(1).map(|x| x.as_str()),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
CpioSubCommand::List(List { path, recursive }) => {
|
CpioAction::List(List { path, recursive }) => {
|
||||||
cpio.ls(path.as_str(), *recursive);
|
cpio.ls(path.as_str(), *recursive);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ mod payload;
|
|||||||
// Suppress warnings in generated code
|
// Suppress warnings in generated code
|
||||||
#[allow(warnings)]
|
#[allow(warnings)]
|
||||||
mod proto;
|
mod proto;
|
||||||
mod ramdisk;
|
|
||||||
mod sign;
|
mod sign;
|
||||||
|
|
||||||
#[cxx::bridge]
|
#[cxx::bridge]
|
||||||
|
@ -1,206 +0,0 @@
|
|||||||
use std::cmp::Ordering;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::str::from_utf8;
|
|
||||||
|
|
||||||
use base::libc::{S_IFDIR, S_IFMT, S_IFREG};
|
|
||||||
use base::{LoggedResult, Utf8CStr};
|
|
||||||
|
|
||||||
use crate::check_env;
|
|
||||||
use crate::cpio::{Cpio, CpioEntry};
|
|
||||||
use crate::patch::{patch_encryption, patch_verity};
|
|
||||||
|
|
||||||
pub trait MagiskCpio {
|
|
||||||
fn patch(&mut self);
|
|
||||||
fn test(&self) -> i32;
|
|
||||||
fn restore(&mut self) -> LoggedResult<()>;
|
|
||||||
fn backup(&mut self, origin: &Utf8CStr, skip_compress: bool) -> LoggedResult<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAGISK_PATCHED: i32 = 1 << 0;
|
|
||||||
const UNSUPPORTED_CPIO: i32 = 1 << 1;
|
|
||||||
|
|
||||||
impl MagiskCpio for Cpio {
|
|
||||||
fn patch(&mut self) {
|
|
||||||
let keep_verity = check_env("KEEPVERITY");
|
|
||||||
let keep_force_encrypt = check_env("KEEPFORCEENCRYPT");
|
|
||||||
eprintln!(
|
|
||||||
"Patch with flag KEEPVERITY=[{}] KEEPFORCEENCRYPT=[{}]",
|
|
||||||
keep_verity, keep_force_encrypt
|
|
||||||
);
|
|
||||||
self.entries.retain(|name, entry| {
|
|
||||||
let fstab = (!keep_verity || !keep_force_encrypt)
|
|
||||||
&& entry.mode & S_IFMT == S_IFREG
|
|
||||||
&& !name.starts_with(".backup")
|
|
||||||
&& !name.starts_with("twrp")
|
|
||||||
&& !name.starts_with("recovery")
|
|
||||||
&& name.starts_with("fstab");
|
|
||||||
if !keep_verity {
|
|
||||||
if fstab {
|
|
||||||
eprintln!("Found fstab file [{}]", name);
|
|
||||||
let len = patch_verity(entry.data.as_mut_slice());
|
|
||||||
if len != entry.data.len() {
|
|
||||||
entry.data.resize(len, 0);
|
|
||||||
}
|
|
||||||
} else if name == "verity_key" {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !keep_force_encrypt && fstab {
|
|
||||||
let len = patch_encryption(entry.data.as_mut_slice());
|
|
||||||
if len != entry.data.len() {
|
|
||||||
entry.data.resize(len, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(&self) -> i32 {
|
|
||||||
let mut ret = 0;
|
|
||||||
for file in [
|
|
||||||
"sbin/launch_daemonsu.sh",
|
|
||||||
"sbin/su",
|
|
||||||
"init.xposed.rc",
|
|
||||||
"boot/sbin/launch_daemonsu.sh",
|
|
||||||
] {
|
|
||||||
if self.exists(file) {
|
|
||||||
return UNSUPPORTED_CPIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for file in [
|
|
||||||
".backup/.magisk",
|
|
||||||
"init.magisk.rc",
|
|
||||||
"overlay/init.magisk.rc",
|
|
||||||
] {
|
|
||||||
if self.exists(file) {
|
|
||||||
ret |= MAGISK_PATCHED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore(&mut self) -> LoggedResult<()> {
|
|
||||||
let mut backups = HashMap::<String, Box<CpioEntry>>::new();
|
|
||||||
let mut rm_list = String::new();
|
|
||||||
self.entries
|
|
||||||
.extract_if(|name, _| name.starts_with(".backup/"))
|
|
||||||
.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 = if name.ends_with(".xz") && entry.decompress() {
|
|
||||||
&name[8..name.len() - 3]
|
|
||||||
} else {
|
|
||||||
&name[8..]
|
|
||||||
};
|
|
||||||
eprintln!("Restore [{}] -> [{}]", name, new_name);
|
|
||||||
backups.insert(new_name.to_string(), entry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.rm(".backup", false);
|
|
||||||
if rm_list.is_empty() && backups.is_empty() {
|
|
||||||
self.entries.clear();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
for rm in rm_list.split('\0') {
|
|
||||||
if !rm.is_empty() {
|
|
||||||
self.rm(rm, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.entries.extend(backups);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn backup(&mut self, origin: &Utf8CStr, skip_compress: bool) -> LoggedResult<()> {
|
|
||||||
let mut backups = HashMap::<String, Box<CpioEntry>>::new();
|
|
||||||
let mut rm_list = String::new();
|
|
||||||
backups.insert(
|
|
||||||
".backup".to_string(),
|
|
||||||
Box::new(CpioEntry {
|
|
||||||
mode: S_IFDIR,
|
|
||||||
uid: 0,
|
|
||||||
gid: 0,
|
|
||||||
rdevmajor: 0,
|
|
||||||
rdevminor: 0,
|
|
||||||
data: vec![],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let mut o = Cpio::load_from_file(origin)?;
|
|
||||||
o.rm(".backup", true);
|
|
||||||
self.rm(".backup", true);
|
|
||||||
|
|
||||||
let mut lhs = o.entries.into_iter().peekable();
|
|
||||||
let mut rhs = self.entries.iter().peekable();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
enum Action<'a> {
|
|
||||||
Backup(String, Box<CpioEntry>),
|
|
||||||
Record(&'a String),
|
|
||||||
Noop,
|
|
||||||
}
|
|
||||||
let action = match (lhs.peek(), rhs.peek()) {
|
|
||||||
(Some((l, _)), Some((r, re))) => match l.as_str().cmp(r.as_str()) {
|
|
||||||
Ordering::Less => {
|
|
||||||
let (l, le) = lhs.next().unwrap();
|
|
||||||
Action::Backup(l, le)
|
|
||||||
}
|
|
||||||
Ordering::Greater => Action::Record(rhs.next().unwrap().0),
|
|
||||||
Ordering::Equal => {
|
|
||||||
let (l, le) = lhs.next().unwrap();
|
|
||||||
let action = if re.data != le.data {
|
|
||||||
Action::Backup(l, le)
|
|
||||||
} else {
|
|
||||||
Action::Noop
|
|
||||||
};
|
|
||||||
rhs.next();
|
|
||||||
action
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(Some(_), None) => {
|
|
||||||
let (l, le) = lhs.next().unwrap();
|
|
||||||
Action::Backup(l, le)
|
|
||||||
}
|
|
||||||
(None, Some(_)) => Action::Record(rhs.next().unwrap().0),
|
|
||||||
(None, None) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match action {
|
|
||||||
Action::Backup(name, mut entry) => {
|
|
||||||
let backup = if !skip_compress && entry.compress() {
|
|
||||||
format!(".backup/{}.xz", name)
|
|
||||||
} else {
|
|
||||||
format!(".backup/{}", name)
|
|
||||||
};
|
|
||||||
eprintln!("Backup [{}] -> [{}]", name, backup);
|
|
||||||
backups.insert(backup, entry);
|
|
||||||
}
|
|
||||||
Action::Record(name) => {
|
|
||||||
eprintln!("Record new entry: [{}] -> [.backup/.rmlist]", name);
|
|
||||||
rm_list.push_str(&format!("{}\0", name));
|
|
||||||
}
|
|
||||||
Action::Noop => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !rm_list.is_empty() {
|
|
||||||
backups.insert(
|
|
||||||
".backup/.rmlist".to_string(),
|
|
||||||
Box::new(CpioEntry {
|
|
||||||
mode: S_IFREG,
|
|
||||||
uid: 0,
|
|
||||||
gid: 0,
|
|
||||||
rdevmajor: 0,
|
|
||||||
rdevminor: 0,
|
|
||||||
data: rm_list.as_bytes().to_vec(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.entries.extend(backups);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user