Migrate disable/remove modules to Rust

This commit is contained in:
topjohnwu
2025-08-03 20:08:34 -07:00
committed by John Wu
parent 7961be5cfa
commit 589a270b8d
9 changed files with 184 additions and 198 deletions

View File

@@ -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,

View File

@@ -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()); }

View File

@@ -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);
}

View File

@@ -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) };
}

View File

@@ -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);
}

View File

@@ -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();
} }
} }

View File

@@ -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();

View File

@@ -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)
} }
} }

View File

@@ -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) {