Compare commits

...

9 Commits

Author SHA1 Message Date
topjohnwu
975120d6a6 Release Magisk v30.2
[skip ci]
2025-08-06 03:32:32 -07:00
topjohnwu
e489b3b6dd Migrate load_modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
589a270b8d Migrate disable/remove modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
7961be5cfa Migrate prepare_modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
959430e030 Fix systemless hosts installation 2025-08-05 09:44:51 -07:00
topjohnwu
2923c8ccd1 Add module upgrade test 2025-08-05 09:44:51 -07:00
topjohnwu
7df4a9d74f Add uninstaller.sh test 2025-08-05 09:44:51 -07:00
topjohnwu
bf4ed295da Update cargo dependencies 2025-08-02 13:43:27 -07:00
topjohnwu
a5fca960dc Update gradle and dependencies 2025-08-02 02:29:14 -07:00
30 changed files with 505 additions and 414 deletions

View File

@@ -22,11 +22,11 @@ import com.topjohnwu.magisk.core.ktx.activity
import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.core.tasks.AppMigration
import com.topjohnwu.magisk.core.utils.LocaleSetting
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.events.AddHomeIconEvent
import com.topjohnwu.magisk.events.AuthEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
@@ -130,7 +130,8 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
}
private fun createHosts() {
Shell.cmd("add_hosts_module").submit {
viewModelScope.launch {
RootUtils.addSystemlessHosts()
AppContext.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
}
}

View File

@@ -18,12 +18,6 @@ gradlePlugin {
}
}
kotlin {
compilerOptions {
languageVersion = KotlinVersion.KOTLIN_2_0
}
}
dependencies {
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
implementation(libs.android.gradle.plugin)

View File

@@ -309,9 +309,9 @@ fun Project.setupStubApk() {
outputs.dir(outResDir)
doLast {
val apkTmp = File("${apk}.tmp")
exec {
providers.exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}
}.result.get()
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->

View File

@@ -38,3 +38,4 @@
-allowaccessmodification
-dontwarn org.junit.**
-dontwarn org.apache.**

View File

@@ -6,4 +6,5 @@ package com.topjohnwu.magisk.core.utils;
interface IRootUtils {
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
IBinder getFileSystem();
boolean addSystemlessHosts();
}

View File

@@ -109,7 +109,7 @@ fun PackageManager.getPackageInfo(uid: Int, pid: Int): PackageInfo? {
return null
}
// Try to find package name from PID
val proc = RootUtils.obj?.getAppProcess(pid)
val proc = RootUtils.getAppProcess(pid)
if (proc == null) {
if (uid == Process.SHELL_UID) {
// It is possible that some apps installed are sharing UID with shell.

View File

@@ -13,7 +13,7 @@ import java.io.IOException
import java.util.Locale
data class LocalModule(
private val base: ExtendedFile,
val base: ExtendedFile,
) : Module() {
private val svc get() = ServiceLocator.networkService

View File

@@ -7,11 +7,14 @@ import android.content.ServiceConnection
import android.os.IBinder
import android.system.Os
import androidx.core.content.getSystemService
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.ipc.RootService
import com.topjohnwu.superuser.nio.FileSystemManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
import java.util.concurrent.locks.AbstractQueuedSynchronizer
@@ -43,16 +46,7 @@ class RootUtils(stub: Any?) : RootService() {
return object : IRootUtils.Stub() {
override fun getAppProcess(pid: Int) = safe(null) { getAppProcessImpl(pid) }
override fun getFileSystem(): IBinder = FileSystemManager.getService()
}
}
private inline fun <T> safe(default: T, block: () -> T): T {
return try {
block()
} catch (e: Throwable) {
// The process died unexpectedly
Timber.e(e)
default
override fun addSystemlessHosts() = safe(false) { addSystemlessHostsImpl() }
}
}
@@ -78,6 +72,26 @@ class RootUtils(stub: Any?) : RootService() {
return null
}
private fun addSystemlessHostsImpl(): Boolean {
val module = File(Const.MODULE_PATH, "hosts")
if (module.exists()) return true
val hosts = File(module, "system/etc/hosts")
if (!hosts.parentFile.mkdirs()) return false
File(module, "module.prop").outputStream().writer().use {
it.write("""
id=hosts
name=Systemless Hosts
version=1.0
versionCode=1
author=Magisk
description=Magisk app built-in systemless hosts module
""".trimIndent())
}
File("/system/etc/hosts").copyTo(hosts)
File(module, "update").createNewFile()
return true
}
object Connection : AbstractQueuedSynchronizer(), ServiceConnection {
init {
state = 1
@@ -131,11 +145,25 @@ class RootUtils(stub: Any?) : RootService() {
return field
}
private set
var obj: IRootUtils? = null
private var obj: IRootUtils? = null
get() {
Connection.await()
return field
}
private set
fun getAppProcess(pid: Int) = safe(null) { obj?.getAppProcess(pid) }
suspend fun addSystemlessHosts() =
withContext(Dispatchers.IO) { safe(false) { obj?.addSystemlessHosts() ?: false } }
private inline fun <T> safe(default: T, block: () -> T): T {
return try {
block()
} catch (e: Throwable) {
// The process died unexpectedly
Timber.e(e)
default
}
}
}
}

View File

@@ -12,6 +12,7 @@ import com.topjohnwu.magisk.test.Environment.Companion.INVALID_ZYGISK
import com.topjohnwu.magisk.test.Environment.Companion.MOUNT_TEST
import com.topjohnwu.magisk.test.Environment.Companion.REMOVE_TEST
import com.topjohnwu.magisk.test.Environment.Companion.SEPOLICY_RULE
import com.topjohnwu.magisk.test.Environment.Companion.UPGRADE_TEST
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.runBlocking
import org.junit.After
@@ -56,7 +57,7 @@ class AdditionalTest : BaseTest {
@Test
fun testModuleCount() {
var expected = 3
var expected = 4
if (Environment.mount()) expected++
if (Environment.preinit()) expected++
if (Environment.lsposed()) expected++
@@ -136,5 +137,25 @@ class AdditionalTest : BaseTest {
@Test
fun testRemoveModule() {
assertNull("$REMOVE_TEST should be removed", modules.find { it.id == REMOVE_TEST })
assertTrue(
"Uninstaller of $REMOVE_TEST should be run",
RootUtils.fs.getFile(Environment.REMOVE_TEST_MARKER).exists()
)
}
@Test
fun testModuleUpgrade() {
val module = modules.find { it.id == UPGRADE_TEST }
assertNotNull("$UPGRADE_TEST is not installed", module)
module!!
assertFalse("$UPGRADE_TEST should be disabled", module.enable)
assertTrue(
"$UPGRADE_TEST should be updated",
module.base.getChildFile("post-fs-data.sh").exists()
)
assertFalse(
"$UPGRADE_TEST should be updated",
module.base.getChildFile("service.sh").exists()
)
}
}

View File

@@ -58,12 +58,15 @@ class Environment : BaseTest {
return Build.VERSION.SDK_INT >= 27
}
private const val MODULE_UPDATE_PATH = "/data/adb/modules_update"
private const val MODULE_ERROR = "Module zip processing incorrect"
const val MOUNT_TEST = "mount_test"
const val SEPOLICY_RULE = "sepolicy_rule"
const val INVALID_ZYGISK = "invalid_zygisk"
const val REMOVE_TEST = "remove_test"
const val REMOVE_TEST_MARKER = "/dev/.remove_test_removed"
const val EMPTY_ZYGISK = "empty_zygisk"
const val UPGRADE_TEST = "upgrade_test"
}
object TimberLog : CallbackList<String>(Runnable::run) {
@@ -120,7 +123,9 @@ class Environment : BaseTest {
}
private fun setupSystemlessHost() {
assertTrue("hosts setup failed", Shell.cmd("add_hosts_module").exec().isSuccess)
val error = "hosts setup failed"
assertTrue(error, runBlocking { RootUtils.addSystemlessHosts() })
assertTrue(error, RootUtils.fs.getFile(Const.MODULE_PATH).getChildFile("hosts").exists())
}
private fun setupSepolicyRuleModule(root: ExtendedFile) {
@@ -170,12 +175,39 @@ class Environment : BaseTest {
// Create a new module but mark is as "remove"
val module = LocalModule(path)
assertTrue(error, path.mkdirs())
// Create uninstaller script
path.getChildFile("uninstall.sh").newOutputStream().writer().use {
it.write("touch $REMOVE_TEST_MARKER")
}
assertTrue(error, path.getChildFile("service.sh").createNewFile())
module.remove = true
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
private fun setupUpgradeModule(root: ExtendedFile, update: ExtendedFile) {
val error = "$UPGRADE_TEST setup failed"
val oldPath = root.getChildFile(UPGRADE_TEST)
val newPath = update.getChildFile(UPGRADE_TEST)
// Create an existing module but mark as "disable
val module = LocalModule(oldPath)
assertTrue(error, oldPath.mkdirs())
module.enable = false
// Install service.sh into the old module
assertTrue(error, oldPath.getChildFile("service.sh").createNewFile())
// Create an upgrade module
assertTrue(error, newPath.mkdirs())
// Install post-fs-data.sh into the new module
assertTrue(error, newPath.getChildFile("post-fs-data.sh").createNewFile())
assertTrue(error, Shell.cmd(
"set_default_perm $oldPath",
"set_default_perm $newPath",
).exec().isSuccess)
}
@Test
fun setupEnvironment() {
runBlocking {
@@ -220,12 +252,14 @@ class Environment : BaseTest {
}
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
if (mount()) { setupMountTest(root) }
if (preinit()) { setupSepolicyRuleModule(root) }
val update = RootUtils.fs.getFile(MODULE_UPDATE_PATH)
if (mount()) { setupMountTest(update) }
if (preinit()) { setupSepolicyRuleModule(update) }
setupSystemlessHost()
setupEmptyZygiskModule(root)
setupInvalidZygiskModule(root)
setupEmptyZygiskModule(update)
setupInvalidZygiskModule(update)
setupRemoveModule(root)
setupUpgradeModule(root, update)
}
@Test

View File

@@ -30,4 +30,4 @@ android.nonFinalResIds=false
# Magisk
magisk.stubVersion=40
magisk.versionCode=30100
magisk.versionCode=30200

View File

@@ -1,9 +1,9 @@
[versions]
kotlin = "2.2.0"
android = "8.11.1"
android = "8.12.0"
ksp = "2.2.0-2.0.2"
rikka = "1.3.0"
navigation = "2.9.1"
navigation = "2.9.3"
libsu = "6.0.0"
okhttp = "5.1.0"
retrofit = "3.0.0"
@@ -11,7 +11,7 @@ room = "2.7.2"
[libraries]
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.81" }
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.27.1" }
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.28.0" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
retrofit-scalars = { module = "com.squareup.retrofit2:converter-scalars", version.ref = "retrofit" }
@@ -41,9 +41,9 @@ transition = { module = "androidx.transition:transition", version = "1.6.0" }
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.5.0" }
material = { module = "com.google.android.material:material", version = "1.12.0" }
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.5" }
test-runner = { module = "androidx.test:runner", version = "1.6.2" }
test-rules = { module = "androidx.test:rules", version = "1.6.1" }
test-junit = { module = "androidx.test.ext:junit", version = "1.2.1" }
test-runner = { module = "androidx.test:runner", version = "1.7.0" }
test-rules = { module = "androidx.test:rules", version = "1.7.0" }
test-junit = { module = "androidx.test.ext:junit", version = "1.3.0" }
test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.3.0" }
# topjohnwu

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -1,5 +1,11 @@
# Magisk Changelog
### v30.2 (2025.8.6)
- [Core] Fix an edge case breaking modules when overlayfs is involved
- [Core] Fix module `.replace` functionality in certain situations
- [resetprop] Reduce property modification traces
### v30.1 (2025.7.3)
- [Core] Fix bug in module mounting implementation

View File

@@ -20,7 +20,6 @@ LOCAL_SRC_FILES := \
core/daemon.cpp \
core/scripting.cpp \
core/sqlite.cpp \
core/module.cpp \
core/thread.cpp \
core/core-rs.cpp \
core/resetprop/resetprop.cpp \

28
native/src/Cargo.lock generated
View File

@@ -124,9 +124,9 @@ dependencies = [
[[package]]
name = "bytemuck_derive"
version = "1.9.3"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
dependencies = [
"proc-macro2",
"quote",
@@ -150,9 +150,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.29"
version = "1.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
dependencies = [
"shlex",
]
@@ -165,18 +165,18 @@ checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "clap"
version = "4.5.41"
version = "4.5.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.41"
version = "4.5.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
dependencies = [
"anstyle",
"clap_lex",
@@ -236,9 +236,9 @@ dependencies = [
[[package]]
name = "crc32fast"
version = "1.4.2"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
@@ -370,9 +370,9 @@ dependencies = [
[[package]]
name = "ecdsa"
version = "0.17.0-rc.4"
version = "0.17.0-rc.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a71089a4626a0efab7561890b599f7c6c9138c0d02b5c2739a8c07ef87101270"
checksum = "112839e868b3376c2066506d42331023165d687a7ed38b2ed77f28763d9a7742"
dependencies = [
"der",
"digest",
@@ -916,9 +916,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signature"
version = "3.0.0-rc.1"
version = "3.0.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8852cecbd17ba45978bbbe43061ebe36a2ae376058c5c172e09f72888f8f7de"
checksum = "4835c3b5ecb10171941a4998a95a3a76ecac1c5ae8e6954f2ad030acd1c7e8ab"
dependencies = [
"digest",
"rand_core",

View File

@@ -309,7 +309,7 @@ impl Directory {
self.pre_order_walk_impl(&mut f)
}
pub fn remove_all(&mut self) -> OsResultStatic<()> {
pub fn remove_all(mut self) -> OsResultStatic<()> {
self.post_order_walk(|e| {
e.unlink()?;
Ok(WalkResult::Continue)

View File

@@ -227,7 +227,7 @@ impl Utf8CStr {
pub fn remove_all(&self) -> OsResultStatic<()> {
let attr = self.get_attr()?;
if attr.is_dir() {
let mut dir = Directory::try_from(open_fd(self, O_RDONLY | O_CLOEXEC, 0)?)?;
let dir = Directory::try_from(open_fd(self, O_RDONLY | O_CLOEXEC, 0)?)?;
dir.remove_all()?;
}
Ok(self.remove()?)

View File

@@ -1,15 +1,15 @@
use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOTOVL, SECURE_DIR};
use crate::db::Sqlite3;
use crate::ffi::{
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, disable_modules, exec_common_scripts,
exec_module_scripts, get_magisk_tmp, initialize_denylist, setup_magisk_env,
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, exec_common_scripts, exec_module_scripts,
get_magisk_tmp, get_prop, initialize_denylist, set_prop, setup_magisk_env,
};
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
use crate::mount::{clean_mounts, setup_module_mount, setup_preinit_dir};
use crate::module::disable_modules;
use crate::mount::{clean_mounts, setup_preinit_dir};
use crate::package::ManagerInfo;
use crate::selinux::restore_tmpcon;
use crate::su::SuInfo;
use crate::{get_prop, set_prop};
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
use base::{
AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, error, info, libc,
@@ -144,10 +144,7 @@ impl MagiskD {
Ordering::Release,
);
initialize_denylist();
setup_module_mount();
let modules = self.load_modules();
self.module_list.set(modules).ok();
self.apply_modules();
self.handle_modules();
clean_mounts();
false

View File

@@ -92,10 +92,6 @@ void exec_task(std::function<void()> &&task);
// Daemon handlers
void denylist_handler(int client, const sock_cred *cred);
// Module stuffs
void disable_modules();
void remove_modules();
// Scripting
void install_apk(rust::Utf8CStr apk);
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) {
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
rust::String get_prop_rs(const char *name, bool persist);
std::string get_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);
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) {
cb.exec(name, value, serial);
// Rust bindings
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::socket::Encodable;
use base::{Utf8CStr, libc};
use base::libc;
use cxx::{ExternType, type_id};
use daemon::{MagiskD, daemon_entry};
use derive::Decodable;
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 resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon};
@@ -133,23 +134,6 @@ pub mod ffi {
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++" {
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"]
@@ -165,7 +149,8 @@ pub mod ffi {
fn resolve_preinit_dir(base_dir: Utf8CStrRef) -> String;
fn setup_magisk_env() -> 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_module_scripts(state: Utf8CStrRef, modules: &Vec<ModuleInfo>);
fn install_apk(apk: Utf8CStrRef);
@@ -173,6 +158,7 @@ pub mod ffi {
fn update_deny_flags(uid: i32, process: &str, flags: &mut u32);
fn initialize_denylist();
fn get_zygisk_lib_name() -> &'static str;
fn set_zygisk_prop();
fn restore_zygisk_prop();
fn switch_mnt_ns(pid: i32) -> i32;
fn app_request(req: &SuAppRequest) -> i32;
@@ -193,6 +179,18 @@ pub mod ffi {
fn get_text(self: &DbValues, index: i32) -> &str;
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> 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" {
@@ -203,6 +201,7 @@ pub mod ffi {
fn setup_logfile();
fn find_preinit_device() -> String;
fn revert_unmount(pid: i32);
fn remove_modules();
fn zygisk_should_load_module(flags: u32) -> bool;
unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCb>);
unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>);
@@ -256,9 +255,6 @@ pub mod ffi {
#[cxx_name = "Get"]
fn get() -> &'static MagiskD;
}
unsafe extern "C++" {
fn load_modules(self: &MagiskD) -> Vec<ModuleInfo>;
}
}
#[repr(transparent)]
@@ -277,15 +273,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

@@ -1,184 +0,0 @@
#include <sys/mman.h>
#include <sys/syscall.h>
#include <base.hpp>
#include <consts.hpp>
#include <core.hpp>
using namespace std;
/************************
* Filesystem operations
************************/
static void prepare_modules() {
// Upgrade modules
if (auto dir = open_dir(MODULEUPGRADE); dir) {
int ufd = dirfd(dir.get());
int mfd = xopen(MODULEROOT, O_RDONLY | O_CLOEXEC);
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) {
// Cleanup old module if exists
if (faccessat(mfd, entry->d_name, F_OK, 0) == 0) {
int modfd = xopenat(mfd, entry->d_name, O_RDONLY | O_CLOEXEC);
if (faccessat(modfd, "disable", F_OK, 0) == 0) {
auto disable = entry->d_name + "/disable"s;
close(xopenat(ufd, disable.data(), O_RDONLY | O_CREAT | O_CLOEXEC, 0));
}
frm_rf(modfd);
unlinkat(mfd, entry->d_name, AT_REMOVEDIR);
}
LOGI("Upgrade / New module: %s\n", entry->d_name);
renameat(ufd, entry->d_name, mfd, entry->d_name);
}
}
close(mfd);
rm_rf(MODULEUPGRADE);
}
}
template<typename Func>
static void foreach_module(Func fn) {
auto dir = open_dir(MODULEROOT);
if (!dir)
return;
int dfd = dirfd(dir.get());
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR && entry->d_name != ".core"sv) {
int modfd = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC);
fn(dfd, entry, modfd);
close(modfd);
}
}
}
static rust::Vec<ModuleInfo> collect_modules(bool zygisk_enabled, bool open_zygisk) {
rust::Vec<ModuleInfo> modules;
foreach_module([&](int dfd, dirent *entry, int modfd) {
if (faccessat(modfd, "remove", F_OK, 0) == 0) {
LOGI("%s: remove\n", entry->d_name);
auto uninstaller = MODULEROOT + "/"s + entry->d_name + "/uninstall.sh";
if (access(uninstaller.data(), F_OK) == 0)
exec_script(uninstaller.data());
frm_rf(xdup(modfd));
unlinkat(dfd, entry->d_name, AT_REMOVEDIR);
return;
}
unlinkat(modfd, "update", 0);
if (faccessat(modfd, "disable", F_OK, 0) == 0)
return;
ModuleInfo info{{}, -1, -1};
if (zygisk_enabled) {
// Riru and its modules are not compatible with zygisk
if (entry->d_name == "riru-core"sv || faccessat(modfd, "riru", F_OK, 0) == 0) {
LOGI("%s: ignore\n", entry->d_name);
return;
}
if (open_zygisk) {
#if defined(__arm__)
info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC);
info.z64 = -1;
#elif defined(__aarch64__)
info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC);
info.z64 = openat(modfd, "zygisk/arm64-v8a.so", O_RDONLY | O_CLOEXEC);
#elif defined(__i386__)
info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC);
info.z64 = -1;
#elif defined(__x86_64__)
info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC);
info.z64 = openat(modfd, "zygisk/x86_64.so", O_RDONLY | O_CLOEXEC);
#elif defined(__riscv)
info.z32 = -1;
info.z64 = openat(modfd, "zygisk/riscv64.so", O_RDONLY | O_CLOEXEC);
#else
#error Unsupported ABI
#endif
unlinkat(modfd, "zygisk/unloaded", 0);
}
} else {
// Ignore zygisk modules when zygisk is not enabled
if (faccessat(modfd, "zygisk", F_OK, 0) == 0) {
LOGI("%s: ignore\n", entry->d_name);
return;
}
}
info.name = entry->d_name;
modules.push_back(std::move(info));
});
if (zygisk_enabled) {
bool use_memfd = true;
auto convert_to_memfd = [&](int fd) -> int {
if (fd < 0)
return -1;
if (use_memfd) {
int memfd = syscall(__NR_memfd_create, "jit-cache", MFD_CLOEXEC);
if (memfd >= 0) {
xsendfile(memfd, fd, nullptr, INT_MAX);
close(fd);
return memfd;
} else {
// memfd_create failed, just use what we had
use_memfd = false;
}
}
return fd;
};
ranges::for_each(modules, [&](ModuleInfo &info) {
info.z32 = convert_to_memfd(info.z32);
info.z64 = convert_to_memfd(info.z64);
});
}
return modules;
}
rust::Vec<ModuleInfo> MagiskD::load_modules() const noexcept {
bool zygisk = zygisk_enabled();
prepare_modules();
exec_module_scripts("post-fs-data", collect_modules(zygisk, false));
// Recollect modules (module scripts could remove itself)
auto list = collect_modules(zygisk, true);
if (zygisk) {
set_zygisk_prop();
}
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,14 +1,21 @@
use crate::consts::{MODULEMNT, MODULEROOT, WORKERDIR};
use crate::consts::{MODULEMNT, MODULEROOT, MODULEUPGRADE, WORKERDIR};
use crate::daemon::MagiskD;
use crate::ffi::{get_magisk_tmp, get_zygisk_lib_name};
use crate::load_prop_file;
use base::{
Directory, FsPathBuilder, LoggedResult, OsResultStatic, ResultExt, Utf8CStr, Utf8CStrBuf,
Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc, warn,
use crate::ffi::{
ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp, get_zygisk_lib_name,
load_prop_file, set_zygisk_prop,
};
use libc::{MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY};
use crate::mount::setup_module_mount;
use base::{
DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResultStatic, ResultExt,
Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc,
raw_cstr, warn,
};
use libc::{AT_REMOVEDIR, MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY};
use std::collections::BTreeMap;
use std::os::fd::{AsRawFd, IntoRawFd};
use std::path::{Component, Path};
use std::ptr;
use std::sync::atomic::Ordering;
const MAGISK_BIN_INJECT_PARTITIONS: [&Utf8CStr; 4] = [
cstr!("/system/"),
@@ -548,112 +555,328 @@ fn inject_zygisk_bins(system: &mut FsNode) {
}
}
impl MagiskD {
pub fn apply_modules(&self) {
let mut system = FsNode::new_dir();
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("/");
// 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_dir = cstr::buf::default().join_path(MODULEROOT);
let mut module_mnt = cstr::buf::default()
.join_path(get_magisk_tmp())
.join_path(MODULEMNT);
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);
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),
};
// 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.
// 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 self.module_list.get().iter().flat_map(|v| v.iter()) {
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();
}
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);
}
}
// 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);
}
}
{
// Check whether skip mounting
let skip = module_paths.append("skip_mount");
if skip.module().exists() {
continue;
}
}
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();
{
// 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 ufd = upgrade.as_raw_fd();
let root = Directory::open(cstr!(MODULEROOT))?;
while let Some(e) = upgrade.read()? {
if !e.is_dir() {
continue;
}
let module_name = e.name();
let mut disable = false;
// Cleanup old module if exists
if root.contains_path(module_name) {
let module = root.open_as_dir_at(module_name)?;
// If the old module is disabled, we need to also disable the new one
disable = module.contains_path(cstr!("disable"));
module.remove_all()?;
root.unlink_at(module_name, AT_REMOVEDIR)?;
}
info!("Upgrade / New module: {module_name}");
unsafe {
libc::renameat(
ufd,
module_name.as_ptr(),
root.as_raw_fd(),
module_name.as_ptr(),
)
.check_os_err("renameat", Some(module_name), None)?;
}
if disable {
let path = cstr::buf::default()
.join_path(module_name)
.join_path("disable");
let _ = root.open_as_file_at(&path, O_RDONLY | O_CREAT | O_CLOEXEC, 0)?;
}
}
upgrade.remove_all()?;
cstr!(MODULEUPGRADE).remove()?;
Ok(())
}
fn for_each_module(mut func: impl FnMut(&DirEntry) -> LoggedResult<()>) -> LoggedResult<()> {
let mut root = Directory::open(cstr!(MODULEROOT))?;
while let Some(ref e) = root.read()? {
if e.is_dir() && e.name() != ".core" {
func(e)?;
}
}
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();
}
fn run_uninstall_script(module_name: &Utf8CStr) {
let script = cstr::buf::default()
.join_path(MODULEROOT)
.join_path(module_name)
.join_path("uninstall.sh");
exec_script(&script);
}
pub fn remove_modules() {
for_each_module(|e| {
let dir = e.open_as_dir()?;
if dir.contains_path(cstr!("uninstall.sh")) {
run_uninstall_script(e.name());
}
Ok(())
})
.log_ok();
cstr!(MODULEROOT).remove_all().log_ok();
}
fn collect_modules(zygisk_enabled: bool, open_zygisk: bool) -> Vec<ModuleInfo> {
let mut modules = Vec::new();
#[allow(unused_mut)] // It's possible that z32 and z64 are unused
for_each_module(|e| {
let name = e.name();
let dir = e.open_as_dir()?;
if dir.contains_path(cstr!("remove")) {
info!("{name}: remove");
if dir.contains_path(cstr!("uninstall.sh")) {
run_uninstall_script(name);
}
dir.remove_all()?;
e.unlink()?;
return Ok(());
}
dir.unlink_at(cstr!("update"), 0).ok();
if dir.contains_path(cstr!("disable")) {
return Ok(());
}
let mut z32 = -1;
let mut z64 = -1;
let is_zygisk = dir.contains_path(cstr!("zygisk"));
if zygisk_enabled {
// Riru and its modules are not compatible with zygisk
if name == "riru-core" || dir.contains_path(cstr!("riru")) {
return Ok(());
}
fn open_fd_safe(dir: &Directory, name: &Utf8CStr) -> i32 {
dir.open_as_file_at(name, O_RDONLY | O_CLOEXEC, 0)
.log()
.map(IntoRawFd::into_raw_fd)
.unwrap_or(-1)
}
if open_zygisk && is_zygisk {
#[cfg(target_arch = "arm")]
{
z32 = open_fd_safe(&dir, cstr!("zygisk/armeabi-v7a.so"));
}
#[cfg(target_arch = "aarch64")]
{
z32 = open_fd_safe(&dir, cstr!("zygisk/armeabi-v7a.so"));
z64 = open_fd_safe(&dir, cstr!("zygisk/arm64-v8a.so"));
}
#[cfg(target_arch = "x86")]
{
z32 = open_fd_safe(&dir, cstr!("zygisk/x86.so"));
}
#[cfg(target_arch = "x86_64")]
{
z32 = open_fd_safe(&dir, cstr!("zygisk/x86.so"));
z64 = open_fd_safe(&dir, cstr!("zygisk/x86_64.so"));
}
#[cfg(target_arch = "riscv64")]
{
z64 = open_fd_safe(&dir, cstr!("zygisk/riscv64.so"));
}
dir.unlink_at(cstr!("zygisk/unloaded"), 0).ok();
}
} else {
// Ignore zygisk modules when zygisk is not enabled
if is_zygisk {
info!("{name}: ignore");
return Ok(());
}
}
modules.push(ModuleInfo {
name: name.to_string(),
z32,
z64,
});
Ok(())
})
.log_ok();
if zygisk_enabled && open_zygisk {
let mut use_memfd = true;
let mut convert_to_memfd = |fd: i32| -> i32 {
if fd < 0 {
return fd;
}
if use_memfd {
let memfd = unsafe {
libc::syscall(
libc::SYS_memfd_create,
raw_cstr!("jit-cache"),
libc::MFD_CLOEXEC,
) as i32
};
if memfd >= 0 {
unsafe {
if libc::sendfile(memfd, fd, ptr::null_mut(), i32::MAX as usize) < 0 {
libc::close(memfd);
} else {
libc::close(fd);
return memfd;
}
}
}
// Some error occurred, don't try again
use_memfd = false;
}
fd
};
modules.iter_mut().for_each(|m| {
m.z32 = convert_to_memfd(m.z32);
m.z64 = convert_to_memfd(m.z64);
});
}
modules
}
impl MagiskD {
pub fn handle_modules(&self) {
setup_module_mount();
upgrade_modules().ok();
let zygisk = self.zygisk_enabled.load(Ordering::Acquire);
let modules = collect_modules(zygisk, false);
exec_module_scripts(cstr!("post-fs-data"), &modules);
// Recollect modules (module scripts could remove itself)
let modules = collect_modules(zygisk, true);
if zygisk {
set_zygisk_prop();
}
apply_modules(zygisk, &modules);
self.module_list.set(modules).ok();
}
}

View File

@@ -12,8 +12,7 @@ use base::{
};
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
use crate::ffi::{get_magisk_tmp, resolve_preinit_dir, switch_mnt_ns};
use crate::get_prop;
use crate::ffi::{get_magisk_tmp, get_prop, resolve_preinit_dir, switch_mnt_ns};
pub fn setup_preinit_dir() {
let magisk_tmp = get_magisk_tmp();

View File

@@ -29,7 +29,7 @@ trait PropCbExec {
impl PropCbExec for Pin<&mut PropCb> {
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);
}
rust::String get_prop_rs(const char *name, bool persist) {
return get_prop_impl<rust::String>(name, persist);
rust::String get_prop_rs(rust::Utf8CStr name, bool persist) {
return get_prop_impl<rust::String>(name.data(), persist);
}
string get_prop(const char *name, bool persist) {

View File

@@ -15,6 +15,7 @@ pub const LOGFILE: &str = "/cache/magisk.log";
// data paths
pub const SECURE_DIR: &str = "/data/adb";
pub const MODULEROOT: &str = concatcp!(SECURE_DIR, "/modules");
pub const MODULEUPGRADE: &str = concatcp!(SECURE_DIR, "/modules_update");
pub const DATABIN: &str = concatcp!(SECURE_DIR, "/magisk");
pub const MAGISKDB: &str = concatcp!(SECURE_DIR, "/magisk.db");

View File

@@ -21,7 +21,7 @@ pub(crate) fn switch_root(path: &Utf8CStr) {
let res: LoggedResult<()> = try {
debug!("Switch root to {}", path);
let mut mounts = BTreeSet::new();
let mut rootfs = Directory::open(cstr!("/"))?;
let rootfs = Directory::open(cstr!("/"))?;
for info in parse_mount_info("self") {
if info.target == "/" || info.target.as_str() == path.as_str() {
continue;

View File

@@ -118,24 +118,6 @@ EOF
cd /
}
add_hosts_module() {
# Do not touch existing hosts module
[ -d /data/adb/modules/hosts ] && return
cd /data/adb/modules
mkdir -p hosts/system/etc
cat << EOF > hosts/module.prop
id=hosts
name=Systemless Hosts
version=1.0
versionCode=1
author=Magisk
description=Magisk app built-in systemless hosts module
EOF
magisk --clone /system/etc/hosts hosts/system/etc/hosts
touch hosts/update
cd /
}
# $1 = APK
# $2 = package name
adb_pm_install() {