Compare commits

..

26 Commits

Author SHA1 Message Date
topjohnwu
62b1310d97 Release Magisk v29.0
[skip ci]
2025-05-14 01:26:05 -07:00
topjohnwu
0a86916d3a Fix cleanup 2025-05-14 01:23:54 -07:00
topjohnwu
9907ce57aa Add Magisk r29.0 release notes 2025-05-14 01:13:37 -07:00
topjohnwu
b92626cacc Use sudo instead of su 2025-05-13 17:26:09 -07:00
topjohnwu
5a762f0a8e Move all gradle files into folder app
Decouple java and native projects
2025-05-13 17:04:41 -07:00
topjohnwu
5dd7a7d804 Better ABI support for build.py 2025-05-13 14:35:57 -07:00
topjohnwu
7831f40691 Make tests more granular 2025-05-13 14:08:54 -07:00
topjohnwu
4f4b1ff885 Add sepolicy.rule patching tests 2025-05-13 14:08:54 -07:00
topjohnwu
97901979dd Test replace functionality 2025-05-13 14:08:54 -07:00
topjohnwu
287316842c Fix Android M sepolicy reading 2025-05-13 12:28:10 -07:00
topjohnwu
608786e8f3 Print verbose logs in avd_test.sh and cuttlefish.sh 2025-05-11 02:18:36 -07:00
topjohnwu
9684a35cab Use rust::String::c_str to ensure nil termination 2025-05-09 16:02:46 -07:00
Radoš Milićev
e3e4202954 Update translation of core strings 2025-05-09 11:06:03 -07:00
Radoš Milićev
23c2054d46 Translate stub strings 2025-05-09 11:06:03 -07:00
Wang Han
a20a2a8fa0 Recognize Samsung custom policy version path 2025-05-09 11:03:58 -07:00
topjohnwu
a2896be4a6 Cargo fmt
[skip ci]
2025-05-08 23:29:27 -07:00
LoveSy
e9220a28d9 Use splice to pump tty to avoid userspace copying 2025-05-08 23:20:46 -07:00
vvb2060
cf12087e21 app: disable multiArch 2025-05-08 21:02:26 -07:00
topjohnwu
00c1b36837 Support generating files for C++ IDE 2025-05-07 22:18:44 -07:00
topjohnwu
03e034795d Implement Ord and PartialOrd for Utf8CStr familiy 2025-05-05 11:33:33 -07:00
LoveSy
79c0fafe43 Fix cleanup pre-init mount 2025-05-05 11:00:39 -07:00
vvb2060
d499819ba0 app: ignore androidx meta prop file 2025-05-02 16:40:37 -07:00
vvb2060
86da917174 core: fix mkdirs 2025-05-02 16:39:46 -07:00
ZGX089
30bd7d6555 feat: Improve Arabic translation (stub)
Fixed typos, Grammatical errors and changed/fixed other strings.
2025-05-01 13:47:46 -07:00
ZGX089
e5a12f0f5f feat: Improve Arabic translation
Fix Grammatical errors, typos, and changed/fixed other strings.
2025-05-01 13:47:46 -07:00
topjohnwu
c85a8434c6 Update dependencies 2025-04-29 16:22:45 -07:00
50 changed files with 628 additions and 320 deletions

View File

@@ -6,10 +6,7 @@ on:
paths:
- "app/**"
- "native/**"
- "buildSrc/**"
- "build.py"
- "gradle.properties"
- "gradle/libs.versions.toml"
- ".github/workflows/build.yml"
pull_request:
branches: [master]
@@ -39,7 +36,7 @@ jobs:
run: ./build.py -v all
- name: Stop gradle daemon
run: ./gradlew --stop
run: ./app/gradlew --stop
- name: Upload build artifact
uses: actions/upload-artifact@v4
@@ -75,7 +72,7 @@ jobs:
run: python build.py -v -c .github/ci.prop all
- name: Stop gradle daemon
run: ./gradlew --stop
run: ./app/gradlew --stop
avd-test:
name: Test API ${{ matrix.version }} (x86_64)
@@ -197,7 +194,7 @@ jobs:
- name: Run Cuttlefish test
timeout-minutes: 10
run: su $USER -c 'scripts/cuttlefish.sh test'
run: sudo -E -u $USER scripts/cuttlefish.sh test
- name: Upload logs on error
if: ${{ failure() }}

10
.gitignore vendored
View File

@@ -2,19 +2,13 @@ out
*.zip
*.jks
*.apk
*.log
/config.prop
/notes.md
/update.sh
/app/dict.txt
# Built binaries
native/out
# Android Studio / Gradle
# Android Studio
*.iml
.gradle
.idea
.kotlin
/local.properties
/build
/captures

View File

@@ -21,7 +21,7 @@ Some highlight features:
Click the icon below to download Magisk apk.
[![](https://img.shields.io/badge/Magisk-v28.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
[![](https://img.shields.io/badge/Magisk%20Beta-v28.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
[![](https://img.shields.io/badge/Magisk%20Beta-v29.0-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v29.0)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://github.com/topjohnwu/Magisk/releases/tag/canary-28104)
## Useful Links

7
app/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
/dict.txt
# Gradle
.gradle
.kotlin
/local.properties
/build

View File

@@ -35,7 +35,7 @@ android {
}
dependencies {
implementation(project(":app:core"))
implementation(project(":core"))
coreLibraryDesugaring(libs.jdk.libs)
implementation(libs.indeterminate.checkbox)

View File

@@ -1,5 +1,11 @@
tasks.register("clean") {
plugins {
id("MagiskPlugin")
}
tasks.register("clean", Delete::class) {
delete(rootProject.layout.buildDirectory)
subprojects.forEach {
dependsOn(":app:${it.name}:clean")
dependsOn(":${it.name}:clean")
}
}

View File

@@ -14,7 +14,7 @@ private val defaultAbis = setOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
object Config {
operator fun get(key: String): String? {
val v = props[key] as? String ?: return null
return if (v.isBlank()) null else v
return v.ifBlank { null }
}
fun contains(key: String) = get(key) != null
@@ -28,19 +28,21 @@ object Config {
}
}
val Project.baseDir: File get() = rootProject.file("..")
class MagiskPlugin : Plugin<Project> {
override fun apply(project: Project) = project.applyPlugin()
private fun Project.applyPlugin() {
initRandom(rootProject.file("app/dict.txt"))
initRandom(rootProject.file("dict.txt"))
props.clear()
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
val configPath: String? by this
val config = configPath?.let { File(it) } ?: rootProject.file("config.prop")
val config = configPath?.let { File(it) } ?: File(baseDir, "config.prop")
if (config.exists())
config.inputStream().use { props.load(it) }
val repo = FileRepository(rootProject.file(".git"))
val repo = FileRepository(File(baseDir, ".git"))
val refId = repo.refDatabase.exactRef("HEAD").objectId
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
}

View File

@@ -89,6 +89,7 @@ fun Project.setupCommon() {
resources {
excludes += arrayOf(
"/META-INF/*",
"/META-INF/androidx/**",
"/META-INF/versions/**",
"/org/bouncycastle/**",
"/org/apache/commons/**",
@@ -150,7 +151,7 @@ fun Project.setupCoreLib() {
into("src/main/jniLibs")
for (abi in abiList) {
into(abi) {
from(rootProject.file("native/out/$abi")) {
from(File(baseDir, "native/out/$abi")) {
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
rename { if (it.endsWith(".so")) it else "lib$it.so" }
}
@@ -172,10 +173,10 @@ fun Project.setupCoreLib() {
val syncResources by tasks.registering(Sync::class) {
into("src/main/resources/META-INF/com/google/android")
from(rootProject.file("scripts/update_binary.sh")) {
from(File(baseDir, "scripts/update_binary.sh")) {
rename { "update-binary" }
}
from(rootProject.file("scripts/flash_script.sh")) {
from(File(baseDir, "scripts/flash_script.sh")) {
rename { "updater-script" }
}
}
@@ -186,7 +187,7 @@ fun Project.setupCoreLib() {
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(downloadBusybox)
processJavaResourcesProvider.configure { dependsOn(syncResources) }
val stubTask = tasks.getByPath(":app:stub:comment$variantCapped")
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
val stubApk = stubTask.outputs.files.asFileTree.filter {
it.name.endsWith(".apk")
}
@@ -196,14 +197,14 @@ fun Project.setupCoreLib() {
inputs.property("version", Config.version)
inputs.property("versionCode", Config.versionCode)
into("src/${this@all.name}/assets")
from(rootProject.file("scripts")) {
from(File(baseDir, "scripts")) {
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
}
from(rootProject.file("tools/bootctl"))
from(File(baseDir, "tools/bootctl"))
into("chromeos") {
from(rootProject.file("tools/futility"))
from(rootProject.file("tools/keys")) {
from(File(baseDir, "tools/futility"))
from(File(baseDir, "tools/keys")) {
include("kernel_data_key.vbprivk", "kernel.keyblock")
}
}
@@ -293,7 +294,7 @@ fun Project.setupAppCommon() {
signingConfigs {
create("config") {
Config["keyStore"]?.also {
storeFile = rootProject.file(it)
storeFile = File(baseDir, it)
storePassword = Config["keyStorePass"]
keyAlias = Config["keyAlias"]
keyPassword = Config["keyPass"]

View File

@@ -29,7 +29,7 @@ android {
}
dependencies {
api(project(":app:shared"))
api(project(":shared"))
api(libs.timber)
api(libs.markwon.core)

View File

@@ -14,7 +14,6 @@
<application
android:name=".App"
android:icon="@drawable/ic_launcher"
android:multiArch="true"
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"
tools:remove="android:appComponentFactory">

View File

@@ -132,7 +132,6 @@ abstract class MagiskInstallImpl protected constructor(
if (entry != null) {
val magisk32 = File(installDir, "magisk32")
zf.getInputStream(entry).writeTo(magisk32)
magisk32.setExecutable(true)
}
}
}
@@ -147,11 +146,15 @@ abstract class MagiskInstallImpl protected constructor(
Os.symlink(lib.path, "$installDir/$name")
}
// Also symlink magisk32 on 64-bit devices that supports 32-bit
val lib32 = info.javaClass.getDeclaredField("secondaryNativeLibraryDir")
.get(info) as String?
if (lib32 != null) {
Os.symlink("$lib32/libmagisk.so", "$installDir/magisk32");
// Also extract magisk32 on 64-bit devices that supports 32-bit
val abi32 = Const.CPU_ABI_32
if (Process.is64Bit() && abi32 != null) {
val name = "lib/$abi32/libmagisk.so"
val entry = javaClass.classLoader!!.getResourceAsStream(name)
if (entry != null) {
val magisk32 = File(installDir, "magisk32")
entry.writeTo(magisk32)
}
}
}

View File

@@ -7,6 +7,12 @@ import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.topjohnwu.magisk.core.model.module.LocalModule
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.magisk.test.Environment.Companion.EMPTY_ZYGISK
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.superuser.Shell
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertEquals
@@ -49,8 +55,9 @@ class AdditionalTest : BaseTest {
@Test
fun testModuleCount() {
var expected = 0
if (Environment.testModules()) expected +=2
var expected = 2
if (Environment.mount()) expected++
if (Environment.preinit()) expected++
if (Environment.lsposed()) expected++
if (Environment.shamiko()) expected++
assertEquals("Module count incorrect", expected, modules.size)
@@ -78,11 +85,10 @@ class AdditionalTest : BaseTest {
}
@Test
fun testModule01() {
assumeTrue(Environment.testModules())
fun testModuleMount() {
assumeTrue(Environment.mount())
val module = modules.find { it.id == "test_01" }
assertNotNull("test_01 is not installed", module)
assertNotNull("$MOUNT_TEST is not installed", modules.find { it.id == MOUNT_TEST })
assertTrue(
"/system/etc/newfile should exist",
RootUtils.fs.getFile("/system/etc/newfile").exists()
@@ -91,24 +97,42 @@ class AdditionalTest : BaseTest {
"/system/bin/screenrecord should not exist",
RootUtils.fs.getFile("/system/bin/screenrecord").exists()
)
module!!
assertTrue("test_01 should be zygisk unloaded", module.zygiskUnloaded)
val egg = RootUtils.fs.getFile("/system/app/EasterEgg").list() ?: arrayOf()
assertTrue(
"/system/app/EasterEgg should be empty",
egg.isEmpty()
)
}
@Test
fun testModule02() {
assumeTrue(Environment.testModules())
fun testSepolicyRule() {
assumeTrue(Environment.preinit())
val module = modules.find { it.id == "test_02" }
assertNotNull("test_02 is not installed", module)
module!!
assertTrue("test_02 should be zygisk unloaded", module.zygiskUnloaded)
assertNotNull("$SEPOLICY_RULE is not installed", modules.find { it.id == SEPOLICY_RULE })
assertTrue(
"Module sepolicy.rule is not applied",
Shell.cmd("magiskpolicy --print-rules | grep -q magisk_test").exec().isSuccess
)
}
@Test
fun testModule03() {
assumeTrue(Environment.testModules())
fun testEmptyZygiskModule() {
val module = modules.find { it.id == EMPTY_ZYGISK }
assertNotNull("$EMPTY_ZYGISK is not installed", module)
module!!
assertTrue("$EMPTY_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
}
assertNull("test_03 should be removed", modules.find { it.id == "test_03" })
@Test
fun testInvalidZygiskModule() {
val module = modules.find { it.id == INVALID_ZYGISK }
assertNotNull("$INVALID_ZYGISK is not installed", module)
module!!
assertTrue("$INVALID_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
}
@Test
fun testRemoveModule() {
assertNull("$REMOVE_TEST should be removed", modules.find { it.id == REMOVE_TEST })
}
}

View File

@@ -28,6 +28,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import timber.log.Timber
import java.io.File
import java.io.PrintStream
@Keep
@RunWith(AndroidJUnit4::class)
@@ -39,11 +40,16 @@ class Environment : BaseTest {
fun before() = BaseTest.prerequisite()
// The kernel running on emulators < API 26 does not play well with
// magic mount. Skip module tests on those legacy platforms.
fun testModules(): Boolean {
// magic mount. Skip mount_test on those legacy platforms.
fun mount(): Boolean {
return Build.VERSION.SDK_INT >= 26
}
// It is possible that there are no suitable preinit partition to use
fun preinit(): Boolean {
return Shell.cmd("magisk --preinit-device").exec().isSuccess
}
fun lsposed(): Boolean {
return Build.VERSION.SDK_INT in 27..34
}
@@ -53,6 +59,11 @@ class Environment : BaseTest {
}
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 EMPTY_ZYGISK = "empty_zygisk"
}
object TimberLog : CallbackList<String>(Runnable::run) {
@@ -83,30 +94,56 @@ class Environment : BaseTest {
}
}
private fun setupModule01(root: ExtendedFile) {
val error = "test_01 setup failed"
val path = root.getChildFile("test_01")
private fun setupMountTest(root: ExtendedFile) {
val error = "$MOUNT_TEST setup failed"
val path = root.getChildFile(MOUNT_TEST)
// Create /system/etc/newfile
val etc = path.getChildFile("system").getChildFile("etc")
assertTrue(error, etc.mkdirs())
assertTrue(error, etc.getChildFile("newfile").createNewFile())
// Create /system/app/EasterEgg/.replace
val egg = path.getChildFile("system").getChildFile("app").getChildFile("EasterEgg")
assertTrue(error, egg.mkdirs())
assertTrue(error, egg.getChildFile(".replace").createNewFile())
// Delete /system/bin/screenrecord
val bin = path.getChildFile("system").getChildFile("bin")
assertTrue(error, bin.mkdirs())
assertTrue(error, Shell.cmd("mknod $bin/screenrecord c 0 0").exec().isSuccess)
// Create an empty zygisk folder
val module = LocalModule(path)
assertTrue(error, module.zygiskFolder.mkdir())
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
private fun setupModule02(root: ExtendedFile) {
val error = "test_02 setup failed"
val path = root.getChildFile("test_02")
private fun setupSepolicyRuleModule(root: ExtendedFile) {
val error = "$SEPOLICY_RULE setup failed"
val path = root.getChildFile(SEPOLICY_RULE)
assertTrue(error, path.mkdirs())
// Add sepolicy patch
PrintStream(path.getChildFile("sepolicy.rule").newOutputStream()).use {
it.println("type magisk_test domain")
}
assertTrue(error, Shell.cmd(
"set_default_perm $path",
"copy_preinit_files"
).exec().isSuccess)
}
private fun setupEmptyZygiskModule(root: ExtendedFile) {
val error = "$EMPTY_ZYGISK setup failed"
val path = root.getChildFile(EMPTY_ZYGISK)
// Create an empty zygisk folder
val module = LocalModule(path)
assertTrue(error, module.zygiskFolder.mkdirs())
}
private fun setupInvalidZygiskModule(root: ExtendedFile) {
val error = "$INVALID_ZYGISK setup failed"
val path = root.getChildFile(INVALID_ZYGISK)
// Create invalid zygisk libraries
val module = LocalModule(path)
@@ -119,9 +156,9 @@ class Environment : BaseTest {
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
}
private fun setupModule03(root: ExtendedFile) {
val error = "test_03 setup failed"
val path = root.getChildFile("test_03")
private fun setupRemoveModule(root: ExtendedFile) {
val error = "$REMOVE_TEST setup failed"
val path = root.getChildFile(REMOVE_TEST)
// Create a new module but mark is as "remove"
val module = LocalModule(path)
@@ -175,12 +212,12 @@ class Environment : BaseTest {
}
}
if (testModules()) {
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
setupModule01(root)
setupModule02(root)
setupModule03(root)
}
if (mount()) { setupMountTest(root) }
if (preinit()) { setupSepolicyRuleModule(root) }
setupEmptyZygiskModule(root)
setupInvalidZygiskModule(root)
setupRemoveModule(root)
}
@Test

View File

@@ -6,25 +6,25 @@
<string name="logs">السجلات</string>
<string name="settings">الإعدادات</string>
<string name="install">تثبيت</string>
<string name="section_home">الأساسي</string>
<string name="section_theme">السِمات</string>
<string name="section_home">الصفحة الرئيسية</string>
<string name="section_theme">المظهر</string>
<!--Home-->
<string name="no_connection">لا يوجد إتصال</string>
<string name="app_changelog">تفاصيل التحديث</string>
<string name="loading">جارٍ التحميل...</string>
<string name="update">تحديث</string>
<string name="not_available">غير/متوفر</string>
<string name="not_available">غير متوفر</string>
<string name="hide">إخفاء</string>
<string name="home_package">الحزمة</string>
<string name="home_support_title">تبرع لنا</string>
<string name="home_item_source">الكود المصدري للتطبيق</string>
<string name="home_support_content">مـاجـيسك هي، وستظل دوماً، مجانيةً و مفتوحة المصدر، اظهر اهتمامك لنا لكي نبقيها هكذا بدعم مالي صغير</string>
<string name="home_support_content">ماجيسك هو، وسيظل دوماً، مجانياّ و مفتوح المصدر، اظهر اهتمامك لنا لكي نبقيه هكذا بدعم مالي صغير</string>
<string name="home_installed_version">تم التثبيت</string>
<string name="home_latest_version">آخر إصدار</string>
<string name="invalid_update_channel">مصدر التحديث غير صالح</string>
<string name="uninstall_magisk_title">إلغاء تثبيت Magisk</string>
<string name="uninstall_magisk_title">إلغاء تثبيت ماجيسك</string>
<string name="uninstall_magisk_msg">ستُعطل/ستُحذف جميع الإضافات. سيُحذف الروت، وربما ستشفر بياناتك إذا لم تكن غير مشفرة حالياً.</string>
<!--Install-->
@@ -34,7 +34,7 @@
<string name="install_options_title">الخيارات</string>
<string name="install_method_title">الطريقة</string>
<string name="install_next">التالي</string>
<string name="install_start">هيا، بنا</string>
<string name="install_start">هيا بنا</string>
<string name="manager_download_install">اضغط للتنزيل و التثبيت</string>
<string name="direct_install">تثبيت مباشر (موصى بها)</string>
<string name="install_inactive_slot">التثبيت على المنطقة الغير نشطة (بعد OTA)</string>
@@ -87,15 +87,15 @@
<!-- MagiskHide -->
<string name="show_system_app">إظهار برامج النظام</string>
<string name="hide_filter_hint">البحث بالإسم</string>
<string name="hide_filter_hint">البحث بالاسم</string>
<string name="hide_search">ابحث</string>
<!--Module Fragment-->
<string name="no_info_provided">م توفر معلومات)</string>
<string name="no_info_provided">ا تتوفر معلومات)</string>
<string name="reboot_recovery">إعادة التشغيل إلى Recovery</string>
<string name="reboot_bootloader">إعادة التشغيل إلى Bootloader</string>
<string name="reboot_download">إعادة التشغيل إلى وضـع Odin</string>
<string name="reboot_edl">إعادة التشغيل إلى وضـعية EDL</string>
<string name="reboot_edl">إعادة التشغيل إلى EDL</string>
<string name="module_version_author">%1$sبواسطة%2$s</string>
<string name="module_state_remove">إزالة </string>
<string name="module_state_restore">إسترجاع</string>
@@ -104,15 +104,15 @@
<string name="external_rw_permission_denied">امنحني إذن الولوج للذاكرة الداخلية</string>
<!--Settings -->
<string name="settings_dark_mode_title">وضـعية الِسمات</string>
<string name="settings_dark_mode_message">حدد الوضـع الذي يناسب ذوقك</string>
<string name="settings_dark_mode_title">المظهر</string>
<string name="settings_dark_mode_message">حدد المظهر الذي يناسب ذوقك</string>
<string name="settings_dark_mode_light">الوضـع المضيء</string>
<string name="settings_dark_mode_system">اتبّع النظام</string>
<string name="settings_dark_mode_dark">وضـع الظلام</string>
<string name="settings_dark_mode_dark">الوضع المظلم</string>
<string name="settings_download_path_title">مسار التحميل</string>
<string name="settings_download_path_message">ستحمل الملفات إلى %1$s</string>
<string name="language">اللغة</string>
<string name="system_default">(الأفتراضي)</string>
<string name="system_default">(الإفتراضي)</string>
<string name="settings_check_update_title">تحقق من التحديثات</string>
<string name="settings_check_update_summary">التحقق من التحديثات في الخلفية بشكل دوري</string>
<string name="settings_update_channel_title">مصدر التحديثات</string>
@@ -123,8 +123,8 @@
<string name="settings_hosts_title">موانع الاعلانات</string>
<string name="settings_hosts_summary">حجب الاعلانات دون تعديل النظام</string>
<string name="settings_hosts_toast">تم تمكين خاصية حجب الاعلانات</string>
<string name="settings_app_name_hint">الإسم الجديد</string>
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الإسم</string>
<string name="settings_app_name_hint">الاسم الجديد</string>
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الاسم</string>
<string name="settings_app_name_error">الصيغة غير مقبولة</string>
<string name="settings_su_app_adb">التطبيقات و ADB</string>
<string name="settings_su_app">التطبيقات فقط</string>
@@ -140,51 +140,51 @@
<string name="auto_response">الفعل التلقائي</string>
<string name="request_timeout">المهلة قبل الفعل التلقائي</string>
<string name="superuser_notification">إشعارات طلبات الروت</string>
<string name="settings_su_reauth_title">إعادة المصادقة بعد الترقية</string>
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد إجراء ترقيات للتطبيق</string>
<string name="settings_su_reauth_title">إعادة المصادقة بعد التحديث</string>
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد تحديث التطبيق</string>
<string name="settings_customization">تخصيص</string>
<string name="multiuser_mode">نمط مستخدمين متعددين</string>
<string name="multiuser_mode">نمط المستخدم المزدوج</string>
<string name="settings_owner_only">مالك الجهاز فقط</string>
<string name="settings_owner_manage">المالك هو من يحدد</string>
<string name="settings_user_independent">مستقل </string>
<string name="settings_user_independent">مستقل</string>
<string name="owner_only_summary">للمالك فقط له صلاحيات الروت</string>
<string name="owner_manage_summary">فقط المالك من يرفض و يمنح صلاحيات الروت</string>
<string name="user_independent_summary">كل مستخدم له قواعد روت خاصة به</string>
<string name="mount_namespace_mode">نمط Mount Namespace</string>
<string name="settings_ns_global">نمط Namespace العام</string>
<string name="settings_ns_requester">نمط NameSpace المتوارث</string>
<string name="settings_ns_isolate">نمط NameSpace المعزول</string>
<string name="settings_ns_global">نمط Namespace عام</string>
<string name="settings_ns_requester">نمط NameSpace متوارث</string>
<string name="settings_ns_isolate">نمط NameSpace معزول</string>
<string name="global_summary">جميع الجلسات الروت تستخدم NameSpace العام</string>
<string name="requester_summary">جميع الجلسات الروت تستخدم NameSpace المتوارث</string>
<string name="isolate_summary">جميع الجلسات الروت تستخدم NameSpace المعزول</string>
<!--Notifications-->
<string name="update_channel">تحديثات Magisk</string>
<string name="update_channel">تحديثات ماجيسك</string>
<string name="progress_channel">إشعارات التقدم</string>
<string name="download_complete">اكتمل التنزيل</string>
<string name="download_file_error">فشل تنزيل الملف</string>
<string name="magisk_update_title">تحديث مـاجـيسك متوفر!</string>
<string name="magisk_update_title">تحديث ماجيسك متوفر!</string>
<!--Toasts, Dialogs-->
<string name="yes">نعم</string>
<string name="no">لا</string>
<string name="download">تنزيل</string>
<string name="reboot">إعادة التشغيل</string>
<string name="release_notes">معلومات الأصدار الجديد</string>
<string name="flashing">يتم الحرق...</string>
<string name="release_notes">معلومات الإصدار الجديد</string>
<string name="flashing">يتم التثبيت...</string>
<string name="done">تم!</string>
<string name="failure">فشل!</string>
<string name="open_link_failed_toast">لم يُعثر على تطبيق لفتح الرابط …</string>
<string name="complete_uninstall">إلغاء التثبيت بالكامل</string>
<string name="restore_img">استعادة الصور</string>
<string name="restore_img_msg">جار الأستعادة…</string>
<string name="restore_done">تم الأستعادة</string>
<string name="restore_fail">النسخة الاحتياطية الأصلية غير موجودة!</string>
<string name="setup_fail">فشل التضبيط</string>
<string name="env_fix_title"> الإعداد الأضافي مطلوب</string>
<string name="setup_msg">جار تضبيت البيئة</string>
<string name="unsupport_magisk_title">إصدار مـاجـيسك غير مدعوم</string>
<string name="restore_img_msg">جار الإستعادة…</string>
<string name="restore_done">تم الإستعادة</string>
<string name="restore_fail">النسخة الإحتياطية الأصلية غير موجودة!</string>
<string name="setup_fail">فشل الإعداد</string>
<string name="env_fix_title">الإعداد الإضافي مطلوب</string>
<string name="setup_msg">جار إعداد البيئة</string>
<string name="unsupport_magisk_title">إصدار ماجيسك غير مدعوم</string>
</resources>

View File

@@ -1,99 +1,57 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<!--Sections-->
<string name="modules">Модули</string>
<string name="superuser">Супер-корисник</string>
<string name="logs">Дневник</string>
<string name="logs">Логови</string>
<string name="settings">Подешавања</string>
<string name="install">Инсталација</string>
<string name="section_home">Почетно</string>
<string name="section_theme">Теме</string>
<string name="denylist">Листа забрана</string>
<!--Status Fragment-->
<string name="invalid_update_channel">Непостојећи Канал Ажурирања</string>
<!--Home-->
<string name="no_connection">Недоступна конекција</string>
<string name="app_changelog">Промене у апликацији</string>
<string name="loading">Учитавање…</string>
<string name="update">Ажурирање</string>
<string name="not_available">N/A</string>
<string name="hide">Сакриј</string>
<string name="home_package">Пакет</string>
<string name="home_app_title">Апл.</string>
<!--Install Fragment-->
<string name="home_notice_content">Преузмите Magisk САМО са званичне GitHub странице. Фајлови из непознатих извора могу бити малициозни!</string>
<string name="home_support_title">Подржите нас</string>
<string name="home_follow_title">Запратите нас</string>
<string name="home_item_source">Извор</string>
<string name="home_support_content">Magisk јесте и увек ће бити бесплатан и open source. Можете показати да вам је стало својом донацијом.</string>
<string name="home_installed_version">Инсталирано</string>
<string name="home_latest_version">Најновије</string>
<string name="invalid_update_channel">Невалидан канал ажурирања</string>
<string name="uninstall_magisk_title">Деинсталирај Magisk</string>
<string name="uninstall_magisk_msg">Сви модули ће бити онемогућени/уклоњени!\nКорен ће бити уклоњен!\nСвако неенкриптовано интерно складиште ће употребом Magisk-а бити поново енкриптовано!</string>
<!--Install-->
<string name="keep_force_encryption">Задржи форсирану енкрипцију</string>
<string name="keep_dm_verity">Задржи AVB 2.0/dm-verity</string>
<string name="uninstall_magisk_title">Унинсталирај Магиск</string>
<string name="uninstall_magisk_msg">Сви модули ће бити онеспособљени/уклоњени. Корен ће бити уклоњен, и потенцијално енкриптовати твоје податке уколико већ нису енкриптовани</string>
<string name="update">Ажурирање</string>
<!--Module Fragment-->
<string name="no_info_provided">(Без информација)</string>
<!--Repo Fragment-->
<string name="update_available">Ажурирање доступно</string>
<string name="home_installed_version">Инсталирано</string>
<!--Log Fragment-->
<string name="menuSaveLog">Сачувај дневник</string>
<string name="menuClearLog">Обриши дневник</string>
<string name="logs_cleared">Дневник успешно креиран</string>
<!--About Activity-->
<string name="app_changelog">Дневник промена апликације</string>
<!--Toasts, Dialogs-->
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
<string name="download">Преузми</string>
<string name="reboot">Рестартуј</string>
<string name="magisk_update_title">Нови Адбејт Магиска Доступан!</string>
<string name="release_notes">Белешке обљављивања</string>
<string name="recovery_mode">Режим опоравка</string>
<string name="install_options_title">Опције</string>
<string name="install_method_title">Метод</string>
<string name="install_next">Наредно</string>
<string name="install_start">Почнимо</string>
<string name="manager_download_install">Притисни да преузмеш и инсталираш</string>
<string name="update_channel">Магиск Ажурирање</string>
<string name="flashing">Флешовање</string>
<string name="direct_install">Директна Инсталација (Препоручено)</string>
<string name="complete_uninstall">Комплетна Унинсталација</string>
<string name="restore_done">Повратак успешан!</string>
<string name="restore_fail">Фабрички бекап не постоји!</string>
<string name="download_file_error">Грешка при преузимању фајла</string>
<!--Settings Activity -->
<string name="language">Језик</string>
<string name="system_default">(Фабрички Система)</string>
<string name="settings_update_channel_title">Канал Ажурирања</string>
<string name="settings_update_stable">Стабилан</string>
<string name="settings_update_beta">Бета</string>
<string name="settings_update_custom">По наруџби</string>
<string name="settings_update_custom_msg">Унеси наруџбени УРЛ</string>
<string name="settings_hosts_title">Без-системски домаћини (hosts)</string>
<string name="settings_hosts_summary">Подршка без-системских домаћина за апликације за блокирање реклама</string>
<string name="settings_su_app_adb">Апликације и АДБ</string>
<string name="settings_su_app">Само Апликације</string>
<string name="settings_su_adb">Само АДБ</string>
<string name="settings_su_disable">Онемогућено</string>
<string name="settings_su_request_10">10 секунди</string>
<string name="settings_su_request_15">15 секунди</string>
<string name="settings_su_request_20">20 секунди</string>
<string name="settings_su_request_30">30 секунди</string>
<string name="settings_su_request_45">45 секунди</string>
<string name="settings_su_request_60">60 секунди</string>
<string name="superuser_access">Приступ Супер-кориснику</string>
<string name="auto_response">Аутоматски одговор</string>
<string name="request_timeout">Истек захтева</string>
<string name="superuser_notification">Нотификације Супер-корисника</string>
<string name="settings_su_reauth_title">Поново одобри после ажурирања</string>
<string name="settings_su_reauth_summary">Поново одобри дозволу Супер-корисника после ажурирања апликације</string>
<string name="multiuser_mode">Више-кориснички режим</string>
<string name="settings_owner_only">Власник уређаја само</string>
<string name="settings_owner_manage">Одређено од стране власника</string>
<string name="settings_user_independent">Независно од корисника</string>
<string name="owner_only_summary">Само власник има приступ корену</string>
<string name="owner_manage_summary">Само власник може да приступа корену и да прими захтеве за њега</string>
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
<string name="mount_namespace_mode">Постоље режима имена простора</string>
<string name="settings_ns_global">Глобално име простора</string>
<string name="settings_ns_requester">Наслеђено име простора</string>
<string name="settings_ns_isolate">Ограђено име простора</string>
<string name="global_summary">Све коренске сесије користе глобално име простора</string>
<string name="requester_summary">Коренске сесије ће наследити свој простор именовања</string>
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани простор именовања</string>
<string name="direct_install">Директна инсталација (Препоручено)</string>
<string name="install_inactive_slot">Инсталација на неактиван слот (Након OTA)</string>
<string name="install_inactive_slot_msg">Ваш уређај ће бити ФОРСИРАН да се покрене на тренутно неактивном слоту након поновног покретања!\nКористите опцију само кад се OTA заврши.\nНастави?</string>
<string name="setup_title">Додатне поставке</string>
<string name="select_patch_file">Изаберите фајл</string>
<string name="patch_file_msg">Изаберите слику (*.img) или ODIN tarfile (*.tar) или payload.bin (*.bin)</string>
<string name="reboot_delay_toast">Поново покретање за 5 секунди…</string>
<string name="flash_screen_title">Инсталација</string>
<!--Superuser-->
<string name="su_request_title">Супер-кориснички захтев</string>
<string name="touch_filtered_warning">Magisk не може да верификује ваш одговор, јер апликација прикрива супер-кориснички захтев</string>
<string name="deny">Забрани</string>
<string name="prompt">Захтев</string>
<string name="grant">Дозволи</string>
@@ -104,20 +62,190 @@
<string name="twentymin">20 мин</string>
<string name="thirtymin">30 мин</string>
<string name="sixtymin">60 мин</string>
<string name="su_allow_toast">%1$s је добио права на Супер-корисника</string>
<string name="su_deny_toast">%1$s није добио права на Супер-корисника</string>
<string name="su_allow_toast">%1$s је добио права на супер-корисника</string>
<string name="su_deny_toast">%1$s није добио права на супер-корисника</string>
<string name="su_snack_grant">Супер-корисничка права од %1$s су пружена</string>
<string name="su_snack_deny">Супер-корисничка права од %1$s су одбијена</string>
<string name="su_snack_notif_on">Нотификације од %1$s су омогућене</string>
<string name="su_snack_notif_off">Нотификације од %1$s су онемогућене</string>
<string name="su_snack_log_on">Записивање у дневник за %1$s је омогућено</string>
<string name="su_snack_log_off">Записивање у дневник за %1$s је онемогућено</string>
<string name="su_snack_log_on">Логовање за %1$s је омогућено</string>
<string name="su_snack_log_off">Логовање за %1$s је онемогућено</string>
<string name="su_revoke_title">Опозови?</string>
<string name="su_revoke_msg">Потврди да опозовеш права од %1$s?</string>
<string name="toast">Тост</string>
<string name="none">Ниједан</string>
<string name="su_revoke_msg">Потврди да опозовеш права на супер-корисника од %1$s?</string>
<string name="toast">Toast</string>
<string name="none">Ништа</string>
<!--Superuser logs-->
<string name="target_uid">Циљани УИД: %1$d</string>
<string name="superuser_toggle_notification">Нотификације</string>
<string name="superuser_toggle_revoke">Опозови</string>
<string name="superuser_policy_none">Ниједна апликација није тражила пермисије за супер-корисника још увек.</string>
<!--Logs-->
<string name="log_data_none">Немате логова, покушајте користити коренске апликације више</string>
<string name="log_data_magisk_none">Magisk логови су празни, то је чудно</string>
<string name="menuSaveLog">Сачувај лог</string>
<string name="menuClearLog">Уклони лог</string>
<string name="logs_cleared">Лог успешно уклоњен</string>
<string name="pid">PID: %1$d</string>
<string name="target_uid">Циљани UID: %1$d</string>
<string name="target_pid">Mount ns цињани PID: %s</string>
<string name="selinux_context">SELinux контекст: %s</string>
<string name="supp_group">Допунска група: %s</string>
<!--SafetyNet-->
<!--MagiskHide-->
<string name="show_system_app">Прикажи системске апл.</string>
<string name="show_os_app">Прикажи апл. ОС-а</string>
<string name="hide_filter_hint">Филтрирај по имену</string>
<string name="hide_search">Претрага</string>
<!--Module-->
<string name="no_info_provided">(Без информација)</string>
<string name="reboot_userspace">Лако поново покретање</string>
<string name="reboot_recovery">Поново покрени за опоравак</string>
<string name="reboot_bootloader">Поново покрени за bootloader</string>
<string name="reboot_download">Поново покрени за преузимање</string>
<string name="reboot_edl">Поново покрени за EDL</string>
<string name="reboot_safe_mode">Сигуран мод</string>
<string name="module_version_author">%1$s од %2$s</string>
<string name="module_state_remove">Уклони</string>
<string name="module_action">Акција</string>
<string name="module_state_restore">Поврати</string>
<string name="module_action_install_external">Инсталирај из складишта</string>
<string name="update_available">Ажурирање доступно</string>
<string name="suspend_text_riru">Модул је суспендован јер је %1$s омогућено</string>
<string name="suspend_text_zygisk">Модул је суспендован јер %1$s није омогућено</string>
<string name="zygisk_module_unloaded">Zygisk модул није учитан због некомпатибилности</string>
<string name="module_empty">Ниједан модул није инсталиран</string>
<string name="confirm_install">Инсталирај модул %1$s?</string>
<string name="confirm_install_title">Потврда инсталације</string>
<!--Settings-->
<string name="settings_dark_mode_title">Тема</string>
<string name="settings_dark_mode_message">Изаберите тему која вам највише одговара!</string>
<string name="settings_dark_mode_light">Увек светло</string>
<string name="settings_dark_mode_system">Прати систем</string>
<string name="settings_dark_mode_dark">Увек тамно</string>
<string name="settings_download_path_title">Путања за преузимање</string>
<string name="settings_download_path_message">Фајлови ће бити сачувани на %1$s</string>
<string name="settings_hide_app_title">Сакриј Magisk апл.</string>
<string name="settings_hide_app_summary">Инсталирај proxy апликацију са насумичним ID-јем пакета и прилагођеном лабелом</string>
<string name="settings_restore_app_title">Поврати Magisk апл.</string>
<string name="settings_restore_app_summary">Откриј апл. и поврати оригинални APK</string>
<string name="language">Језик</string>
<string name="system_default">(Подразумевано системски)</string>
<string name="settings_check_update_title">Провери ажурирања</string>
<string name="settings_check_update_summary">Периодично провери ажурирања у позадини</string>
<string name="settings_update_channel_title">Канал ажурирања</string>
<string name="settings_update_stable">Стабилно</string>
<string name="settings_update_beta">Бета</string>
<string name="settings_update_custom">Прилагођено</string>
<string name="settings_update_custom_msg">Унеси прилагођени URL канала</string>
<string name="settings_zygisk_summary">Покрени делове Magisk-а у zygote daemon-у</string>
<string name="settings_denylist_title">Спроведи листу забрана</string>
<string name="settings_denylist_summary">Процеси на листи забрана ће повратити све Magisk измене</string>
<string name="settings_denylist_config_title">Конфигуриши листу забрана</string>
<string name="settings_denylist_config_summary">Изабери процесе који ће бити на листи забрана</string>
<string name="settings_hosts_title">Безсистемски домаћини (hosts)</string>
<string name="settings_hosts_summary">Подршка безсистемских домаћина за апликације блокирања реклама</string>
<string name="settings_hosts_toast">Модул безсистемских домаћина додат</string>
<string name="settings_app_name_hint">Ново име</string>
<string name="settings_app_name_helper">Апл. ће бити спакована под овим именом</string>
<string name="settings_app_name_error">Невалидан формат</string>
<string name="settings_su_app_adb">Апликације и ADB</string>
<string name="settings_su_app">Само апликације</string>
<string name="settings_su_adb">Само ADB</string>
<string name="settings_su_disable">Онемогућено</string>
<string name="settings_su_request_10">10 секунди</string>
<string name="settings_su_request_15">15 секунди</string>
<string name="settings_su_request_20">20 секунди</string>
<string name="settings_su_request_30">30 секунди</string>
<string name="settings_su_request_45">45 секунди</string>
<string name="settings_su_request_60">60 секунди</string>
<string name="superuser_access">Приступ супер-корисника</string>
<string name="auto_response">Аутоматски одговор</string>
<string name="request_timeout">Истек захтева</string>
<string name="superuser_notification">Нотификације супер-корисника</string>
<string name="settings_su_reauth_title">Поново одобри након ажурирања</string>
<string name="settings_su_reauth_summary">Поново тражи пермисије супер-корисника након ажурирања апликација</string>
<string name="settings_su_tapjack_title">Заштита од tapjacking-а</string>
<string name="settings_su_tapjack_summary">Prompt дијалог супер-корисника неће реаговати док је прикривен другим прозором или overlay-ем</string>
<string name="settings_su_auth_title">Аутентификација корисника</string>
<string name="settings_su_auth_summary">Тражи аутентификацију корисника током захтева супер-корисника</string>
<string name="settings_su_auth_insecure">Ниједан метод аутентификације није подешен на уређају</string>
<string name="settings_customization">Прилагођавање</string>
<string name="setting_add_shortcut_summary">Додај лепу пречицу на почетни екран у случају да се име и иконица не препознају лако након скривања апликације</string>
<string name="settings_doh_title">DNS преко HTTPS-а</string>
<string name="settings_doh_description">Заобилазно решење DNS тровања у неким нацијама</string>
<string name="settings_random_name_title">Насумично име на излазу</string>
<string name="settings_random_name_description">Насумично име излазног фајла слика и tar фајлова ради спречавања детекције</string>
<string name="multiuser_mode">Вишекориснички режим</string>
<string name="settings_owner_only">Само власник уређаја</string>
<string name="settings_owner_manage">Одређено од стране власника</string>
<string name="settings_user_independent">Независно од корисника</string>
<string name="owner_only_summary">Само власник има приступ корену</string>
<string name="owner_manage_summary">Само власник може да приступа корену и да прима захтеве за њега</string>
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
<string name="mount_namespace_mode">Mount режим namespace-а</string>
<string name="settings_ns_global">Глобални namespace</string>
<string name="settings_ns_requester">Наслеђени namespace</string>
<string name="settings_ns_isolate">Изоловани namespace</string>
<string name="global_summary">Све коренске сесије користе глобални mount namespace</string>
<string name="requester_summary">Коренске сесије ће наследити namespace од подносиоца захтева</string>
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани namespace</string>
<!--Notifications-->
<string name="update_channel">Ажурирања Magisk-а</string>
<string name="progress_channel">Нотификације о прогресу</string>
<string name="updated_channel">Ажурирање завршено</string>
<string name="download_complete">Преузимање завршено</string>
<string name="download_file_error">Грешка при преузимању фајла</string>
<string name="magisk_update_title">Ажурирање Magisk-а доступно!</string>
<string name="updated_title">Magisk је ажуриран</string>
<string name="updated_text">Кликни да отвориш апликацију</string>
<!--Toasts, Dialogs-->
<string name="yes">Да</string>
<string name="no">Не</string>
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
<string name="download">Преузми</string>
<string name="reboot">Поново покрени</string>
<string name="close">Затвори</string>
<string name="release_notes">Release notes</string>
<string name="flashing">Флешовање…</string>
<string name="running">Покретање…</string>
<string name="done">Завршено!</string>
<string name="done_action">Покретање акције %1$s завршено</string>
<string name="failure">Неуспешно!</string>
<string name="hide_app_title">Скривање Magisk апликације…</string>
<string name="open_link_failed_toast">Није пронађена апликација за отварање линка</string>
<string name="complete_uninstall">Комплетна деинсталација</string>
<string name="restore_img">Поврати слике</string>
<string name="restore_img_msg">Повратак…</string>
<string name="restore_done">Повратак успешан!</string>
<string name="restore_fail">Фабрички бекап не постоји!</string>
<string name="setup_fail">Неуспешна поставка</string>
<string name="env_fix_title">Потребно додатно подешавање</string>
<string name="env_fix_msg">Ваш уређај захтева додатно подешавање да би Magisk радио како треба. Да ли желите наставити и покренути поново?</string>
<string name="env_full_fix_msg">Ваш уређај захтева поновно флешовање да би Magisk радио како треба. Реинсталирајте Magisk кроз апликацију, режим опоравка не може добити тачне информације о уређају.</string>
<string name="setup_msg">Покретање подешавања окружења…</string>
<string name="unsupport_magisk_title">Неподржана верзија Magisk-а</string>
<string name="unsupport_magisk_msg">Ова верзија апликације не подржава Magisk верзије мање од %1$s.\n\nАпликација ће се понашати као да Magisk није инсталиран, молимо ажурирајте Magisk што пре.</string>
<string name="unsupport_general_title">Ненормално стање</string>
<string name="unsupport_system_app_msg">Покретање апликације као системске није подржано. Молимо поставите апликацију да буде корисничка.</string>
<string name="unsupport_other_su_msg">Детектован \"su\" binary који није Magisk-ов. Молимо уклоните конкурентно коренско решење и/или реинсталирајте Magisk.</string>
<string name="unsupport_external_storage_msg">Magisk је инсталиран на екстерно складиште. Молимо померите апл. у интерно складиште.</string>
<string name="unsupport_nonroot_stub_msg">Скривена Magisk апликација не може наставити са радом јер је корен изгубљен. Молимо повратите оригинални APK.</string>
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
<string name="external_rw_permission_denied">Дозволите пермисију за складиште да бисте омогућили ову функционалност</string>
<string name="post_notifications_denied">Дозволите пермисију за нотификације да бисте омогућили ову функционалност</string>
<string name="install_unknown_denied">Дозволите "инсталирање непознатих апликација" да бисте омогућили ову функционалност</string>
<string name="add_shortcut_title">Додај пречицу на почетни екран</string>
<string name="add_shortcut_msg">Након скривања апликације, њено име и иконицу ћете тешко препознати. Желите ли додати лепу пречицу на почетни екран?</string>
<string name="app_not_found">Није пронађена апликација за ову акцију</string>
<string name="reboot_apply_change">Поново покрени да примениш измене</string>
<string name="restore_app_confirmation">Ово ће вратити скривену апликацију на оригиналну. Да ли стварно то желите?</string>
</resources>

View File

@@ -30,5 +30,4 @@ android.nonFinalResIds=false
# Magisk
magisk.stubVersion=40
magisk.versionCode=28104
magisk.ondkVersion=r29.1
magisk.versionCode=29000

View File

@@ -1,14 +1,14 @@
[versions]
kotlin = "2.1.20"
android = "8.9.1"
android = "8.9.2"
ksp = "2.1.20-1.0.31"
rikka = "1.3.0"
navigation = "2.8.4"
navigation = "2.8.9"
libsu = "6.0.0"
moshi = "1.15.1"
moshi = "1.15.2"
okhttp = "4.12.0"
retrofit = "2.11.0"
room = "2.6.1"
room = "2.7.1"
[libraries]
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.80" }
@@ -26,12 +26,12 @@ timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "7.1.0.202411261347-r" }
# AndroidX
activity = { module = "androidx.activity:activity", version = "1.10.0" }
activity = { module = "androidx.activity:activity", version = "1.10.1" }
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" }
core-ktx = { module = "androidx.core:core-ktx", version = "1.15.0" }
core-ktx = { module = "androidx.core:core-ktx", version = "1.16.0" }
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.0" }
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.5" }
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.1" }
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.6" }
navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" }
profileinstaller = { module = "androidx.profileinstaller:profileinstaller", version = "1.4.1" }
@@ -40,10 +40,10 @@ room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" }
transition = { module = "androidx.transition:transition", version = "1.5.1" }
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.4.5" }
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.3" }
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" }

View File

View File

View File

@@ -8,4 +8,4 @@ dependencyResolutionManagement {
}
}
rootProject.name = "Magisk"
include(":app:apk", ":app:core", ":app:shared", ":app:stub", ":app:test", ":native")
include(":apk", ":core", ":shared", ":stub", ":test")

View File

@@ -41,5 +41,5 @@ android {
setupStubApk()
dependencies {
implementation(project(":app:shared"))
implementation(project(":shared"))
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="upgrade_msg">عليك الترقية Magisk لإكمال تهيئة التطبيق. هل تريد التنزيل والتثبيت؟</string>
<string name="no_internet_msg">يرجى اللإتصال بالإنترنت! ترقية Magisk مطلوبة.</string>
<string name="upgrade_msg">عليك بتحديث ماجيسك لإكمال تهيئة التطبيق. هل تريد التنزيل والتثبيت؟</string>
<string name="no_internet_msg">يرجى الإتصال بالإنترنت! تحديث ماجيسك مطلوب.</string>
<string name="dling">جارٍ التنزيل</string>
<string name="relaunch_app">يرجى إعادة تشغيل التطبيق يدوياً</string>
</resources>

View File

@@ -1 +1,7 @@
<resources></resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="upgrade_msg">Ажурирајте Magisk да бисте завршили постављање. Преузми и инсталирај?</string>
<string name="no_internet_msg">Молимо повежите се на интернет! Неопходно је ажурирање Magisk-а.</string>
<string name="dling">Преузимање</string>
<string name="relaunch_app">Молимо покрените апликацију поново</string>
</resources>

View File

@@ -1,7 +0,0 @@
plugins {
id("MagiskPlugin")
}
tasks.register("clean", Delete::class) {
delete(rootProject.layout.buildDirectory)
}

View File

@@ -67,9 +67,11 @@ support_abis = {
"x86_64": "x86_64-linux-android",
"riscv64": "riscv64-linux-android",
}
default_archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
default_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
support_targets = default_targets | {"resetprop"}
rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
ondk_version = "r29.1"
# Global vars
config = {}
@@ -160,6 +162,15 @@ def clean_elf():
run_cargo(cmds)
def collect_ndk_build():
for arch in build_abis.keys():
arch_dir = Path("native", "libs", arch)
out_dir = Path("native", "out", arch)
for source in arch_dir.iterdir():
target = out_dir / source.name
mv(source, target)
def run_ndk_build(cmds: list):
os.chdir("native")
cmds.append("NDK_PROJECT_PATH=.")
@@ -175,13 +186,6 @@ def run_ndk_build(cmds: list):
error("Build binary failed!")
os.chdir("..")
for arch in build_abis.keys():
arch_dir = Path("native", "libs", arch)
out_dir = Path("native", "out", arch)
for source in arch_dir.iterdir():
target = out_dir / source.name
mv(source, target)
def build_cpp_src(targets: set):
cmds = []
@@ -203,6 +207,7 @@ def build_cpp_src(targets: set):
if cmds:
run_ndk_build(cmds)
collect_ndk_build()
cmds.clear()
@@ -215,6 +220,7 @@ def build_cpp_src(targets: set):
if cmds:
cmds.append("B_CRT0=1")
run_ndk_build(cmds)
collect_ndk_build()
if clean:
clean_elf()
@@ -313,7 +319,7 @@ def build_native():
# Verify NDK install
try:
with open(Path(ndk_path, "ONDK_VERSION"), "r") as ondk_ver:
assert ondk_ver.read().strip(" \t\r\n") == config["ondkVersion"]
assert ondk_ver.read().strip(" \t\r\n") == ondk_version
except:
error('Unmatched NDK. Please install/upgrade NDK with "build.py ndk"')
@@ -378,16 +384,19 @@ def find_jdk():
def build_apk(module: str):
ensure_paths()
env = find_jdk()
props = args.config.resolve()
os.chdir("app")
build_type = "Release" if args.release else "Debug"
proc = execv(
[
gradlew,
f"{module}:assemble{build_type}",
f"-PconfigPath={args.config.resolve()}",
f"-PconfigPath={props}",
],
env=env,
)
os.chdir("..")
if proc.returncode != 0:
error(f"Build {module} failed!")
@@ -396,7 +405,7 @@ def build_apk(module: str):
paths = module.split(":")
apk = f"{paths[-1]}-{build_type}.apk"
source = Path(*paths, "build", "outputs", "apk", build_type, apk)
source = Path("app", *paths, "build", "outputs", "apk", build_type, apk)
target = config["outdir"] / apk
mv(source, target)
return target
@@ -404,7 +413,7 @@ def build_apk(module: str):
def build_app():
header("* Building the Magisk app")
apk = build_apk(":app:apk")
apk = build_apk(":apk")
build_type = "release" if args.release else "debug"
@@ -423,7 +432,7 @@ def build_app():
def build_stub():
header("* Building the stub app")
apk = build_apk(":app:stub")
apk = build_apk(":stub")
header(f"Output: {apk}")
@@ -434,7 +443,7 @@ def build_test():
args.release = True
try:
header("* Building the test app")
source = build_apk(":app:test")
source = build_apk(":test")
target = source.parent / "test.apk"
mv(source, target)
header(f"Output: {target}")
@@ -472,12 +481,15 @@ def cleanup():
rm(rs_gen)
if "native" in targets:
header("* Cleaning native")
rm_rf(Path("native", "out"))
rm_rf(Path("tools", "elf-cleaner", "target"))
if "app" in targets:
header("* Cleaning app")
execv([gradlew, ":app:clean"], env=find_jdk())
os.chdir("app")
execv([gradlew, ":clean"], env=find_jdk())
os.chdir("..")
def build_all():
@@ -491,8 +503,38 @@ def build_all():
############
def gen_ide():
ensure_paths()
set_archs({args.abi})
# Dump flags for both C++ and Rust code
dump_flag_header()
# Run build.rs to generate Rust/C++ FFI bindings
os.chdir(Path("native", "src"))
run_cargo(["check"])
os.chdir(Path("..", ".."))
# Generate compilation database
rm_rf(Path("native", "compile_commands.json"))
run_ndk_build(
[
"B_MAGISK=1",
"B_INIT=1",
"B_BOOT=1",
"B_POLICY=1",
"B_PRELOAD=1",
"B_PROP=1",
"B_CRT0=1",
"compile_commands.json",
]
)
def clippy_cli():
args.force_out = True
set_archs(default_archs)
os.chdir(Path("native", "src"))
cmds = ["clippy", "--no-deps", "--target"]
for triple in build_abis.values():
@@ -512,10 +554,9 @@ def cargo_cli():
def setup_ndk():
ensure_paths()
ndk_ver = config["ondkVersion"]
url = f"https://github.com/topjohnwu/ondk/releases/download/{ndk_ver}/ondk-{ndk_ver}-{os_name}.tar.xz"
url = f"https://github.com/topjohnwu/ondk/releases/download/{ondk_version}/ondk-{ondk_version}-{os_name}.tar.xz"
ndk_archive = url.split("/")[-1]
ondk_path = Path(ndk_root, f"ondk-{ndk_ver}")
ondk_path = Path(ndk_root, f"ondk-{ondk_version}")
header(f"* Downloading and extracting {ndk_archive}")
rm_rf(ondk_path)
@@ -663,7 +704,7 @@ def ensure_paths():
ndk_path / "toolchains" / "llvm" / "prebuilt" / f"{os_name}-x86_64" / "bin"
)
adb_path = sdk_path / "platform-tools" / "adb"
gradlew = Path.cwd() / "gradlew"
gradlew = Path.cwd() / "app" / "gradlew"
# We allow using several functionality with only ADB
@@ -694,6 +735,12 @@ def parse_props(file):
return props
def set_archs(archs: set):
triples = map(support_abis.get, archs)
global build_abis
build_abis = dict(zip(archs, triples))
def load_config():
commit_hash = cmd_out(["git", "rev-parse", "--short=8", "HEAD"])
@@ -708,8 +755,9 @@ def load_config():
if args.config.exists():
config.update(parse_props(args.config))
if Path("gradle.properties").exists():
for key, value in parse_props("gradle.properties").items():
gradle_props = Path("app", "gradle.properties")
if gradle_props.exists():
for key, value in parse_props(gradle_props).items():
if key.startswith("magisk."):
config[key[7:]] = value
@@ -725,12 +773,9 @@ def load_config():
abiList = re.split("\\s*,\\s*", config["abiList"])
archs = set(abiList) & support_abis.keys()
else:
archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
archs = default_archs
triples = map(support_abis.get, archs)
global build_abis
build_abis = dict(zip(archs, triples))
set_archs(archs)
def parse_args():
@@ -801,12 +846,16 @@ def parse_args():
"wrapper_dir", help="path to setup rustup wrapper binaries"
)
gen_parser = subparsers.add_parser("gen", help="generate files for IDE")
gen_parser.add_argument("--abi", default="arm64-v8a", help="target ABI to generate")
# Set callbacks
all_parser.set_defaults(func=build_all)
native_parser.set_defaults(func=build_native)
cargo_parser.set_defaults(func=cargo_cli)
clippy_parser.set_defaults(func=clippy_cli)
rustup_parser.set_defaults(func=setup_rustup)
gen_parser.set_defaults(func=gen_ide)
app_parser.set_defaults(func=build_app)
stub_parser.set_defaults(func=build_stub)
test_parser.set_defaults(func=build_test)

View File

@@ -1,5 +1,14 @@
# Magisk Changelog
### v29.0
- [General] Massive internal refactoring and code migration
- [App] Support downloading module zip files with XZ compression
- [App] Disable app animations when system animations are disabled
- [MagiskMount] Support systemlessly deleting files with modules using blank file nodes
- [MagiskInit] Redesign sepolicy patching and injection logic
- [MagiskSU] Better TTY/PTY support
### v28.1
- [App] Fix stub APK download link

16
docs/releases/29000.md Normal file
View File

@@ -0,0 +1,16 @@
## 2025.5.14 Magisk v29.0
This release looks minor at the surface, however, the entire codebase has gone through significant refactoring and migration. The native code in Magisk used to be mainly C++, but several contributors and I have been steadily rewriting parts of the code in Rust since April 2022. After years of effort, the Rust-ification of the project slowly began picking up steam, and at the moment of this release, over 40% of the native code has been rewritten in Rust, with several major subsystem rewrites in the PR queue, planned to be merged for the next release.
Many might wonder, why introduce a new language to the project? My reason is actually not to reduce memory safety issues (although it is a nice side benefit), but to be able to develop Magisk using a more modern programming language. After using Rust for a while, it's clear to me that using Rust allows me to write more correct code and makes me happier compared to dealing with C++. People share the [same sentiment as I do](https://threadreaderapp.com/thread/1577667445719912450.html).
## Changelog
- [General] Massive internal refactoring and code migration
- [App] Support downloading module zip files with XZ compression
- [App] Disable app animations when system animations are disabled
- [MagiskMount] Support systemlessly deleting files with modules using blank file nodes
- [MagiskInit] Redesign sepolicy patching and injection logic
- [MagiskSU] Better TTY/PTY support
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)

View File

@@ -1,5 +1,6 @@
# Release Notes
- [v29.0](29000.md)
- [v28.1](28100.md)
- [v28.0](28000.md)
- [v27.0](27000.md)

1
native/.gitignore vendored
View File

@@ -5,3 +5,4 @@ libs
/.cxx
*-rs.cpp
*-rs.hpp
/compile_commands.json

View File

@@ -1,7 +1,7 @@
use cxx::{ExternType, type_id};
use libc::c_char;
use std::borrow::Borrow;
use std::cmp::min;
use std::cmp::{Ordering, min};
use std::ffi::{CStr, FromBytesWithNulError, OsStr};
use std::fmt::{Debug, Display, Formatter, Write};
use std::ops::Deref;
@@ -467,6 +467,17 @@ macro_rules! impl_cstr_misc {
self.as_bytes_with_nul() == other.as_ref().as_bytes_with_nul()
}
}
impl<$($g)*> Eq for $t {}
impl<$($g)*> PartialOrd for $t {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<$($g)*> Ord for $t {
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
)*}
}

View File

@@ -143,7 +143,7 @@ string resolve_preinit_dir(const char *base_dir) {
if (access((dir + "/unencrypted").data(), F_OK) == 0) {
dir += "/unencrypted/magisk";
} else if (access((dir + "/adb").data(), F_OK) == 0) {
dir += "/adb/modules";
dir += "/adb";
} else if (access((dir + "/watchdog").data(), F_OK) == 0) {
dir += "/watchdog/magisk";
} else {

View File

@@ -261,6 +261,11 @@ impl Utf8CStr {
let mut path = cstr::buf::default();
let mut components = self.split('/').filter(|s| !s.is_empty());
if self.starts_with('/') {
path.append_path("/");
}
loop {
let Some(s) = components.next() else {
break;

View File

@@ -5,7 +5,7 @@ use crate::ffi::{
exec_module_scripts, get_magisk_tmp, initialize_denylist, setup_magisk_env,
};
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
use crate::mount::{clean_mounts, setup_mounts};
use crate::mount::{clean_mounts, setup_module_mount, setup_preinit_dir};
use crate::package::ManagerInfo;
use crate::selinux::restore_tmpcon;
use crate::su::SuInfo;
@@ -144,7 +144,7 @@ impl MagiskD {
Ordering::Release,
);
initialize_denylist();
setup_mounts();
setup_module_mount();
let modules = self.handle_modules();
self.module_list.set(modules).ok();
clean_mounts();
@@ -175,6 +175,7 @@ impl MagiskD {
secure_dir.mkdir(0o700).log_ok();
}
setup_preinit_dir();
self.ensure_manager();
self.zygisk_reset(true)
}
@@ -297,6 +298,7 @@ pub fn daemon_entry() {
tmp_path.append_path(ROOTMNT);
if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) {
BufReader::new(mount_list).foreach_lines(|line| {
line.truncate(line.trim_end().len());
let item = Utf8CStr::from_string(line);
item.unmount().log_ok();
true

View File

@@ -4,6 +4,7 @@
#![feature(fn_traits)]
#![feature(unix_socket_ancillary_data)]
#![feature(unix_socket_peek)]
#![feature(maybe_uninit_uninit_array)]
#![allow(clippy::missing_safety_doc)]
use crate::ffi::SuRequest;

View File

@@ -15,16 +15,13 @@ use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
use crate::ffi::{get_magisk_tmp, resolve_preinit_dir, switch_mnt_ns};
use crate::get_prop;
pub fn setup_mounts() {
info!("* Setup internal mounts");
pub fn setup_preinit_dir() {
let magisk_tmp = get_magisk_tmp();
// Mount preinit directory
let dev_path = cstr::buf::new::<64>()
.join_path(magisk_tmp)
.join_path(PREINITDEV);
let mut linked = false;
if let Ok(attr) = dev_path.get_attr() {
if attr.st.st_mode & libc::S_IFMT as c_uint == libc::S_IFBLK.as_() {
// DO NOT mount the block device directly, as we do not know the flags and configs
@@ -53,23 +50,21 @@ pub fn setup_mounts() {
mnt_path.create_symlink_to(preinit_dir)?;
};
if r.is_ok() {
linked = true;
break;
info!("* Found preinit dir: {}", preinit_dir);
return;
}
}
}
}
}
if !linked {
warn!("mount: preinit dir not found");
dev_path.remove().ok();
} else {
debug!("mount: preinit dir found");
}
warn!("mount: preinit dir not found");
}
pub fn setup_module_mount() {
// Bind remount module root to clear nosuid
let module_mnt = cstr::buf::default()
.join_path(magisk_tmp)
.join_path(get_magisk_tmp())
.join_path(MODULEMNT);
let _: LoggedResult<()> = try {
module_mnt.mkdir(0o755)?;

View File

@@ -1,25 +1,18 @@
use base::{
ResultExt, error,
libc::{
POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ,
cfmakeraw, close, poll, pollfd, raise, sigaddset, sigemptyset, signalfd, sigprocmask,
sigset_t, tcsetattr, winsize,
},
libc::{STDIN_FILENO, STDOUT_FILENO, tcgetattr, termios},
warn,
use base::{error, libc, warn};
use libc::{
POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH,
TIOCGWINSZ, TIOCSWINSZ, cfmakeraw, close, pipe, poll, pollfd, raise, read, sigaddset,
sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, splice, tcgetattr, tcsetattr,
termios, winsize,
};
use std::fs::File;
use std::io::{Read, Write};
use std::mem::ManuallyDrop;
use std::os::fd::{FromRawFd, RawFd};
use std::ptr::null_mut;
use std::{ffi::c_int, mem::MaybeUninit, ptr::null_mut};
static mut OLD_STDIN: Option<termios> = None;
const TIOCGPTN: u32 = 0x80045430;
unsafe extern "C" {
// Don't use the declaration from the libc crate as request should be u32 not i32
fn ioctl(fd: RawFd, request: u32, ...) -> i32;
fn ioctl(fd: c_int, request: u32, ...) -> i32;
}
pub fn get_pty_num(fd: i32) -> i32 {
@@ -79,6 +72,24 @@ fn resize_pty(outfd: i32) {
}
}
fn pump_via_pipe(infd: i32, outfd: i32, pipe: &[c_int; 2]) -> bool {
// usize::MAX will EINVAL in some kernels, use i32::MAX in case
let s = unsafe { splice(infd, null_mut(), pipe[1], null_mut(), i32::MAX as _, 0) };
if s < 0 {
error!("splice error");
return false;
}
if s == 0 {
return true;
}
let s = unsafe { splice(pipe[0], null_mut(), outfd, null_mut(), s as usize, 0) };
if s < 0 {
error!("splice error");
return false;
}
true
}
pub fn pump_tty(infd: i32, outfd: i32) {
set_stdin_raw();
@@ -112,7 +123,11 @@ pub fn pump_tty(infd: i32, outfd: i32) {
},
];
let mut buf = [0u8; 4096];
let mut p: [c_int; 2] = [0; 2];
if unsafe { pipe(&mut p as *mut c_int) } < 0 {
error!("pipe error");
return;
}
'poll: loop {
let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) };
@@ -123,21 +138,25 @@ pub fn pump_tty(infd: i32, outfd: i32) {
for pfd in &pfds {
if pfd.revents & POLLIN != 0 {
let mut in_file = ManuallyDrop::new(unsafe { File::from_raw_fd(pfd.fd) });
let Ok(n) = in_file.read(&mut buf) else {
error!("read error");
break 'poll;
};
if pfd.fd == STDIN_FILENO {
let mut out = ManuallyDrop::new(unsafe { File::from_raw_fd(outfd) });
out.write_all(&buf[..n]).log_ok();
let res = if pfd.fd == STDIN_FILENO {
pump_via_pipe(pfd.fd, outfd, &p)
} else if pfd.fd == infd {
let mut out = ManuallyDrop::new(unsafe { File::from_raw_fd(STDOUT_FILENO) });
out.write_all(&buf[..n]).log_ok();
pump_via_pipe(pfd.fd, STDOUT_FILENO, &p)
} else if pfd.fd == sfd {
resize_pty(outfd);
let mut buf: [MaybeUninit<u8>; size_of::<signalfd_siginfo>()] =
MaybeUninit::uninit_array();
if unsafe { read(pfd.fd, buf.as_mut_ptr() as *mut _, buf.len()) } < 0 {
error!("read error");
false
} else {
true
}
} else {
false
};
if !res {
break 'poll;
}
} else if pfd.revents != 0 && pfd.fd == infd {
unsafe { close(pfd.fd) };

View File

@@ -98,9 +98,9 @@ pub mod ffi {
fn patch_ro_root(self: &mut MagiskInit);
// Used in C++
unsafe fn setup_tmp(self: &MagiskInit, path: *const c_char);
unsafe fn setup_tmp(self: &mut MagiskInit, path: *const c_char);
fn collect_devices(self: &MagiskInit);
fn mount_preinit_dir(self: &MagiskInit);
fn mount_preinit_dir(self: &mut MagiskInit);
unsafe fn find_block(self: &MagiskInit, partname: *const c_char) -> u64;
unsafe fn patch_fissiond(self: &mut MagiskInit, tmp_path: *const c_char);
}

View File

@@ -103,11 +103,11 @@ uint64_t MagiskInit::find_block(const char *partname) const noexcept {
return 0;
}
void MagiskInit::mount_preinit_dir() const noexcept {
void MagiskInit::mount_preinit_dir() noexcept {
if (preinit_dev.empty()) return;
auto dev = find_block(preinit_dev.data());
auto dev = find_block(preinit_dev.c_str());
if (dev == 0) {
LOGE("Cannot find preinit %s, abort!\n", preinit_dev.data());
LOGE("Cannot find preinit %s, abort!\n", preinit_dev.c_str());
return;
}
xmknod(PREINITDEV, S_IFBLK | 0600, dev);
@@ -123,8 +123,7 @@ void MagiskInit::mount_preinit_dir() const noexcept {
// Since we are mounting the block device directly, make sure to ONLY mount the partitions
// as read-only, or else the kernel might crash due to crappy drivers.
// After the device boots up, magiskd will properly bind mount the correct partition
// on to PREINITMIRR as writable. For more details, check bootstages.cpp
// After the device boots up, magiskd will properly symlink the correct path at PREINITMIRR as writable.
if (mounted || mount(PREINITDEV, MIRRDIR, "ext4", MS_RDONLY, nullptr) == 0 ||
mount(PREINITDEV, MIRRDIR, "f2fs", MS_RDONLY, nullptr) == 0) {
string preinit_dir = resolve_preinit_dir(MIRRDIR);
@@ -138,8 +137,9 @@ void MagiskInit::mount_preinit_dir() const noexcept {
}
xumount2(MIRRDIR, MNT_DETACH);
} else {
PLOGE("Failed to mount preinit %s\n", preinit_dev.data());
unlink(PREINITDEV);
PLOGE("Mount preinit %s", preinit_dev.c_str());
// Do NOT delete the block device. Even though we cannot mount it here,
// it might get formatted later in the boot process.
}
}
@@ -210,7 +210,7 @@ mount_root:
return is_two_stage;
}
void MagiskInit::setup_tmp(const char *path) const noexcept {
void MagiskInit::setup_tmp(const char *path) noexcept {
LOGD("Setup Magisk tmp at %s\n", path);
chdir("/data");

View File

@@ -11,8 +11,6 @@ use std::ptr;
use std::thread::sleep;
use std::time::Duration;
const POLICY_VERSION: &Utf8CStr = cstr!("/selinux_version");
const MOCK_VERSION: &Utf8CStr = cstr!(concatcp!(SELINUXMOCK, "/version"));
const MOCK_LOAD: &Utf8CStr = cstr!(concatcp!(SELINUXMOCK, "/load"));
const MOCK_ENFORCE: &Utf8CStr = cstr!(concatcp!(SELINUXMOCK, "/enforce"));
@@ -91,6 +89,7 @@ impl MagiskInit {
cstr!(SELINUXMOCK).mkdir(0o711)?;
let mut rules = String::new();
let mut policy_ver = cstr!("/selinux_version");
let rule_file = cstr!(concatcp!("/data/", PREINITMIRR, "/sepolicy.rule"));
if rule_file.exists() {
debug!("Loading custom sepolicy patch: [{}]", rule_file);
@@ -108,9 +107,13 @@ impl MagiskInit {
if init.contains(split_plat_cil().as_str().as_bytes()) {
// Supports split policy
strat = SePatchStrategy::SelinuxFs;
} else if init.contains(POLICY_VERSION.as_bytes()) {
} else if init.contains(policy_ver.as_bytes()) {
// Does not support split policy, hijack /selinux_version
strat = SePatchStrategy::Legacy;
} else if init.contains(cstr!("/sepolicy_version").as_bytes()) {
// Samsung custom path
policy_ver = cstr!("/sepolicy_version");
strat = SePatchStrategy::Legacy;
} else {
error!("Unknown sepolicy setup, abort...");
return Ok(());
@@ -160,15 +163,15 @@ impl MagiskInit {
SePatchStrategy::Legacy => {
info!("SePatchStrategy: LEGACY");
if !POLICY_VERSION.exists() {
if !policy_ver.exists() {
// The file does not exist, create one
drop(POLICY_VERSION.create(libc::O_RDONLY, 0o666)?);
drop(policy_ver.create(libc::O_RDONLY, 0o666)?);
}
// The only purpose of this is to block init's control flow after it mounts
// selinuxfs and before it calls security_load_policy().
// selinux_android_load_policy() -> set_policy_index() -> open(POLICY_VERSION)
mock_fifo(POLICY_VERSION, MOCK_VERSION)?;
// selinux_android_load_policy() -> set_policy_index() -> open(policy_ver)
mock_fifo(policy_ver, MOCK_VERSION)?;
}
}
@@ -203,7 +206,7 @@ impl MagiskInit {
// This will unblock init at selinux_android_load_policy() -> set_policy_index().
drop(MOCK_VERSION.open(libc::O_WRONLY)?);
POLICY_VERSION.unmount()?;
policy_ver.unmount()?;
// libselinux does not read /selinux_version after open; instead it mmap the file,
// which can never succeed on FIFO files. This is fine as set_policy_index() will just

View File

@@ -134,7 +134,7 @@ test_main() {
if [ -z "$AVD_TEST_SKIP_DEBUG" ]; then
# Patch and test debug build
./build.py avd_patch "$ramdisk" magisk_patched.img
./build.py -v avd_patch "$ramdisk" magisk_patched.img
kill -INT $emu_pid
wait $emu_pid
test_emu debug $api
@@ -142,7 +142,7 @@ test_main() {
if [ -z "$AVD_TEST_SKIP_RELEASE" ]; then
# Patch and test release build
./build.py -r avd_patch "$ramdisk" magisk_patched.img
./build.py -vr avd_patch "$ramdisk" magisk_patched.img
kill -INT $emu_pid
wait $emu_pid
test_emu release $api

View File

@@ -85,11 +85,11 @@ test_main() {
adb wait-for-device
# Patch and test debug build
./build.py avd_patch "$CF_HOME/init_boot.img" magisk_patched.img
./build.py -v avd_patch "$CF_HOME/init_boot.img" magisk_patched.img
test_cf debug
# Patch and test release build
./build.py -r avd_patch "$CF_HOME/init_boot.img" magisk_patched.img
./build.py -vr avd_patch "$CF_HOME/init_boot.img" magisk_patched.img
test_cf release
# Cleanup

View File

@@ -11,7 +11,7 @@ CONFIG=config.prop
NOTES=notes.md
# These are constants, do not modify
GCONFIG=gradle.properties
GCONFIG=app/gradle.properties
README=README.MD
BUILDCMD="./build.py -c $CONFIG"
CWD=$(pwd)