mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-26 11:50:46 +00:00
Refactor module mount load in rust
This commit is contained in:
parent
1f162b819d
commit
0905adcd64
@ -81,6 +81,9 @@ pub trait Utf8CStrBuf:
|
|||||||
// 2. Ensure len <= capacity - 1
|
// 2. Ensure len <= capacity - 1
|
||||||
// 3. All bytes from 0 to len is valid UTF-8 and does not contain null
|
// 3. All bytes from 0 to len is valid UTF-8 and does not contain null
|
||||||
unsafe fn set_len(&mut self, len: usize);
|
unsafe fn set_len(&mut self, len: usize);
|
||||||
|
|
||||||
|
// self[len] = b'\0'; self.set_len(len)
|
||||||
|
fn resize(&mut self, len: usize);
|
||||||
fn push_str(&mut self, s: &str) -> usize;
|
fn push_str(&mut self, s: &str) -> usize;
|
||||||
fn push_lossy(&mut self, s: &[u8]) -> usize;
|
fn push_lossy(&mut self, s: &[u8]) -> usize;
|
||||||
// The capacity of the internal buffer. The maximum string length this buffer can contain
|
// The capacity of the internal buffer. The maximum string length this buffer can contain
|
||||||
@ -185,6 +188,7 @@ impl StringExt for PathBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, Ord, PartialOrd)]
|
||||||
pub struct Utf8CString(String);
|
pub struct Utf8CString(String);
|
||||||
|
|
||||||
impl Default for Utf8CString {
|
impl Default for Utf8CString {
|
||||||
@ -237,6 +241,16 @@ impl Utf8CStrBuf for Utf8CString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, len: usize) {
|
||||||
|
if len >= self.0.capacity() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
*self.0.as_mut_ptr().add(len) = b'\0' as _;
|
||||||
|
self.set_len(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn push_str(&mut self, s: &str) -> usize {
|
fn push_str(&mut self, s: &str) -> usize {
|
||||||
self.0.push_str(s);
|
self.0.push_str(s);
|
||||||
self.0.nul_terminate();
|
self.0.nul_terminate();
|
||||||
@ -584,6 +598,14 @@ impl<const N: usize> FsPathBuf<N> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize(mut self, len: usize) -> Self {
|
||||||
|
unsafe {
|
||||||
|
self.0.as_bytes_mut()[len] = b'\0';
|
||||||
|
self.0.set_len(len)
|
||||||
|
};
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn join_fmt<T: Display>(mut self, name: T) -> Self {
|
pub fn join_fmt<T: Display>(mut self, name: T) -> Self {
|
||||||
self.0.write_fmt(format_args!("/{}", name)).ok();
|
self.0.write_fmt(format_args!("/{}", name)).ok();
|
||||||
self
|
self
|
||||||
@ -742,6 +764,14 @@ macro_rules! impl_str_buf_with_slice {
|
|||||||
unsafe fn set_len(&mut self, len: usize) {
|
unsafe fn set_len(&mut self, len: usize) {
|
||||||
self.used = len;
|
self.used = len;
|
||||||
}
|
}
|
||||||
|
fn resize(&mut self, len: usize) {
|
||||||
|
if len < self.capacity() {
|
||||||
|
unsafe {
|
||||||
|
self.buf[len] = b'\0';
|
||||||
|
self.set_len(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn push_str(&mut self, s: &str) -> usize {
|
fn push_str(&mut self, s: &str) -> usize {
|
||||||
utf8_cstr_buf_append(self, s.as_bytes())
|
utf8_cstr_buf_append(self, s.as_bytes())
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
cstr, cstr_buf, errno, fd_path, fd_set_attr, FileAttr, FsPath, LibcReturn, Utf8CStr,
|
cstr, cstr_buf, errno, fd_path, fd_set_attr, FileAttr, FsPath, LibcReturn, Utf8CStr,
|
||||||
Utf8CStrBuf,
|
Utf8CStrBuf,
|
||||||
};
|
};
|
||||||
use libc::{dirent, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
use libc::{dirent, O_CLOEXEC, O_CREAT, O_DIRECTORY, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@ -195,6 +195,13 @@ impl Directory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_dir(&self, name: &CStr) -> io::Result<Directory> {
|
||||||
|
let dirp = unsafe {
|
||||||
|
libc::fdopendir(self.open_raw_fd(name, O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0)?).check_os_err()?
|
||||||
|
};
|
||||||
|
Ok(Directory { dirp })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn contains_path(&self, path: &CStr) -> bool {
|
pub fn contains_path(&self, path: &CStr) -> bool {
|
||||||
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
||||||
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
||||||
|
@ -3,10 +3,7 @@ use crate::{
|
|||||||
Utf8CStrBuf,
|
Utf8CStrBuf,
|
||||||
};
|
};
|
||||||
use bytemuck::{bytes_of, bytes_of_mut, Pod};
|
use bytemuck::{bytes_of, bytes_of_mut, Pod};
|
||||||
use libc::{
|
use libc::{c_uint, makedev, mode_t, stat, EEXIST, ENOENT, F_OK, MS_BIND, MS_RDONLY, MS_REC, MS_REMOUNT, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
|
||||||
c_uint, makedev, mode_t, stat, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
|
||||||
O_RDWR, O_TRUNC, O_WRONLY,
|
|
||||||
};
|
|
||||||
use mem::MaybeUninit;
|
use mem::MaybeUninit;
|
||||||
use num_traits::AsPrimitive;
|
use num_traits::AsPrimitive;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
@ -185,6 +182,10 @@ impl FileAttr {
|
|||||||
pub fn is_socket(&self) -> bool {
|
pub fn is_socket(&self) -> bool {
|
||||||
self.is(libc::S_IFSOCK)
|
self.is(libc::S_IFSOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_whiteout(&self) -> bool {
|
||||||
|
self.is_char_device() && self.st.st_rdev == 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const XATTR_NAME_SELINUX: &CStr = c"security.selinux";
|
const XATTR_NAME_SELINUX: &CStr = c"security.selinux";
|
||||||
@ -199,7 +200,7 @@ impl FsPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(&self, flags: i32, mode: mode_t) -> io::Result<File> {
|
pub fn create(&self, flags: i32, mode: mode_t) -> io::Result<File> {
|
||||||
Ok(File::from(open_fd!(self, flags, mode)?))
|
Ok(File::from(open_fd!(self, flags | O_CREAT, mode)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exists(&self) -> bool {
|
pub fn exists(&self) -> bool {
|
||||||
@ -429,6 +430,18 @@ impl FsPath {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub fn bind_mount_to(&self, path: &FsPath, ro: bool) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
libc::mount(path.as_ptr(), self.as_ptr(), ptr::null(), MS_BIND | MS_REC, ptr::null()).as_os_err()?;
|
||||||
|
if ro {
|
||||||
|
libc::mount(ptr::null(), self.as_ptr(), ptr::null(), MS_REMOUNT | MS_BIND | MS_RDONLY, ptr::null()).as_os_err()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FsPathFollow {
|
impl FsPathFollow {
|
||||||
|
@ -55,7 +55,7 @@ impl<T> SilentResultExt<T> for Option<T> {
|
|||||||
pub trait ResultExt<T> {
|
pub trait ResultExt<T> {
|
||||||
fn log(self) -> LoggedResult<T>;
|
fn log(self) -> LoggedResult<T>;
|
||||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
|
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
|
||||||
fn log_ok(self);
|
fn log_ok(self) -> Option<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal C++ bridging logging routines
|
// Internal C++ bridging logging routines
|
||||||
@ -108,14 +108,14 @@ impl<T, R: Loggable<T>> ResultExt<T> for R {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
fn log_ok(self) {
|
fn log_ok(self) -> Option<T> {
|
||||||
self.log().ok();
|
self.log().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn log_ok(self) {
|
fn log_ok(self) -> Option<T> {
|
||||||
self.do_log(LogLevel::Error, Some(Location::caller())).ok();
|
self.do_log(LogLevel::Error, Some(Location::caller())).ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#![feature(fn_traits)]
|
#![feature(fn_traits)]
|
||||||
#![feature(unix_socket_ancillary_data)]
|
#![feature(unix_socket_ancillary_data)]
|
||||||
#![feature(unix_socket_peek)]
|
#![feature(unix_socket_peek)]
|
||||||
|
#![feature(btree_set_entry)]
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
|
||||||
use crate::ffi::SuRequest;
|
use crate::ffi::SuRequest;
|
||||||
@ -21,6 +22,7 @@ use std::mem::ManuallyDrop;
|
|||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::os::fd::FromRawFd;
|
use std::os::fd::FromRawFd;
|
||||||
use zygisk::zygisk_should_load_module;
|
use zygisk::zygisk_should_load_module;
|
||||||
|
use module::deploy_modules;
|
||||||
|
|
||||||
#[path = "../include/consts.rs"]
|
#[path = "../include/consts.rs"]
|
||||||
mod consts;
|
mod consts;
|
||||||
@ -33,6 +35,7 @@ mod resetprop;
|
|||||||
mod socket;
|
mod socket;
|
||||||
mod su;
|
mod su;
|
||||||
mod zygisk;
|
mod zygisk;
|
||||||
|
mod module;
|
||||||
|
|
||||||
#[allow(clippy::needless_lifetimes)]
|
#[allow(clippy::needless_lifetimes)]
|
||||||
#[cxx::bridge]
|
#[cxx::bridge]
|
||||||
@ -239,6 +242,7 @@ pub mod ffi {
|
|||||||
#[Self = MagiskD]
|
#[Self = MagiskD]
|
||||||
#[cxx_name = "Get"]
|
#[cxx_name = "Get"]
|
||||||
fn get() -> &'static MagiskD;
|
fn get() -> &'static MagiskD;
|
||||||
|
fn deploy_modules(module_list: &Vec<ModuleInfo>, zygisk_lib: &CxxString, magisk_path: &CxxString) -> bool;
|
||||||
}
|
}
|
||||||
unsafe extern "C++" {
|
unsafe extern "C++" {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -9,284 +9,9 @@
|
|||||||
#include <core.hpp>
|
#include <core.hpp>
|
||||||
#include <selinux.hpp>
|
#include <selinux.hpp>
|
||||||
|
|
||||||
#include "node.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from)
|
|
||||||
|
|
||||||
static int bind_mount(const char *reason, const char *from, const char *to) {
|
|
||||||
int ret = xmount(from, to, nullptr, MS_BIND | MS_REC, nullptr);
|
|
||||||
if (ret == 0)
|
|
||||||
VLOGD(reason, from, to);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************
|
|
||||||
* Node Tree Construction
|
|
||||||
*************************/
|
|
||||||
|
|
||||||
tmpfs_node::tmpfs_node(node_entry *node) : dir_node(node, this) {
|
|
||||||
if (!replace()) {
|
|
||||||
if (auto dir = open_dir(node_path().data())) {
|
|
||||||
set_exist(true);
|
|
||||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
|
||||||
// create a dummy inter_node to upgrade later
|
|
||||||
emplace<inter_node>(entry->d_name, entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = children.begin(); it != children.end(); ++it) {
|
|
||||||
// Upgrade resting inter_node children to tmpfs_node
|
|
||||||
if (isa<inter_node>(it->second))
|
|
||||||
it = upgrade<tmpfs_node>(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dir_node::prepare() {
|
|
||||||
// If direct replace or not exist, mount ourselves as tmpfs
|
|
||||||
bool upgrade_to_tmpfs = replace() || !exist();
|
|
||||||
|
|
||||||
for (auto it = children.begin(); it != children.end();) {
|
|
||||||
// We also need to upgrade to tmpfs node if any child:
|
|
||||||
// - Target does not exist
|
|
||||||
// - Source or target is a symlink (since we cannot bind mount symlink) or whiteout
|
|
||||||
bool cannot_mnt;
|
|
||||||
if (struct stat st{}; lstat(it->second->node_path().data(), &st) != 0) {
|
|
||||||
// if it's a whiteout, we don't care if the target doesn't exist
|
|
||||||
cannot_mnt = !it->second->is_wht();
|
|
||||||
} else {
|
|
||||||
it->second->set_exist(true);
|
|
||||||
cannot_mnt = it->second->is_lnk() || S_ISLNK(st.st_mode) || it->second->is_wht();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cannot_mnt) {
|
|
||||||
if (_node_type > type_id<tmpfs_node>()) {
|
|
||||||
// Upgrade will fail, remove the unsupported child node
|
|
||||||
LOGW("Unable to add: %s, skipped\n", it->second->node_path().data());
|
|
||||||
delete it->second;
|
|
||||||
it = children.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
upgrade_to_tmpfs = true;
|
|
||||||
}
|
|
||||||
if (auto dn = dyn_cast<dir_node>(it->second)) {
|
|
||||||
if (replace()) {
|
|
||||||
// Propagate skip mirror state to all children
|
|
||||||
dn->set_replace(true);
|
|
||||||
}
|
|
||||||
if (dn->prepare()) {
|
|
||||||
// Upgrade child to tmpfs
|
|
||||||
it = upgrade<tmpfs_node>(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
return upgrade_to_tmpfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dir_node::collect_module_files(std::string_view module, int dfd) {
|
|
||||||
auto dir = xopen_dir(xopenat(dfd, name().data(), O_RDONLY | O_CLOEXEC));
|
|
||||||
if (!dir)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
|
||||||
if (entry->d_name == ".replace"sv) {
|
|
||||||
set_replace(true);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->d_type == DT_DIR) {
|
|
||||||
inter_node *node;
|
|
||||||
if (auto it = children.find(entry->d_name); it == children.end()) {
|
|
||||||
node = emplace<inter_node>(entry->d_name, entry->d_name);
|
|
||||||
} else {
|
|
||||||
node = dyn_cast<inter_node>(it->second);
|
|
||||||
}
|
|
||||||
if (node) {
|
|
||||||
node->collect_module_files(module, dirfd(dir.get()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (entry->d_type == DT_CHR) {
|
|
||||||
struct stat st{};
|
|
||||||
int ret = fstatat(dirfd(dir.get()), entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
|
||||||
if (ret == 0 && st.st_rdev == 0) {
|
|
||||||
// if the file is a whiteout, mark it as such
|
|
||||||
entry->d_type = DT_WHT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
emplace<module_node>(entry->d_name, module, entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************
|
|
||||||
* Mount Implementations
|
|
||||||
************************/
|
|
||||||
|
|
||||||
void node_entry::create_and_mount(const char *reason, const string &src, bool ro) {
|
|
||||||
const string dest = isa<tmpfs_node>(parent()) ? worker_path() : node_path();
|
|
||||||
if (is_lnk()) {
|
|
||||||
VLOGD("cp_link", src.data(), dest.data());
|
|
||||||
cp_afc(src.data(), dest.data());
|
|
||||||
} else {
|
|
||||||
if (is_dir())
|
|
||||||
xmkdir(dest.data(), 0);
|
|
||||||
else if (is_reg())
|
|
||||||
close(xopen(dest.data(), O_RDONLY | O_CREAT | O_CLOEXEC, 0));
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
bind_mount(reason, src.data(), dest.data());
|
|
||||||
if (ro) {
|
|
||||||
xmount(nullptr, dest.data(), nullptr, MS_REMOUNT | MS_BIND | MS_RDONLY, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void module_node::mount() {
|
|
||||||
if (is_wht()) {
|
|
||||||
VLOGD("delete", "null", node_path().data());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string path{module.begin(), module.end()};
|
|
||||||
path += parent()->root()->prefix;
|
|
||||||
path += node_path();
|
|
||||||
string mnt_src = module_mnt + path;
|
|
||||||
{
|
|
||||||
string src = MODULEROOT "/" + path;
|
|
||||||
if (exist()) clone_attr(node_path().data(), src.data());
|
|
||||||
}
|
|
||||||
if (isa<tmpfs_node>(parent())) {
|
|
||||||
create_and_mount("module", mnt_src);
|
|
||||||
} else {
|
|
||||||
bind_mount("module", mnt_src.data(), node_path().data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tmpfs_node::mount() {
|
|
||||||
if (!is_dir()) {
|
|
||||||
create_and_mount("mirror", node_path());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isa<tmpfs_node>(parent())) {
|
|
||||||
auto worker_dir = worker_path();
|
|
||||||
mkdirs(worker_dir.data(), 0);
|
|
||||||
clone_attr(exist() ? node_path().data() : parent()->node_path().data(), worker_dir.data());
|
|
||||||
dir_node::mount();
|
|
||||||
bind_mount(replace() ? "replace" : "move", worker_dir.data(), node_path().data());
|
|
||||||
xmount(nullptr, node_path().data(), nullptr, MS_REMOUNT | MS_BIND | MS_RDONLY, nullptr);
|
|
||||||
} else {
|
|
||||||
const string dest = worker_path();
|
|
||||||
// We don't need another layer of tmpfs if parent is tmpfs
|
|
||||||
mkdir(dest.data(), 0);
|
|
||||||
clone_attr(exist() ? node_path().data() : parent()->worker_path().data(), dest.data());
|
|
||||||
dir_node::mount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************
|
|
||||||
* Magisk Stuffs
|
|
||||||
****************/
|
|
||||||
|
|
||||||
class magisk_node : public node_entry {
|
|
||||||
public:
|
|
||||||
explicit magisk_node(const char *name) : node_entry(name, DT_REG, this) {}
|
|
||||||
explicit magisk_node(const char *name, const char *target)
|
|
||||||
: node_entry(name, DT_LNK, this), target(target) {}
|
|
||||||
|
|
||||||
void mount() override {
|
|
||||||
if (target) {
|
|
||||||
string dest = isa<tmpfs_node>(parent()) ? worker_path() : node_path();
|
|
||||||
VLOGD("create", target, dest.data());
|
|
||||||
xsymlink(target, dest.data());
|
|
||||||
} else {
|
|
||||||
string src = get_magisk_tmp() + "/"s + name();
|
|
||||||
if (access(src.data(), F_OK) == 0)
|
|
||||||
create_and_mount("magisk", src, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const char *target = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class zygisk_node : public node_entry {
|
|
||||||
public:
|
|
||||||
explicit zygisk_node(const char *name, bool is64bit)
|
|
||||||
: node_entry(name, DT_REG, this), is64bit(is64bit) {}
|
|
||||||
|
|
||||||
void mount() override {
|
|
||||||
#if defined(__LP64__)
|
|
||||||
const string src = get_magisk_tmp() + "/magisk"s + (is64bit ? "" : "32");
|
|
||||||
#else
|
|
||||||
const string src = get_magisk_tmp() + "/magisk"s;
|
|
||||||
(void) is64bit;
|
|
||||||
#endif
|
|
||||||
if (access(src.data(), F_OK))
|
|
||||||
return;
|
|
||||||
create_and_mount("zygisk", src, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool is64bit;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void inject_magisk_bins(root_node *system) {
|
|
||||||
dir_node* bin = system->get_child<inter_node>("bin");
|
|
||||||
if (!bin) {
|
|
||||||
struct stat st{};
|
|
||||||
bin = system;
|
|
||||||
for (auto &item: split(getenv("PATH"), ":")) {
|
|
||||||
item.erase(0, item.starts_with("/system/") ? 8 : 1);
|
|
||||||
auto system_path = "/system/" + item;
|
|
||||||
if (stat(system_path.data(), &st) == 0 && st.st_mode & S_IXOTH) {
|
|
||||||
for (const auto &dir: split(item, "/")) {
|
|
||||||
auto node = bin->get_child<inter_node>(dir);
|
|
||||||
bin = node ? node : bin->emplace<inter_node>(dir, dir.data());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert binaries
|
|
||||||
bin->insert(new magisk_node("magisk"));
|
|
||||||
bin->insert(new magisk_node("magiskpolicy"));
|
|
||||||
|
|
||||||
// Also insert all applets to make sure no one can override it
|
|
||||||
for (int i = 0; applet_names[i]; ++i)
|
|
||||||
bin->insert(new magisk_node(applet_names[i], "./magisk"));
|
|
||||||
bin->insert(new magisk_node("supolicy", "./magiskpolicy"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void inject_zygisk_libs(root_node *system) {
|
|
||||||
if (access("/system/bin/linker", F_OK) == 0) {
|
|
||||||
auto lib = system->get_child<inter_node>("lib");
|
|
||||||
if (!lib) {
|
|
||||||
lib = new inter_node("lib");
|
|
||||||
system->insert(lib);
|
|
||||||
}
|
|
||||||
lib->insert(new zygisk_node(native_bridge.data(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access("/system/bin/linker64", F_OK) == 0) {
|
|
||||||
auto lib64 = system->get_child<inter_node>("lib64");
|
|
||||||
if (!lib64) {
|
|
||||||
lib64 = new inter_node("lib64");
|
|
||||||
system->insert(lib64);
|
|
||||||
}
|
|
||||||
lib64->insert(new zygisk_node(native_bridge.data(), true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void load_modules(bool zygisk_enabled, const rust::Vec<ModuleInfo> &module_list) {
|
static void load_modules(bool zygisk_enabled, const rust::Vec<ModuleInfo> &module_list) {
|
||||||
node_entry::module_mnt = get_magisk_tmp() + "/"s MODULEMNT "/";
|
|
||||||
|
|
||||||
auto root = make_unique<root_node>("");
|
|
||||||
auto system = new root_node("system");
|
|
||||||
root->insert(system);
|
|
||||||
|
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
LOGI("* Loading modules\n");
|
LOGI("* Loading modules\n");
|
||||||
for (const auto &m : module_list) {
|
for (const auto &m : module_list) {
|
||||||
@ -300,26 +25,19 @@ static void load_modules(bool zygisk_enabled, const rust::Vec<ModuleInfo> &modul
|
|||||||
// Do NOT go through property service as it could cause boot lock
|
// Do NOT go through property service as it could cause boot lock
|
||||||
load_prop_file(buf, true);
|
load_prop_file(buf, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether skip mounting
|
|
||||||
strcpy(b, "skip_mount");
|
|
||||||
if (access(buf, F_OK) == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Double check whether the system folder exists
|
|
||||||
strcpy(b, "system");
|
|
||||||
if (access(buf, F_OK) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
LOGI("%.*s: loading mount files\n", (int) m.name.size(), m.name.data());
|
|
||||||
b[-1] = '\0';
|
|
||||||
int fd = xopen(buf, O_RDONLY | O_CLOEXEC);
|
|
||||||
system->collect_module_files({ m.name.begin(), m.name.end() }, fd);
|
|
||||||
close(fd);
|
|
||||||
}
|
}
|
||||||
|
std::string magisk_path;
|
||||||
if (get_magisk_tmp() != "/sbin"sv || !str_contains(getenv("PATH") ?: "", "/sbin")) {
|
if (get_magisk_tmp() != "/sbin"sv || !str_contains(getenv("PATH") ?: "", "/sbin")) {
|
||||||
// Need to inject our binaries into /system/bin
|
// Need to inject our binaries into /system/bin
|
||||||
inject_magisk_bins(system);
|
magisk_path = "/system/bin";
|
||||||
|
for (struct stat st{}; auto &item: split(getenv("PATH"), ":")) {
|
||||||
|
item.erase(0, item.starts_with("/system/") ? 8 : 1);
|
||||||
|
auto &&system_path = "/system/"s + item;
|
||||||
|
if (stat(system_path.data(), &st) == 0 && st.st_mode & S_IXOTH) {
|
||||||
|
magisk_path = std::move(system_path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zygisk_enabled) {
|
if (zygisk_enabled) {
|
||||||
@ -335,23 +53,9 @@ static void load_modules(bool zygisk_enabled, const rust::Vec<ModuleInfo> &modul
|
|||||||
if (get_prop("ro.maple.enable") == "1") {
|
if (get_prop("ro.maple.enable") == "1") {
|
||||||
set_prop("ro.maple.enable", "0");
|
set_prop("ro.maple.enable", "0");
|
||||||
}
|
}
|
||||||
inject_zygisk_libs(system);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!system->is_empty()) {
|
deploy_modules(module_list, native_bridge, magisk_path);
|
||||||
// Handle special read-only partitions
|
|
||||||
for (const char *part : { "/vendor", "/product", "/system_ext" }) {
|
|
||||||
struct stat st{};
|
|
||||||
if (lstat(part, &st) == 0 && S_ISDIR(st.st_mode)) {
|
|
||||||
if (auto old = system->extract(part + 1)) {
|
|
||||||
auto new_node = new root_node(old);
|
|
||||||
root->insert(new_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
root->prepare();
|
|
||||||
root->mount();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************
|
/************************
|
||||||
|
548
native/src/core/module.rs
Normal file
548
native/src/core/module.rs
Normal file
@ -0,0 +1,548 @@
|
|||||||
|
use crate::consts::{MODULEMNT, WORKERDIR};
|
||||||
|
use crate::ffi::{ModuleInfo, get_magisk_tmp};
|
||||||
|
use base::{
|
||||||
|
Directory, FsPath, FsPathBuf, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, Utf8CString,
|
||||||
|
WalkResult::Continue, clone_attr, cstr, debug, error, info, libc::O_RDONLY, path, warn,
|
||||||
|
};
|
||||||
|
use cxx::CxxString;
|
||||||
|
use std::collections::{
|
||||||
|
BTreeMap, BTreeSet, btree_map::Entry as MapEntry, btree_set::Entry as SetEntry,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Node {
|
||||||
|
Dir {
|
||||||
|
children: BTreeMap<Utf8CString, Node>,
|
||||||
|
exist: bool,
|
||||||
|
replace: bool,
|
||||||
|
},
|
||||||
|
File {
|
||||||
|
src: Option<Utf8CString>,
|
||||||
|
is_link: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
fn mount(&mut self, dest: &Utf8CStr) -> LoggedResult<()> {
|
||||||
|
if let Node::Dir { exist, .. } = self {
|
||||||
|
*exist = true;
|
||||||
|
}
|
||||||
|
self.do_mount(dest, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_mount(&self, path: &Utf8CStr, parent_tmpfs: bool) -> LoggedResult<()> {
|
||||||
|
// todo: do mount
|
||||||
|
match &self {
|
||||||
|
Node::Dir {
|
||||||
|
children,
|
||||||
|
exist,
|
||||||
|
replace,
|
||||||
|
} => {
|
||||||
|
let mut worker = if !*exist || *replace {
|
||||||
|
let mut worker = get_magisk_tmp().to_owned();
|
||||||
|
worker.push_str("/");
|
||||||
|
worker.push_str(WORKERDIR);
|
||||||
|
let len = worker.len();
|
||||||
|
worker.push_str(path);
|
||||||
|
if *replace {
|
||||||
|
debug!("replace : {}", worker);
|
||||||
|
} else if parent_tmpfs {
|
||||||
|
debug!("mkdir : {}", worker);
|
||||||
|
} else {
|
||||||
|
debug!("tmpfs : {}", worker);
|
||||||
|
}
|
||||||
|
FsPath::from(&worker).mkdirs(0o000)?;
|
||||||
|
let mut buf = path.to_owned();
|
||||||
|
while !FsPath::from(&buf).exists() {
|
||||||
|
let mut tmp = Utf8CString::default();
|
||||||
|
FsPath::from(&buf).parent(&mut tmp);
|
||||||
|
buf = tmp.to_owned();
|
||||||
|
}
|
||||||
|
clone_attr(FsPath::from(&buf), FsPath::from(&worker)).ok();
|
||||||
|
worker.resize(len);
|
||||||
|
Some(worker)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = path.to_owned();
|
||||||
|
let len = buf.len();
|
||||||
|
for (name, child) in children {
|
||||||
|
buf.push_str("/");
|
||||||
|
buf.push_str(name);
|
||||||
|
child.do_mount(&buf, worker.is_some())?;
|
||||||
|
buf.resize(len);
|
||||||
|
}
|
||||||
|
if !*replace
|
||||||
|
&& let Some(worker) = &mut worker
|
||||||
|
&& let Ok(mut dest) = Directory::open(&buf)
|
||||||
|
{
|
||||||
|
let len = worker.len();
|
||||||
|
dest.pre_order_walk(|f| {
|
||||||
|
if let Ok(name) = Utf8CStr::from_cstr(f.name())
|
||||||
|
&& !children.contains_key(&name.to_owned())
|
||||||
|
{
|
||||||
|
f.path(&mut buf)?;
|
||||||
|
worker.push_str(&buf);
|
||||||
|
if f.is_dir() {
|
||||||
|
debug!("mkdir : {}", &worker);
|
||||||
|
let worker = FsPath::from(worker);
|
||||||
|
worker.mkdir(0o00)?;
|
||||||
|
clone_attr(FsPath::from(&buf), worker)?;
|
||||||
|
} else if f.is_symlink() {
|
||||||
|
debug!("cp_link : {} <- {}", &worker, buf);
|
||||||
|
let attr = f.get_attr()?;
|
||||||
|
let mut link = Utf8CString::default();
|
||||||
|
f.read_link(&mut link)?;
|
||||||
|
let worker = FsPath::from(worker);
|
||||||
|
FsPath::from(&link).symlink_to(worker)?;
|
||||||
|
worker.set_attr(&attr)?;
|
||||||
|
} else {
|
||||||
|
debug!("mirror : {} <- {}", &worker, buf);
|
||||||
|
let worker = FsPath::from(worker);
|
||||||
|
worker.create(O_RDONLY, 0o000)?;
|
||||||
|
worker.bind_mount_to(FsPath::from(&buf), true)?;
|
||||||
|
}
|
||||||
|
worker.resize(len);
|
||||||
|
};
|
||||||
|
Ok(Continue)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
if !parent_tmpfs && let Some(mut worker) = worker {
|
||||||
|
worker.push_str(path);
|
||||||
|
debug!("move : {} <- {}", path, worker);
|
||||||
|
FsPath::from(path).bind_mount_to(FsPath::from(&worker), true)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node::File { src, is_link } => match (src, is_link) {
|
||||||
|
(None, _) => {
|
||||||
|
debug!("delete : {}", path);
|
||||||
|
}
|
||||||
|
(Some(src), false) => {
|
||||||
|
if parent_tmpfs {
|
||||||
|
let mut worker = get_magisk_tmp().to_owned();
|
||||||
|
worker.push_str("/");
|
||||||
|
worker.push_str(WORKERDIR);
|
||||||
|
worker.push_str(path);
|
||||||
|
debug!("module : {} <- {}", worker, src);
|
||||||
|
let worker = FsPath::from(&worker);
|
||||||
|
worker.create(O_RDONLY, 0o000)?;
|
||||||
|
worker.bind_mount_to(FsPath::from(src), true)?;
|
||||||
|
} else {
|
||||||
|
debug!("module : {} <- {}", path, src);
|
||||||
|
let src = FsPath::from(src);
|
||||||
|
let path = FsPath::from(path);
|
||||||
|
clone_attr(path, src).ok();
|
||||||
|
path.bind_mount_to(src, true)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(src), true) => {
|
||||||
|
if parent_tmpfs {
|
||||||
|
let mut worker = get_magisk_tmp().to_owned();
|
||||||
|
worker.push_str("/");
|
||||||
|
worker.push_str(WORKERDIR);
|
||||||
|
worker.push_str(path);
|
||||||
|
debug!("symlink : {} <- {}", worker, src);
|
||||||
|
FsPath::from(src).symlink_to(FsPath::from(&worker))?;
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract(&mut self, path: &Utf8CStr) -> Option<Node> {
|
||||||
|
let path = path.to_owned();
|
||||||
|
match self {
|
||||||
|
Node::Dir { children, .. } => children.remove_entry(&path).map(|(_, v)| v),
|
||||||
|
Node::File { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ModuleEntry {
|
||||||
|
Dir {
|
||||||
|
name: Utf8CString,
|
||||||
|
iter: ModuleIterator,
|
||||||
|
},
|
||||||
|
File {
|
||||||
|
name: Utf8CString,
|
||||||
|
path: Option<Utf8CString>,
|
||||||
|
is_link: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CustomModule {
|
||||||
|
Dir {
|
||||||
|
children: BTreeMap<Utf8CString, CustomModule>,
|
||||||
|
},
|
||||||
|
File {
|
||||||
|
path: Utf8CString,
|
||||||
|
is_link: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
trait CustomModuleExt {
|
||||||
|
fn insert_entry(&mut self, path: &Utf8CStr, target: &Utf8CStr, is_link: bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomModuleExt for BTreeMap<Utf8CString, CustomModule> {
|
||||||
|
fn insert_entry(&mut self, path: &Utf8CStr, target: &Utf8CStr, is_link: bool) {
|
||||||
|
match path.split_once('/') {
|
||||||
|
Some((dir, rest)) => {
|
||||||
|
if dir.is_empty() {
|
||||||
|
return self.insert_entry(&Utf8CString::from(rest.to_owned()), target, is_link);
|
||||||
|
}
|
||||||
|
match self
|
||||||
|
.entry(Utf8CString::from(dir.to_owned()))
|
||||||
|
.or_insert_with(|| CustomModule::Dir {
|
||||||
|
children: BTreeMap::new(),
|
||||||
|
}) {
|
||||||
|
CustomModule::Dir { children } => {
|
||||||
|
children.insert_entry(&Utf8CString::from(rest.to_owned()), target, is_link);
|
||||||
|
}
|
||||||
|
CustomModule::File { .. } => {
|
||||||
|
warn!("Duplicate entry: {}", dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => match self.entry(path.to_owned()) {
|
||||||
|
MapEntry::Vacant(v) => {
|
||||||
|
v.insert(CustomModule::File {
|
||||||
|
path: target.to_owned(),
|
||||||
|
is_link,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!("Duplicate entry: {}", path);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait DirExt {
|
||||||
|
fn open_dir(&mut self, name: &Utf8CStr) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirExt for Vec<Directory> {
|
||||||
|
fn open_dir(&mut self, name: &Utf8CStr) -> Self {
|
||||||
|
self.iter()
|
||||||
|
.filter_map(|d| d.open_dir(name.as_cstr()).ok())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirExt for Vec<CustomModule> {
|
||||||
|
fn open_dir(&mut self, name: &Utf8CStr) -> Self {
|
||||||
|
let name = name.to_owned();
|
||||||
|
self.iter_mut()
|
||||||
|
.filter_map(|d| {
|
||||||
|
if let CustomModule::Dir { children } = d {
|
||||||
|
children.remove(&name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModuleIterator {
|
||||||
|
modules: Vec<Directory>,
|
||||||
|
customs: Vec<CustomModule>,
|
||||||
|
collected: BTreeSet<Utf8CString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for ModuleIterator {
|
||||||
|
type Item = ModuleEntry;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(e) = self.modules.last_mut().and_then(|d| d.read().log_ok()) {
|
||||||
|
let res: Option<Self::Item> = try {
|
||||||
|
let e = e?;
|
||||||
|
let name = Utf8CStr::from_cstr(e.name()).log_ok()?;
|
||||||
|
if let SetEntry::Vacant(v) = self.collected.entry(name.to_owned()) {
|
||||||
|
let mut path = Utf8CString::default();
|
||||||
|
let attr = e.get_attr().log_ok()?;
|
||||||
|
if attr.is_symlink() {
|
||||||
|
e.read_link(&mut path).log_ok()?;
|
||||||
|
} else {
|
||||||
|
e.path(&mut path).log_ok()?;
|
||||||
|
}
|
||||||
|
let entry = if e.is_dir() {
|
||||||
|
ModuleEntry::Dir {
|
||||||
|
name: name.to_owned(),
|
||||||
|
iter: ModuleIterator {
|
||||||
|
modules: self.modules.open_dir(v.get()),
|
||||||
|
customs: self.customs.open_dir(v.get()),
|
||||||
|
collected: BTreeSet::new(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ModuleEntry::File {
|
||||||
|
name: name.to_owned(),
|
||||||
|
path: Some(path).take_if(|_| !attr.is_whiteout()),
|
||||||
|
is_link: attr.is_symlink(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
v.insert();
|
||||||
|
entry
|
||||||
|
} else if !e.is_dir() {
|
||||||
|
warn!("Duplicate entry: {}", name);
|
||||||
|
None?
|
||||||
|
} else {
|
||||||
|
None?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if res.is_some() {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.modules.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(CustomModule::Dir { children }) = self.customs.last_mut() {
|
||||||
|
let res: Option<Self::Item> = try {
|
||||||
|
let e = children.first_entry()?;
|
||||||
|
|
||||||
|
let name: &Utf8CStr = e.key();
|
||||||
|
|
||||||
|
if let SetEntry::Vacant(v) = self.collected.entry(name.to_owned()) {
|
||||||
|
let entry = match e.get() {
|
||||||
|
CustomModule::Dir { .. } => ModuleEntry::Dir {
|
||||||
|
name: name.to_owned(),
|
||||||
|
iter: ModuleIterator {
|
||||||
|
modules: vec![],
|
||||||
|
customs: self.customs.open_dir(v.get()),
|
||||||
|
collected: BTreeSet::new(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CustomModule::File { .. } => {
|
||||||
|
if let (name, CustomModule::File { path, is_link }) = e.remove_entry() {
|
||||||
|
ModuleEntry::File {
|
||||||
|
name: name.to_owned(),
|
||||||
|
path: Some(path),
|
||||||
|
is_link,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
v.insert();
|
||||||
|
entry
|
||||||
|
} else if matches!(e.get(), CustomModule::File { .. }) {
|
||||||
|
error!("Duplicate entry: {}", name);
|
||||||
|
None?
|
||||||
|
} else {
|
||||||
|
None?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if res.is_some() {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.customs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModuleList {
|
||||||
|
modules: Vec<Directory>,
|
||||||
|
customs: Vec<CustomModule>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleList {
|
||||||
|
fn from_module_infos(module_infos: &[ModuleInfo]) -> Self {
|
||||||
|
let mut path = FsPathBuf::default().join(get_magisk_tmp()).join(MODULEMNT);
|
||||||
|
|
||||||
|
let len = path.len();
|
||||||
|
let mut modules = Vec::new();
|
||||||
|
for info in module_infos {
|
||||||
|
path = path.join(&info.name);
|
||||||
|
if let Ok(m) = Directory::open(&path) {
|
||||||
|
if !m.contains_path(c"skip_mount") && m.contains_path(c"system") {
|
||||||
|
info!("{}: loading mount files", info.name);
|
||||||
|
modules.push(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path = path.resize(len);
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
modules,
|
||||||
|
customs: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inject_zygisk_bins(&mut self, zygisk_lib: &str) {
|
||||||
|
self.customs.push(CustomModule::Dir {
|
||||||
|
children: {
|
||||||
|
let mut children = BTreeMap::new();
|
||||||
|
let mut path = FsPathBuf::default();
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
{
|
||||||
|
if path!("/system/bin/linker").exists() {
|
||||||
|
path = path.join("/system/lib").join(zygisk_lib);
|
||||||
|
children.insert_entry(
|
||||||
|
&path,
|
||||||
|
&FsPathBuf::default().join(get_magisk_tmp()).join("magisk32"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if path!("/system/bin/linker64").exists() {
|
||||||
|
path = path.join("/system/lib64").join(zygisk_lib);
|
||||||
|
children.insert_entry(
|
||||||
|
&path,
|
||||||
|
&FsPathBuf::default().join(get_magisk_tmp()).join("magisk"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
{
|
||||||
|
path = path.join("/system/lib").join(zygisk_lib);
|
||||||
|
if path!("/system/bin/linker").exists() {
|
||||||
|
children.insert_entry(
|
||||||
|
&path,
|
||||||
|
&FsPathBuf::default().join(get_magisk_tmp()).join("magisk"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
children
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inject_magisk_bins(&mut self, magisk_path: &str) {
|
||||||
|
let mut path = Utf8CString::default();
|
||||||
|
path.push_str(magisk_path);
|
||||||
|
path.push_str("/");
|
||||||
|
let len = path.len();
|
||||||
|
self.customs.push(CustomModule::Dir {
|
||||||
|
children: {
|
||||||
|
let mut children = BTreeMap::new();
|
||||||
|
|
||||||
|
path.push_str("magisk");
|
||||||
|
children.insert_entry(
|
||||||
|
&path,
|
||||||
|
&FsPathBuf::default().join(get_magisk_tmp()).join("magisk"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
path.resize(len);
|
||||||
|
path.push_str("magiskpolicy");
|
||||||
|
children.insert_entry(
|
||||||
|
&path,
|
||||||
|
&FsPathBuf::default()
|
||||||
|
.join(get_magisk_tmp())
|
||||||
|
.join("magiskpolicy"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
path.resize(len);
|
||||||
|
path.push_str("su");
|
||||||
|
children.insert_entry(&path, cstr!("./magisk"), true);
|
||||||
|
path.resize(len);
|
||||||
|
path.push_str("resetprop");
|
||||||
|
children.insert_entry(&path, cstr!("./magisk"), true);
|
||||||
|
path.resize(len);
|
||||||
|
path.push_str("supolicy");
|
||||||
|
children.insert_entry(&path, cstr!("./magiskpolicy"), true);
|
||||||
|
|
||||||
|
children
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter(&mut self, name: &Utf8CStr) -> ModuleIterator {
|
||||||
|
ModuleIterator {
|
||||||
|
modules: self.modules.open_dir(name),
|
||||||
|
customs: self.customs.open_dir(name),
|
||||||
|
collected: BTreeSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deploy_modules(
|
||||||
|
module_list: &[ModuleInfo],
|
||||||
|
zygisk_lib: &CxxString,
|
||||||
|
magisk_path: &CxxString,
|
||||||
|
) -> bool {
|
||||||
|
let res: LoggedResult<()> = try {
|
||||||
|
let mut modules = ModuleList::from_module_infos(module_list);
|
||||||
|
if !magisk_path.is_empty() {
|
||||||
|
modules.inject_magisk_bins(&magisk_path.to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
|
if zygisk_lib != "0" {
|
||||||
|
modules.inject_zygisk_bins(&zygisk_lib.to_string_lossy());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut system_node = tree_module(path!("/system"), &mut modules.iter(cstr!("system")))?;
|
||||||
|
for dir in [cstr!("product"), cstr!("vendor"), cstr!("system_ext")] {
|
||||||
|
system_node.extract(dir).and_then(|mut n| {
|
||||||
|
let mut dest = cstr!("/").to_owned();
|
||||||
|
dest.push_str(dir);
|
||||||
|
n.mount(&dest).log_ok()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
system_node.mount(cstr!("/system"))?;
|
||||||
|
};
|
||||||
|
res.log().is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tree_module(base: &FsPath, dir: &mut ModuleIterator) -> LoggedResult<Node> {
|
||||||
|
let mut children = BTreeMap::new();
|
||||||
|
let mut target_path = FsPathBuf::default().join(base);
|
||||||
|
let mut replace = false;
|
||||||
|
let mut exist = target_path.exists();
|
||||||
|
|
||||||
|
debug!("tree on {}", target_path);
|
||||||
|
|
||||||
|
let parent_len = target_path.len();
|
||||||
|
for e in dir {
|
||||||
|
match e {
|
||||||
|
ModuleEntry::Dir { name, mut iter } => {
|
||||||
|
target_path = target_path.join(&name);
|
||||||
|
exist = exist && target_path.exists();
|
||||||
|
children.insert(name, tree_module(&target_path, &mut iter)?);
|
||||||
|
}
|
||||||
|
ModuleEntry::File {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
is_link,
|
||||||
|
} => {
|
||||||
|
if &name == ".replace" {
|
||||||
|
replace = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
target_path = target_path.join(&name);
|
||||||
|
|
||||||
|
if !matches!(
|
||||||
|
(
|
||||||
|
is_link,
|
||||||
|
&path,
|
||||||
|
target_path.get_attr().map(|attr| attr.is_symlink())
|
||||||
|
),
|
||||||
|
(false, Some(_), Ok(false))
|
||||||
|
) {
|
||||||
|
exist = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
children.insert(name, Node::File { src: path, is_link });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target_path = target_path.resize(parent_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Node::Dir {
|
||||||
|
children,
|
||||||
|
exist,
|
||||||
|
replace,
|
||||||
|
})
|
||||||
|
}
|
@ -1,318 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define TYPE_INTER (1 << 0) /* intermediate node */
|
|
||||||
#define TYPE_TMPFS (1 << 1) /* replace with tmpfs */
|
|
||||||
#define TYPE_MODULE (1 << 2) /* mount from module */
|
|
||||||
#define TYPE_ROOT (1 << 3) /* partition root */
|
|
||||||
#define TYPE_CUSTOM (1 << 4) /* custom node type overrides all */
|
|
||||||
#define TYPE_DIR (TYPE_INTER|TYPE_TMPFS|TYPE_ROOT)
|
|
||||||
|
|
||||||
class node_entry;
|
|
||||||
class dir_node;
|
|
||||||
class inter_node;
|
|
||||||
class tmpfs_node;
|
|
||||||
class module_node;
|
|
||||||
class root_node;
|
|
||||||
|
|
||||||
// Poor man's dynamic cast without RTTI
|
|
||||||
template<class T> static bool isa(node_entry *node);
|
|
||||||
template<class T> static T *dyn_cast(node_entry *node);
|
|
||||||
template<class T> uint8_t type_id() { return TYPE_CUSTOM; }
|
|
||||||
template<> uint8_t type_id<dir_node>() { return TYPE_DIR; }
|
|
||||||
template<> uint8_t type_id<inter_node>() { return TYPE_INTER; }
|
|
||||||
template<> uint8_t type_id<tmpfs_node>() { return TYPE_TMPFS; }
|
|
||||||
template<> uint8_t type_id<module_node>() { return TYPE_MODULE; }
|
|
||||||
template<> uint8_t type_id<root_node>() { return TYPE_ROOT; }
|
|
||||||
|
|
||||||
class node_entry {
|
|
||||||
public:
|
|
||||||
virtual ~node_entry() = default;
|
|
||||||
|
|
||||||
// Node info
|
|
||||||
bool is_dir() const { return file_type() == DT_DIR; }
|
|
||||||
bool is_lnk() const { return file_type() == DT_LNK; }
|
|
||||||
bool is_reg() const { return file_type() == DT_REG; }
|
|
||||||
bool is_wht() const { return file_type() == DT_WHT; }
|
|
||||||
const string &name() const { return _name; }
|
|
||||||
dir_node *parent() const { return _parent; }
|
|
||||||
|
|
||||||
// Don't call the following two functions before prepare
|
|
||||||
const string &node_path();
|
|
||||||
const string worker_path();
|
|
||||||
|
|
||||||
virtual void mount() = 0;
|
|
||||||
|
|
||||||
inline static string module_mnt;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template<class T>
|
|
||||||
node_entry(const char *name, uint8_t file_type, T*)
|
|
||||||
: _name(name), _file_type(file_type & 15), _node_type(type_id<T>()) {}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
explicit node_entry(T*) : _file_type(0), _node_type(type_id<T>()) {}
|
|
||||||
|
|
||||||
virtual void consume(node_entry *other) {
|
|
||||||
_name.swap(other->_name);
|
|
||||||
_file_type = other->_file_type;
|
|
||||||
_parent = other->_parent;
|
|
||||||
delete other;
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_and_mount(const char *reason, const string &src, bool ro=false);
|
|
||||||
|
|
||||||
// Use bit 7 of _file_type for exist status
|
|
||||||
bool exist() const { return static_cast<bool>(_file_type & (1 << 7)); }
|
|
||||||
void set_exist(bool b) { if (b) _file_type |= (1 << 7); else _file_type &= ~(1 << 7); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class dir_node;
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
friend bool isa(node_entry *node);
|
|
||||||
|
|
||||||
uint8_t file_type() const { return static_cast<uint8_t>(_file_type & 15); }
|
|
||||||
|
|
||||||
// Node properties
|
|
||||||
string _name;
|
|
||||||
dir_node *_parent = nullptr;
|
|
||||||
|
|
||||||
// Cache, it should only be used within prepare
|
|
||||||
string _node_path;
|
|
||||||
|
|
||||||
uint8_t _file_type;
|
|
||||||
const uint8_t _node_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
class dir_node : public node_entry {
|
|
||||||
public:
|
|
||||||
using map_type = map<string_view, node_entry *>;
|
|
||||||
using iterator = map_type::iterator;
|
|
||||||
|
|
||||||
~dir_node() override {
|
|
||||||
for (auto &it : children)
|
|
||||||
delete it.second;
|
|
||||||
children.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************
|
|
||||||
* Entrypoints
|
|
||||||
**************/
|
|
||||||
|
|
||||||
// Traverse through module directories to generate a tree of module files
|
|
||||||
void collect_module_files(std::string_view module, int dfd);
|
|
||||||
|
|
||||||
// Traverse through the real filesystem and prepare the tree for magic mount.
|
|
||||||
// Return true to indicate that this node needs to be upgraded to tmpfs_node.
|
|
||||||
bool prepare();
|
|
||||||
|
|
||||||
// Default directory mount logic
|
|
||||||
void mount() override {
|
|
||||||
for (auto &pair : children)
|
|
||||||
pair.second->mount();
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************
|
|
||||||
* Tree Methods
|
|
||||||
***************/
|
|
||||||
|
|
||||||
bool is_empty() { return children.empty(); }
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
T *get_child(string_view name) { return iterator_to_node<T>(children.find(name)); }
|
|
||||||
|
|
||||||
root_node *root() {
|
|
||||||
if (!_root)
|
|
||||||
_root = _parent->root();
|
|
||||||
return _root;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return child with name or nullptr
|
|
||||||
node_entry *extract(string_view name) {
|
|
||||||
auto it = children.find(name);
|
|
||||||
if (it != children.end()) {
|
|
||||||
auto ret = it->second;
|
|
||||||
children.erase(it);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return false if rejected
|
|
||||||
bool insert(node_entry *node) {
|
|
||||||
auto fn = [=](auto) { return node; };
|
|
||||||
return node && iterator_to_node(insert(node->_name, node->_node_type, fn));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return inserted node or null if rejected
|
|
||||||
template<class T, class ...Args>
|
|
||||||
T *emplace(string_view name, Args &&...args) {
|
|
||||||
auto fn = [&](auto) { return new T(std::forward<Args>(args)...); };
|
|
||||||
return iterator_to_node<T>(insert(name, type_id<T>(), fn));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return upgraded node or null if rejected
|
|
||||||
template<class T, class ...Args>
|
|
||||||
T *upgrade(string_view name, Args &...args) {
|
|
||||||
return iterator_to_node<T>(upgrade<T>(children.find(name), args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template<class T>
|
|
||||||
dir_node(const char *name, T *self) : node_entry(name, DT_DIR, self) {
|
|
||||||
if constexpr (std::is_same_v<T, root_node>)
|
|
||||||
_root = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
dir_node(dirent *entry, T *self) : node_entry(entry->d_name, entry->d_type, self) {
|
|
||||||
if constexpr (std::is_same_v<T, root_node>)
|
|
||||||
_root = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
dir_node(node_entry *node, T *self) : node_entry(self) {
|
|
||||||
if constexpr (std::is_same_v<T, root_node>)
|
|
||||||
_root = self;
|
|
||||||
dir_node::consume(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
void consume(node_entry *other) override {
|
|
||||||
if (auto o = dyn_cast<dir_node>(other)) {
|
|
||||||
children.merge(o->children);
|
|
||||||
for (auto &pair : children)
|
|
||||||
pair.second->_parent = this;
|
|
||||||
}
|
|
||||||
node_entry::consume(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use bit 6 of _file_type
|
|
||||||
// Skip binding mirror for this directory
|
|
||||||
bool replace() const { return static_cast<bool>(_file_type & (1 << 6)); }
|
|
||||||
void set_replace(bool b) { if (b) _file_type |= (1 << 6); else _file_type &= ~(1 << 6); }
|
|
||||||
|
|
||||||
template<class T = node_entry>
|
|
||||||
T *iterator_to_node(iterator it) {
|
|
||||||
return static_cast<T*>(it == children.end() ? nullptr : it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Builder>
|
|
||||||
iterator insert(string_view name, uint8_t type, const Builder &builder) {
|
|
||||||
return insert_at(children.find(name), type, builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emplace insert a new node, or upgrade if the requested type has a higher rank.
|
|
||||||
// Return iterator to the new/upgraded node, or end() if insertion is rejected.
|
|
||||||
// fn is the node builder function. Signature: (node_entry *&) -> node_entry *
|
|
||||||
// fn gets a reference to the existing node pointer and returns a new node object.
|
|
||||||
// Input is null when there is no existing node. If returns null, the insertion is rejected.
|
|
||||||
// If fn consumes the input, it should set the reference to null.
|
|
||||||
template<typename Builder>
|
|
||||||
iterator insert_at(iterator it, uint8_t type, const Builder &builder) {
|
|
||||||
node_entry *node = nullptr;
|
|
||||||
if (it != children.end()) {
|
|
||||||
// Upgrade existing node only if higher rank
|
|
||||||
if (it->second->_node_type < type) {
|
|
||||||
node = builder(it->second);
|
|
||||||
if (!node)
|
|
||||||
return children.end();
|
|
||||||
if (it->second)
|
|
||||||
node->consume(it->second);
|
|
||||||
it = children.erase(it);
|
|
||||||
// Minor optimization to make insert O(1) by using hint
|
|
||||||
if (it == children.begin())
|
|
||||||
it = children.emplace(node->_name, node).first;
|
|
||||||
else
|
|
||||||
it = children.emplace_hint(--it, node->_name, node);
|
|
||||||
} else {
|
|
||||||
return children.end();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node = builder(node);
|
|
||||||
if (!node)
|
|
||||||
return children.end();
|
|
||||||
node->_parent = this;
|
|
||||||
it = children.emplace(node->_name, node).first;
|
|
||||||
}
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T, class ...Args>
|
|
||||||
iterator upgrade(iterator it, Args &&...args) {
|
|
||||||
return insert_at(it, type_id<T>(), [&](node_entry *&ex) -> node_entry * {
|
|
||||||
if (!ex) return nullptr;
|
|
||||||
auto node = new T(ex, std::forward<Args>(args)...);
|
|
||||||
ex = nullptr;
|
|
||||||
return node;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// dir nodes host children
|
|
||||||
map_type children;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Root node lookup cache
|
|
||||||
root_node *_root = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class root_node : public dir_node {
|
|
||||||
public:
|
|
||||||
explicit root_node(const char *name) : dir_node(name, this), prefix("") {
|
|
||||||
set_exist(true);
|
|
||||||
}
|
|
||||||
explicit root_node(node_entry *node) : dir_node(node, this), prefix("/system") {
|
|
||||||
set_exist(true);
|
|
||||||
}
|
|
||||||
const char * const prefix;
|
|
||||||
};
|
|
||||||
|
|
||||||
class inter_node : public dir_node {
|
|
||||||
public:
|
|
||||||
inter_node(const char *name) : dir_node(name, this) {}
|
|
||||||
inter_node(dirent *entry) : dir_node(entry, this) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class module_node : public node_entry {
|
|
||||||
public:
|
|
||||||
module_node(std::string_view module, dirent *entry)
|
|
||||||
: node_entry(entry->d_name, entry->d_type, this), module(module) {}
|
|
||||||
|
|
||||||
module_node(node_entry *node, std::string_view module) : node_entry(this), module(module) {
|
|
||||||
node_entry::consume(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mount() override;
|
|
||||||
private:
|
|
||||||
std::string_view module;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Don't create tmpfs_node before prepare
|
|
||||||
class tmpfs_node : public dir_node {
|
|
||||||
public:
|
|
||||||
explicit tmpfs_node(node_entry *node);
|
|
||||||
void mount() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
static bool isa(node_entry *node) {
|
|
||||||
return node && (node->_node_type & type_id<T>());
|
|
||||||
}
|
|
||||||
template<class T>
|
|
||||||
static T *dyn_cast(node_entry *node) {
|
|
||||||
return isa<T>(node) ? static_cast<T*>(node) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const string &node_entry::node_path() {
|
|
||||||
if (_parent && _node_path.empty())
|
|
||||||
_node_path = _parent->node_path() + '/' + _name;
|
|
||||||
return _node_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const string node_entry::worker_path() {
|
|
||||||
return get_magisk_tmp() + "/"s WORKERDIR + node_path();
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user