You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Compare commits
9 Commits
f99912b9db
...
v30.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
975120d6a6 | ||
|
|
e489b3b6dd | ||
|
|
589a270b8d | ||
|
|
7961be5cfa | ||
|
|
959430e030 | ||
|
|
2923c8ccd1 | ||
|
|
7df4a9d74f | ||
|
|
bf4ed295da | ||
|
|
a5fca960dc |
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
1
app/core/proguard-rules.pro
vendored
1
app/core/proguard-rules.pro
vendored
@@ -38,3 +38,4 @@
|
||||
-allowaccessmodification
|
||||
|
||||
-dontwarn org.junit.**
|
||||
-dontwarn org.apache.**
|
||||
|
||||
@@ -6,4 +6,5 @@ package com.topjohnwu.magisk.core.utils;
|
||||
interface IRootUtils {
|
||||
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
|
||||
IBinder getFileSystem();
|
||||
boolean addSystemlessHosts();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,4 +30,4 @@ android.nonFinalResIds=false
|
||||
|
||||
# Magisk
|
||||
magisk.stubVersion=40
|
||||
magisk.versionCode=30100
|
||||
magisk.versionCode=30200
|
||||
|
||||
@@ -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
|
||||
|
||||
2
app/gradle/wrapper/gradle-wrapper.properties
vendored
2
app/gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
28
native/src/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()?)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()); }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) };
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user