mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-11 12:57:27 +00:00
Migrate disable/remove modules to Rust
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOTOVL, SECURE_DIR};
|
use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOTOVL, SECURE_DIR};
|
||||||
use crate::db::Sqlite3;
|
use crate::db::Sqlite3;
|
||||||
use crate::ffi::{
|
use crate::ffi::{
|
||||||
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, disable_modules, exec_common_scripts,
|
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, exec_common_scripts, exec_module_scripts,
|
||||||
exec_module_scripts, get_magisk_tmp, initialize_denylist, setup_magisk_env,
|
get_magisk_tmp, get_prop, initialize_denylist, set_prop, setup_magisk_env,
|
||||||
};
|
};
|
||||||
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
|
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
|
||||||
|
use crate::module::disable_modules;
|
||||||
use crate::mount::{clean_mounts, setup_preinit_dir};
|
use crate::mount::{clean_mounts, setup_preinit_dir};
|
||||||
use crate::package::ManagerInfo;
|
use crate::package::ManagerInfo;
|
||||||
use crate::selinux::restore_tmpcon;
|
use crate::selinux::restore_tmpcon;
|
||||||
use crate::su::SuInfo;
|
use crate::su::SuInfo;
|
||||||
use crate::{get_prop, set_prop};
|
|
||||||
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
|
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
|
||||||
use base::{
|
use base::{
|
||||||
AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, error, info, libc,
|
AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, error, info, libc,
|
||||||
|
@@ -92,10 +92,6 @@ void exec_task(std::function<void()> &&task);
|
|||||||
// Daemon handlers
|
// Daemon handlers
|
||||||
void denylist_handler(int client, const sock_cred *cred);
|
void denylist_handler(int client, const sock_cred *cred);
|
||||||
|
|
||||||
// Module stuffs
|
|
||||||
void disable_modules();
|
|
||||||
void remove_modules();
|
|
||||||
|
|
||||||
// Scripting
|
// Scripting
|
||||||
void install_apk(rust::Utf8CStr apk);
|
void install_apk(rust::Utf8CStr apk);
|
||||||
void uninstall_pkg(rust::Utf8CStr pkg);
|
void uninstall_pkg(rust::Utf8CStr pkg);
|
||||||
@@ -125,3 +121,4 @@ static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
|
|||||||
static inline rust::String resolve_preinit_dir_rs(rust::Utf8CStr base_dir) {
|
static inline rust::String resolve_preinit_dir_rs(rust::Utf8CStr base_dir) {
|
||||||
return resolve_preinit_dir(base_dir.c_str());
|
return resolve_preinit_dir(base_dir.c_str());
|
||||||
}
|
}
|
||||||
|
static inline void exec_script_rs(rust::Utf8CStr script) { exec_script(script.data()); }
|
||||||
|
@@ -20,12 +20,19 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// System properties
|
// System properties
|
||||||
rust::String get_prop_rs(const char *name, bool persist);
|
|
||||||
std::string get_prop(const char *name, bool persist = false);
|
std::string get_prop(const char *name, bool persist = false);
|
||||||
int delete_prop(const char *name, bool persist = false);
|
int delete_prop(const char *name, bool persist = false);
|
||||||
int set_prop(const char *name, const char *value, bool skip_svc = false);
|
int set_prop(const char *name, const char *value, bool skip_svc = false);
|
||||||
void load_prop_file(const char *filename, bool skip_svc = false);
|
void load_prop_file(const char *filename, bool skip_svc = false);
|
||||||
|
|
||||||
static inline void prop_cb_exec(prop_cb &cb, const char *name, const char *value, uint32_t serial) {
|
// Rust bindings
|
||||||
cb.exec(name, value, serial);
|
rust::String get_prop_rs(rust::Utf8CStr name, bool persist);
|
||||||
|
static inline int set_prop_rs(rust::Utf8CStr name, rust::Utf8CStr value, bool skip_svc) {
|
||||||
|
return set_prop(name.data(), value.data(), skip_svc);
|
||||||
|
}
|
||||||
|
static inline void load_prop_file_rs(rust::Utf8CStr filename, bool skip_svc) {
|
||||||
|
load_prop_file(filename.data(), skip_svc);
|
||||||
|
}
|
||||||
|
static inline void prop_cb_exec(prop_cb &cb, rust::Utf8CStr name, rust::Utf8CStr value, uint32_t serial) {
|
||||||
|
cb.exec(name.data(), value.data(), serial);
|
||||||
}
|
}
|
@@ -7,11 +7,12 @@
|
|||||||
|
|
||||||
use crate::ffi::SuRequest;
|
use crate::ffi::SuRequest;
|
||||||
use crate::socket::Encodable;
|
use crate::socket::Encodable;
|
||||||
use base::{Utf8CStr, libc};
|
use base::libc;
|
||||||
use cxx::{ExternType, type_id};
|
use cxx::{ExternType, type_id};
|
||||||
use daemon::{MagiskD, daemon_entry};
|
use daemon::{MagiskD, daemon_entry};
|
||||||
use derive::Decodable;
|
use derive::Decodable;
|
||||||
use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
|
use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
|
||||||
|
use module::remove_modules;
|
||||||
use mount::{find_preinit_device, revert_unmount};
|
use mount::{find_preinit_device, revert_unmount};
|
||||||
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
||||||
use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon};
|
use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon};
|
||||||
@@ -133,23 +134,6 @@ pub mod ffi {
|
|||||||
request: &'a SuRequest,
|
request: &'a SuRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C++" {
|
|
||||||
include!("include/resetprop.hpp");
|
|
||||||
|
|
||||||
#[cxx_name = "prop_cb"]
|
|
||||||
type PropCb;
|
|
||||||
unsafe fn get_prop_rs(name: *const c_char, persist: bool) -> String;
|
|
||||||
#[cxx_name = "set_prop"]
|
|
||||||
unsafe fn set_prop_rs(name: *const c_char, value: *const c_char, skip_svc: bool) -> i32;
|
|
||||||
unsafe fn prop_cb_exec(
|
|
||||||
cb: Pin<&mut PropCb>,
|
|
||||||
name: *const c_char,
|
|
||||||
value: *const c_char,
|
|
||||||
serial: u32,
|
|
||||||
);
|
|
||||||
unsafe fn load_prop_file(filename: *const c_char, skip_svc: bool);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C++" {
|
unsafe extern "C++" {
|
||||||
#[namespace = "rust"]
|
#[namespace = "rust"]
|
||||||
#[cxx_name = "Utf8CStr"]
|
#[cxx_name = "Utf8CStr"]
|
||||||
@@ -165,7 +149,8 @@ pub mod ffi {
|
|||||||
fn resolve_preinit_dir(base_dir: Utf8CStrRef) -> String;
|
fn resolve_preinit_dir(base_dir: Utf8CStrRef) -> String;
|
||||||
fn setup_magisk_env() -> bool;
|
fn setup_magisk_env() -> bool;
|
||||||
fn check_key_combo() -> bool;
|
fn check_key_combo() -> bool;
|
||||||
fn disable_modules();
|
#[cxx_name = "exec_script_rs"]
|
||||||
|
fn exec_script(script: Utf8CStrRef);
|
||||||
fn exec_common_scripts(stage: Utf8CStrRef);
|
fn exec_common_scripts(stage: Utf8CStrRef);
|
||||||
fn exec_module_scripts(state: Utf8CStrRef, modules: &Vec<ModuleInfo>);
|
fn exec_module_scripts(state: Utf8CStrRef, modules: &Vec<ModuleInfo>);
|
||||||
fn install_apk(apk: Utf8CStrRef);
|
fn install_apk(apk: Utf8CStrRef);
|
||||||
@@ -193,6 +178,18 @@ pub mod ffi {
|
|||||||
fn get_text(self: &DbValues, index: i32) -> &str;
|
fn get_text(self: &DbValues, index: i32) -> &str;
|
||||||
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32;
|
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32;
|
||||||
fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> i32;
|
fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> i32;
|
||||||
|
|
||||||
|
include!("include/resetprop.hpp");
|
||||||
|
|
||||||
|
#[cxx_name = "prop_cb"]
|
||||||
|
type PropCb;
|
||||||
|
#[cxx_name = "get_prop_rs"]
|
||||||
|
fn get_prop(name: Utf8CStrRef, persist: bool) -> String;
|
||||||
|
#[cxx_name = "set_prop_rs"]
|
||||||
|
fn set_prop(name: Utf8CStrRef, value: Utf8CStrRef, skip_svc: bool) -> i32;
|
||||||
|
#[cxx_name = "load_prop_file_rs"]
|
||||||
|
fn load_prop_file(filename: Utf8CStrRef, skip_svc: bool);
|
||||||
|
fn prop_cb_exec(cb: Pin<&mut PropCb>, name: Utf8CStrRef, value: Utf8CStrRef, serial: u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
@@ -203,6 +200,7 @@ pub mod ffi {
|
|||||||
fn setup_logfile();
|
fn setup_logfile();
|
||||||
fn find_preinit_device() -> String;
|
fn find_preinit_device() -> String;
|
||||||
fn revert_unmount(pid: i32);
|
fn revert_unmount(pid: i32);
|
||||||
|
fn remove_modules();
|
||||||
fn zygisk_should_load_module(flags: u32) -> bool;
|
fn zygisk_should_load_module(flags: u32) -> bool;
|
||||||
unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCb>);
|
unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCb>);
|
||||||
unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>);
|
unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>);
|
||||||
@@ -277,15 +275,3 @@ impl SuRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_prop(name: &Utf8CStr, persist: bool) -> String {
|
|
||||||
unsafe { ffi::get_prop_rs(name.as_ptr(), persist) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_prop(name: &Utf8CStr, value: &Utf8CStr, skip_svc: bool) -> bool {
|
|
||||||
unsafe { ffi::set_prop_rs(name.as_ptr(), value.as_ptr(), skip_svc) == 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_prop_file(filename: &Utf8CStr, skip_svc: bool) {
|
|
||||||
unsafe { ffi::load_prop_file(filename.as_ptr(), skip_svc) };
|
|
||||||
}
|
|
||||||
|
@@ -117,41 +117,3 @@ rust::Vec<ModuleInfo> MagiskD::load_modules() const noexcept {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int check_rules_dir(char *buf, size_t sz) {
|
|
||||||
int off = ssprintf(buf, sz, "%s/" PREINITMIRR, get_magisk_tmp());
|
|
||||||
struct stat st1{};
|
|
||||||
struct stat st2{};
|
|
||||||
if (xstat(buf, &st1) < 0 || xstat(MODULEROOT, &st2) < 0)
|
|
||||||
return 0;
|
|
||||||
if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
|
|
||||||
return 0;
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_modules() {
|
|
||||||
char buf[4096];
|
|
||||||
int off = check_rules_dir(buf, sizeof(buf));
|
|
||||||
foreach_module([&](int, dirent *entry, int modfd) {
|
|
||||||
close(xopenat(modfd, "disable", O_RDONLY | O_CREAT | O_CLOEXEC, 0));
|
|
||||||
if (off) {
|
|
||||||
ssprintf(buf + off, sizeof(buf) - off, "/%s/sepolicy.rule", entry->d_name);
|
|
||||||
unlink(buf);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_modules() {
|
|
||||||
char buf[4096];
|
|
||||||
int off = check_rules_dir(buf, sizeof(buf));
|
|
||||||
foreach_module([&](int, dirent *entry, int) {
|
|
||||||
auto uninstaller = MODULEROOT + "/"s + entry->d_name + "/uninstall.sh";
|
|
||||||
if (access(uninstaller.data(), F_OK) == 0)
|
|
||||||
exec_script(uninstaller.data());
|
|
||||||
if (off) {
|
|
||||||
ssprintf(buf + off, sizeof(buf) - off, "/%s/sepolicy.rule", entry->d_name);
|
|
||||||
unlink(buf);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
rm_rf(MODULEROOT);
|
|
||||||
}
|
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
use crate::consts::{MODULEMNT, MODULEROOT, MODULEUPGRADE, WORKERDIR};
|
use crate::consts::{MODULEMNT, MODULEROOT, MODULEUPGRADE, WORKERDIR};
|
||||||
use crate::daemon::MagiskD;
|
use crate::daemon::MagiskD;
|
||||||
use crate::ffi::{ModuleInfo, get_magisk_tmp, get_zygisk_lib_name};
|
use crate::ffi::{ModuleInfo, exec_script, get_magisk_tmp, get_zygisk_lib_name, load_prop_file};
|
||||||
use crate::load_prop_file;
|
|
||||||
use crate::mount::setup_module_mount;
|
use crate::mount::setup_module_mount;
|
||||||
use base::{
|
use base::{
|
||||||
Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResultStatic, ResultExt, Utf8CStr,
|
DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResultStatic, ResultExt,
|
||||||
Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc, warn,
|
Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc,
|
||||||
|
warn,
|
||||||
};
|
};
|
||||||
use libc::{AT_REMOVEDIR, MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY};
|
use libc::{AT_REMOVEDIR, MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -550,11 +550,119 @@ fn inject_zygisk_bins(system: &mut FsNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_modules() -> LoggedResult<()> {
|
fn apply_modules(zygisk: bool, module_list: &[ModuleInfo]) {
|
||||||
|
let mut system = FsNode::new_dir();
|
||||||
|
|
||||||
|
// Build all the base "prefix" paths
|
||||||
|
let mut root = cstr::buf::default().join_path("/");
|
||||||
|
|
||||||
|
let mut module_dir = cstr::buf::default().join_path(MODULEROOT);
|
||||||
|
|
||||||
|
let mut module_mnt = cstr::buf::default()
|
||||||
|
.join_path(get_magisk_tmp())
|
||||||
|
.join_path(MODULEMNT);
|
||||||
|
|
||||||
|
let mut worker = cstr::buf::default()
|
||||||
|
.join_path(get_magisk_tmp())
|
||||||
|
.join_path(WORKERDIR);
|
||||||
|
|
||||||
|
// Create a collection of all relevant paths
|
||||||
|
let mut root_paths = FilePaths {
|
||||||
|
real: PathTracker::from(&mut root),
|
||||||
|
worker: PathTracker::from(&mut worker),
|
||||||
|
module_mnt: PathTracker::from(&mut module_mnt),
|
||||||
|
module_root: PathTracker::from(&mut module_dir),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 1: Create virtual filesystem tree
|
||||||
|
//
|
||||||
|
// In this step, there is zero logic applied during tree construction; we simply collect and
|
||||||
|
// record the union of all module filesystem trees under each of their /system directory.
|
||||||
|
|
||||||
|
for info in module_list {
|
||||||
|
let mut module_paths = root_paths.append(&info.name);
|
||||||
|
{
|
||||||
|
// Read props
|
||||||
|
let prop = module_paths.append("system.prop");
|
||||||
|
if prop.module().exists() {
|
||||||
|
// Do NOT go through property service as it could cause boot lock
|
||||||
|
load_prop_file(prop.module(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Check whether skip mounting
|
||||||
|
let skip = module_paths.append("skip_mount");
|
||||||
|
if skip.module().exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Double check whether the system folder exists
|
||||||
|
let sys = module_paths.append("system");
|
||||||
|
if sys.module().exists() {
|
||||||
|
info!("{}: loading module files", &info.name);
|
||||||
|
system.collect(sys).log_ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Inject custom files
|
||||||
|
//
|
||||||
|
// Magisk provides some built-in functionality that requires augmenting the filesystem.
|
||||||
|
// We expose several cmdline tools (e.g. su) into PATH, and the zygisk shared library
|
||||||
|
// has to also be added into the default LD_LIBRARY_PATH for code injection.
|
||||||
|
// We directly inject file nodes into the virtual filesystem tree we built in the previous
|
||||||
|
// step, treating Magisk just like a special "module".
|
||||||
|
|
||||||
|
if get_magisk_tmp() != "/sbin" || get_path_env().split(":").all(|s| s != "/sbin") {
|
||||||
|
inject_magisk_bins(&mut system);
|
||||||
|
}
|
||||||
|
if zygisk {
|
||||||
|
inject_zygisk_bins(&mut system);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Extract all supported read-only partition roots
|
||||||
|
//
|
||||||
|
// For simplicity and backwards compatibility on older Android versions, when constructing
|
||||||
|
// Magisk modules, we always assume that there is only a single read-only partition mounted
|
||||||
|
// at /system. However, on modern Android there are actually multiple read-only partitions
|
||||||
|
// mounted at their respective paths. We need to extract these subtrees out of the main
|
||||||
|
// tree and treat them as individual trees.
|
||||||
|
|
||||||
|
let mut roots = BTreeMap::new(); /* mapOf(partition_name -> FsNode) */
|
||||||
|
if let FsNode::Directory { children } = &mut system {
|
||||||
|
for dir in SECONDARY_READ_ONLY_PARTITIONS {
|
||||||
|
// Only treat these nodes as root iff it is actually a directory in rootdir
|
||||||
|
if let Ok(attr) = dir.get_attr()
|
||||||
|
&& attr.is_dir()
|
||||||
|
{
|
||||||
|
let name = dir.trim_start_matches('/');
|
||||||
|
if let Some(root) = children.remove(name) {
|
||||||
|
roots.insert(name, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roots.insert("system", system);
|
||||||
|
|
||||||
|
for (dir, mut root) in roots {
|
||||||
|
// Step 4: Convert virtual filesystem tree into concrete operations
|
||||||
|
//
|
||||||
|
// Compare the virtual filesystem tree we constructed against the real filesystem
|
||||||
|
// structure on-device to generate a series of "operations".
|
||||||
|
// The "core" of the logic is to decide which directories need to be rebuilt in the
|
||||||
|
// tmpfs worker directory, and real sub-nodes need to be mirrored inside it.
|
||||||
|
|
||||||
|
let path = root_paths.append(dir);
|
||||||
|
root.commit(path, true).log_ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn upgrade_modules() -> LoggedResult<()> {
|
||||||
let mut upgrade = Directory::open(cstr!(MODULEUPGRADE))?;
|
let mut upgrade = Directory::open(cstr!(MODULEUPGRADE))?;
|
||||||
let ufd = upgrade.as_raw_fd();
|
let ufd = upgrade.as_raw_fd();
|
||||||
let root = Directory::open(cstr!(MODULEROOT))?;
|
let root = Directory::open(cstr!(MODULEROOT))?;
|
||||||
while let Some(ref e) = upgrade.read()? {
|
while let Some(e) = upgrade.read()? {
|
||||||
if !e.is_dir() {
|
if !e.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -590,120 +698,47 @@ fn prepare_modules() -> LoggedResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MagiskD {
|
fn for_each_module(func: impl Fn(&DirEntry) -> LoggedResult<()>) -> LoggedResult<()> {
|
||||||
fn apply_modules(&self, module_list: &[ModuleInfo]) {
|
let mut root = Directory::open(cstr!(MODULEROOT))?;
|
||||||
let mut system = FsNode::new_dir();
|
while let Some(ref e) = root.read()? {
|
||||||
|
if e.is_dir() && e.name() != ".core" {
|
||||||
// Build all the base "prefix" paths
|
func(e)?;
|
||||||
let mut root = cstr::buf::default().join_path("/");
|
|
||||||
|
|
||||||
let mut module_dir = cstr::buf::default().join_path(MODULEROOT);
|
|
||||||
|
|
||||||
let mut module_mnt = cstr::buf::default()
|
|
||||||
.join_path(get_magisk_tmp())
|
|
||||||
.join_path(MODULEMNT);
|
|
||||||
|
|
||||||
let mut worker = cstr::buf::default()
|
|
||||||
.join_path(get_magisk_tmp())
|
|
||||||
.join_path(WORKERDIR);
|
|
||||||
|
|
||||||
// Create a collection of all relevant paths
|
|
||||||
let mut root_paths = FilePaths {
|
|
||||||
real: PathTracker::from(&mut root),
|
|
||||||
worker: PathTracker::from(&mut worker),
|
|
||||||
module_mnt: PathTracker::from(&mut module_mnt),
|
|
||||||
module_root: PathTracker::from(&mut module_dir),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Step 1: Create virtual filesystem tree
|
|
||||||
//
|
|
||||||
// In this step, there is zero logic applied during tree construction; we simply collect and
|
|
||||||
// record the union of all module filesystem trees under each of their /system directory.
|
|
||||||
|
|
||||||
for info in module_list {
|
|
||||||
let mut module_paths = root_paths.append(&info.name);
|
|
||||||
{
|
|
||||||
// Read props
|
|
||||||
let prop = module_paths.append("system.prop");
|
|
||||||
if prop.module().exists() {
|
|
||||||
// Do NOT go through property service as it could cause boot lock
|
|
||||||
load_prop_file(prop.module(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Check whether skip mounting
|
|
||||||
let skip = module_paths.append("skip_mount");
|
|
||||||
if skip.module().exists() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Double check whether the system folder exists
|
|
||||||
let sys = module_paths.append("system");
|
|
||||||
if sys.module().exists() {
|
|
||||||
info!("{}: loading module files", &info.name);
|
|
||||||
system.collect(sys).log_ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Inject custom files
|
|
||||||
//
|
|
||||||
// Magisk provides some built-in functionality that requires augmenting the filesystem.
|
|
||||||
// We expose several cmdline tools (e.g. su) into PATH, and the zygisk shared library
|
|
||||||
// has to also be added into the default LD_LIBRARY_PATH for code injection.
|
|
||||||
// We directly inject file nodes into the virtual filesystem tree we built in the previous
|
|
||||||
// step, treating Magisk just like a special "module".
|
|
||||||
|
|
||||||
if get_magisk_tmp() != "/sbin" || get_path_env().split(":").all(|s| s != "/sbin") {
|
|
||||||
inject_magisk_bins(&mut system);
|
|
||||||
}
|
|
||||||
if self.zygisk_enabled() {
|
|
||||||
inject_zygisk_bins(&mut system);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Extract all supported read-only partition roots
|
|
||||||
//
|
|
||||||
// For simplicity and backwards compatibility on older Android versions, when constructing
|
|
||||||
// Magisk modules, we always assume that there is only a single read-only partition mounted
|
|
||||||
// at /system. However, on modern Android there are actually multiple read-only partitions
|
|
||||||
// mounted at their respective paths. We need to extract these subtrees out of the main
|
|
||||||
// tree and treat them as individual trees.
|
|
||||||
|
|
||||||
let mut roots = BTreeMap::new(); /* mapOf(partition_name -> FsNode) */
|
|
||||||
if let FsNode::Directory { children } = &mut system {
|
|
||||||
for dir in SECONDARY_READ_ONLY_PARTITIONS {
|
|
||||||
// Only treat these nodes as root iff it is actually a directory in rootdir
|
|
||||||
if let Ok(attr) = dir.get_attr()
|
|
||||||
&& attr.is_dir()
|
|
||||||
{
|
|
||||||
let name = dir.trim_start_matches('/');
|
|
||||||
if let Some(root) = children.remove(name) {
|
|
||||||
roots.insert(name, root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roots.insert("system", system);
|
|
||||||
|
|
||||||
for (dir, mut root) in roots {
|
|
||||||
// Step 4: Convert virtual filesystem tree into concrete operations
|
|
||||||
//
|
|
||||||
// Compare the virtual filesystem tree we constructed against the real filesystem
|
|
||||||
// structure on-device to generate a series of "operations".
|
|
||||||
// The "core" of the logic is to decide which directories need to be rebuilt in the
|
|
||||||
// tmpfs worker directory, and real sub-nodes need to be mirrored inside it.
|
|
||||||
|
|
||||||
let path = root_paths.append(dir);
|
|
||||||
root.commit(path, true).log_ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_modules() {
|
||||||
|
for_each_module(|e| {
|
||||||
|
let dir = e.open_as_dir()?;
|
||||||
|
dir.open_as_file_at(cstr!("disable"), O_RDONLY | O_CREAT | O_CLOEXEC, 0)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.log_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_modules() {
|
||||||
|
for_each_module(|e| {
|
||||||
|
let dir = e.open_as_dir()?;
|
||||||
|
if dir.contains_path(cstr!("uninstall.sh")) {
|
||||||
|
let script = cstr::buf::default()
|
||||||
|
.join_path(MODULEROOT)
|
||||||
|
.join_path(e.name())
|
||||||
|
.join_path("uninstall.sh");
|
||||||
|
exec_script(&script);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.log_ok();
|
||||||
|
cstr!(MODULEROOT).remove_all().log_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MagiskD {
|
||||||
pub fn handle_modules(&self) {
|
pub fn handle_modules(&self) {
|
||||||
setup_module_mount();
|
setup_module_mount();
|
||||||
prepare_modules().ok();
|
upgrade_modules().ok();
|
||||||
let modules = self.load_modules();
|
let modules = self.load_modules();
|
||||||
self.apply_modules(&modules);
|
apply_modules(self.zygisk_enabled(), &modules);
|
||||||
self.module_list.set(modules).ok();
|
self.module_list.set(modules).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,8 +12,7 @@ use base::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
||||||
use crate::ffi::{get_magisk_tmp, resolve_preinit_dir, switch_mnt_ns};
|
use crate::ffi::{get_magisk_tmp, get_prop, resolve_preinit_dir, switch_mnt_ns};
|
||||||
use crate::get_prop;
|
|
||||||
|
|
||||||
pub fn setup_preinit_dir() {
|
pub fn setup_preinit_dir() {
|
||||||
let magisk_tmp = get_magisk_tmp();
|
let magisk_tmp = get_magisk_tmp();
|
||||||
|
@@ -29,7 +29,7 @@ trait PropCbExec {
|
|||||||
|
|
||||||
impl PropCbExec for Pin<&mut PropCb> {
|
impl PropCbExec for Pin<&mut PropCb> {
|
||||||
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) {
|
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) {
|
||||||
unsafe { prop_cb_exec(self.as_mut(), name.as_ptr(), value.as_ptr(), u32::MAX) }
|
prop_cb_exec(self.as_mut(), name, value, u32::MAX)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -429,8 +429,8 @@ static StringType get_prop_impl(const char *name, bool persist) {
|
|||||||
return get_prop<StringType>(name, flags);
|
return get_prop<StringType>(name, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
rust::String get_prop_rs(const char *name, bool persist) {
|
rust::String get_prop_rs(rust::Utf8CStr name, bool persist) {
|
||||||
return get_prop_impl<rust::String>(name, persist);
|
return get_prop_impl<rust::String>(name.data(), persist);
|
||||||
}
|
}
|
||||||
|
|
||||||
string get_prop(const char *name, bool persist) {
|
string get_prop(const char *name, bool persist) {
|
||||||
|
Reference in New Issue
Block a user