mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-03-14 14:07:04 -07:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ded94550a5 | ||
|
|
e8a58776f1 | ||
|
|
07e92948c8 | ||
|
|
74ead75ce8 | ||
|
|
bb082e703c | ||
|
|
ee25db0627 | ||
|
|
9ee6a6b3d7 | ||
|
|
e872fafd8b | ||
|
|
dd3798905f | ||
|
|
c07eac118c | ||
|
|
4dd6e3c252 | ||
|
|
0d39b10889 | ||
|
|
628b4d4715 | ||
|
|
be5246aef5 | ||
|
|
b55f597ccf | ||
|
|
e729eec636 | ||
|
|
a657af5dc9 | ||
|
|
34137b112a | ||
|
|
5f13a8f8f7 | ||
|
|
8495a03947 | ||
|
|
ac8d4200e1 | ||
|
|
e590ffbdcb | ||
|
|
97138f1e91 | ||
|
|
bea50fafba | ||
|
|
0919db6b11 | ||
|
|
18c1347bd3 | ||
|
|
0bbc736051 | ||
|
|
01cb75eaef | ||
|
|
33eaa7c5eb | ||
|
|
8734423cb0 | ||
|
|
da9e72b2a2 | ||
|
|
ff4ca74cfe | ||
|
|
200665c48a | ||
|
|
dd42aa99ea | ||
|
|
0936cdb192 | ||
|
|
871643dce2 | ||
|
|
a510554b21 | ||
|
|
9cc830c565 | ||
|
|
ddbac50645 | ||
|
|
b5138a4af0 | ||
|
|
64752f38e8 | ||
|
|
9ac4b5ce7d | ||
|
|
505053f9b4 | ||
|
|
ccb264f33a | ||
|
|
84f7d75d30 | ||
|
|
9a776c22d9 |
5
.github/kvm.sh
vendored
Executable file
5
.github/kvm.sh
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
43
.github/workflows/build.yml
vendored
43
.github/workflows/build.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
@@ -39,14 +39,14 @@ jobs:
|
||||
run: ./app/gradlew --stop
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: out
|
||||
compression-level: 9
|
||||
|
||||
- name: Upload mapping and native debug symbols
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ github.sha }}-symbols
|
||||
path: app/apk/build/outputs
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
os: [windows-2025, ubuntu-24.04]
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
@@ -82,27 +82,24 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, "CANARY"]
|
||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36.1, "CANARY"]
|
||||
type: [""]
|
||||
include:
|
||||
- version: "CANARY"
|
||||
- version: "CinnamonBun"
|
||||
type: "google_apis_ps16k"
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: out
|
||||
|
||||
- name: Enable KVM group perms
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
run: .github/kvm.sh
|
||||
|
||||
- name: Run AVD test
|
||||
timeout-minutes: 15
|
||||
@@ -112,7 +109,7 @@ jobs:
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "avd-logs-${{ matrix.version }}"
|
||||
path: |
|
||||
@@ -131,19 +128,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: out
|
||||
|
||||
- name: Enable KVM group perms
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
run: .github/kvm.sh
|
||||
|
||||
- name: Run AVD test
|
||||
timeout-minutes: 15
|
||||
@@ -154,7 +148,7 @@ jobs:
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "avd32-logs-${{ matrix.version }}"
|
||||
path: |
|
||||
@@ -177,14 +171,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: out
|
||||
|
||||
- name: Enable KVM group perms
|
||||
run: .github/kvm.sh
|
||||
|
||||
- name: Setup Cuttlefish environment
|
||||
run: |
|
||||
scripts/cuttlefish.sh setup
|
||||
@@ -196,7 +193,7 @@ jobs:
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "cvd-logs-${{ matrix.device }}"
|
||||
path: |
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,9 +1,6 @@
|
||||
[submodule "selinux"]
|
||||
path = native/src/external/selinux
|
||||
url = https://github.com/topjohnwu/selinux.git
|
||||
[submodule "lz4"]
|
||||
path = native/src/external/lz4
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "libcxx"]
|
||||
path = native/src/external/libcxx
|
||||
url = https://github.com/topjohnwu/libcxx.git
|
||||
|
||||
2
app/.gitignore
vendored
2
app/.gitignore
vendored
@@ -3,5 +3,5 @@
|
||||
# Gradle
|
||||
.gradle
|
||||
.kotlin
|
||||
build
|
||||
/local.properties
|
||||
/build
|
||||
|
||||
1
app/apk/.gitignore
vendored
1
app/apk/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,8 +1,7 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
kotlin("plugin.parcelize")
|
||||
kotlin("kapt")
|
||||
id("com.android.legacy-kapt")
|
||||
id("androidx.navigation.safeargs.kotlin")
|
||||
}
|
||||
|
||||
@@ -26,6 +25,10 @@ android {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
proguardFile("proguard-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
|
||||
3
app/apk/proguard-rules.pro
vendored
Normal file
3
app/apk/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Excessive obfuscation
|
||||
-flattenpackagehierarchy
|
||||
-allowaccessmodification
|
||||
@@ -104,7 +104,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
override fun onItemAction(view: View, item: BaseSettingsItem) {
|
||||
when (item) {
|
||||
Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().navigate()
|
||||
LanguageSystem -> launchAppLocaleSettings(view.activity)
|
||||
LanguageSystem -> view.activity.startActivity(LocaleSetting.localeSettingsIntent)
|
||||
AddShortcut -> AddHomeIconEvent().publish()
|
||||
SystemlessHosts -> createHosts()
|
||||
DenyListConfig -> SettingsFragmentDirections.actionSettingsFragmentToDenyFragment().navigate()
|
||||
@@ -116,12 +116,6 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchAppLocaleSettings(activity: Activity) {
|
||||
val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS)
|
||||
intent.data = Uri.fromParts("package", activity.packageName, null)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun openUrlIfNecessary(view: View) {
|
||||
UpdateChannelUrl.refresh()
|
||||
if (UpdateChannelUrl.isEnabled && UpdateChannelUrl.value.isBlank()) {
|
||||
|
||||
1
app/buildSrc/.gitignore
vendored
1
app/buildSrc/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,5 +1,3 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
@@ -21,6 +19,7 @@ gradlePlugin {
|
||||
dependencies {
|
||||
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
|
||||
implementation(libs.android.gradle.plugin)
|
||||
implementation(libs.android.kapt.plugin)
|
||||
implementation(libs.ksp.plugin)
|
||||
implementation(libs.navigation.safe.args.plugin)
|
||||
implementation(libs.lsparanoid.plugin)
|
||||
|
||||
@@ -46,11 +46,25 @@ class MagiskPlugin : Plugin<Project> {
|
||||
private fun Project.applyPlugin() {
|
||||
initRandom(rootProject.file("dict.txt"))
|
||||
props.clear()
|
||||
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
|
||||
|
||||
// Get gradle properties relevant to Magisk
|
||||
props.putAll(properties.filter { (key, _) -> key.startsWith("magisk.") })
|
||||
|
||||
// Load config.prop
|
||||
val configPath: String? by this
|
||||
val config = rootFile(configPath ?: "config.prop")
|
||||
if (config.exists())
|
||||
config.inputStream().use { props.load(it) }
|
||||
val configFile = rootFile(configPath ?: "config.prop")
|
||||
if (configFile.exists()) {
|
||||
configFile.inputStream().use {
|
||||
val config = Properties()
|
||||
config.load(it)
|
||||
// Remove properties that should be passed by commandline
|
||||
config.remove("abiList")
|
||||
props.putAll(config)
|
||||
}
|
||||
}
|
||||
|
||||
// Commandline override
|
||||
findProperty("abiList")?.let { props.put("abiList", it) }
|
||||
|
||||
val repo = FileRepository(rootFile(".git"))
|
||||
val refId = repo.refDatabase.exactRef("HEAD").objectId
|
||||
|
||||
@@ -1,72 +1,70 @@
|
||||
import com.android.build.api.artifact.SingleArtifact
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
|
||||
import com.android.build.api.instrumentation.InstrumentationScope
|
||||
import com.android.build.api.variant.AndroidComponentsExtension
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.LibraryExtension
|
||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.StopExecutionException
|
||||
import org.gradle.api.tasks.Sync
|
||||
import org.gradle.kotlin.dsl.assign
|
||||
import org.gradle.kotlin.dsl.exclude
|
||||
import org.gradle.kotlin.dsl.filter
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.getValue
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.security.MessageDigest
|
||||
import java.util.HexFormat
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
private fun Project.androidBase(configure: Action<BaseExtension>) =
|
||||
private fun Project.android(configure: Action<CommonExtension>) =
|
||||
extensions.configure("android", configure)
|
||||
|
||||
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
|
||||
private fun Project.androidApp(configure: Action<ApplicationExtension>) =
|
||||
extensions.configure("android", configure)
|
||||
|
||||
internal val Project.androidApp: BaseAppModuleExtension
|
||||
get() = extensions["android"] as BaseAppModuleExtension
|
||||
internal val Project.androidApp: ApplicationExtension
|
||||
get() = extensions["android"] as ApplicationExtension
|
||||
|
||||
private val Project.androidLib: LibraryExtension
|
||||
get() = extensions["android"] as LibraryExtension
|
||||
private fun Project.androidComponents(configure: Action<AndroidComponentsExtension<*, *, *>>) =
|
||||
extensions.configure(AndroidComponentsExtension::class.java, configure)
|
||||
|
||||
internal val Project.androidComponents
|
||||
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
|
||||
private val Project.androidComponents: AndroidComponentsExtension<*, *, *>
|
||||
get() = extensions["androidComponents"] as AndroidComponentsExtension<*, *, *>
|
||||
|
||||
internal fun Project.androidAppComponents(configure: Action<ApplicationAndroidComponentsExtension>) =
|
||||
extensions.configure(ApplicationAndroidComponentsExtension::class.java, configure)
|
||||
|
||||
fun Project.setupCommon() {
|
||||
androidBase {
|
||||
compileSdkVersion(36)
|
||||
buildToolsVersion = "36.0.0"
|
||||
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||
android {
|
||||
compileSdk {
|
||||
version = release(36) {
|
||||
minorApiLevel = 1
|
||||
}
|
||||
}
|
||||
buildToolsVersion = "36.1.0"
|
||||
ndkPath = "${androidComponents.sdkComponents.sdkDirectory.get().asFile}/ndk/magisk"
|
||||
ndkVersion = "29.0.14206865"
|
||||
|
||||
defaultConfig {
|
||||
defaultConfig.apply {
|
||||
minSdk = 23
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
compileOptions.apply {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
packaging.apply {
|
||||
resources {
|
||||
excludes += arrayOf(
|
||||
"/META-INF/*",
|
||||
@@ -123,93 +121,108 @@ const val BUSYBOX_DOWNLOAD_URL =
|
||||
const val BUSYBOX_ZIP_CHECKSUM =
|
||||
"b4d0551feabaf314e53c79316c980e8f66432e9fb91a69dbbf10a93564b40951"
|
||||
|
||||
private abstract class SyncWithDir : Sync() {
|
||||
@get:OutputDirectory
|
||||
abstract val outputFolder: DirectoryProperty
|
||||
}
|
||||
|
||||
fun Project.setupCoreLib() {
|
||||
setupCommon()
|
||||
|
||||
androidLib.libraryVariants.all {
|
||||
val variant = name
|
||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||
val abiList = Config.abiList
|
||||
val abiList = Config.abiList
|
||||
|
||||
val syncLibs = tasks.register("sync${variantCapped}JniLibs", Sync::class) {
|
||||
into("src/$variant/jniLibs")
|
||||
for (abi in abiList) {
|
||||
into(abi) {
|
||||
from(rootFile("native/out/$abi")) {
|
||||
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
|
||||
rename { if (it.endsWith(".so")) it else "lib$it.so" }
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
val variantName = variant.name
|
||||
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
||||
|
||||
val syncLibs = tasks.register("sync${variantCapped}JniLibs", SyncWithDir::class) {
|
||||
outputFolder.set(layout.buildDirectory.dir("$variantName/jniLibs"))
|
||||
into(outputFolder)
|
||||
|
||||
for (abi in abiList) {
|
||||
into(abi) {
|
||||
from(rootFile("native/out/$abi")) {
|
||||
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
|
||||
rename { if (it.endsWith(".so")) it else "lib$it.so" }
|
||||
}
|
||||
}
|
||||
}
|
||||
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
|
||||
include(abiList.map { "$it/libbusybox.so" })
|
||||
onlyIf {
|
||||
if (inputs.sourceFiles.files.size != abiList.size * 6)
|
||||
throw StopExecutionException("Please build binaries first! (./build.py binary)")
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
variant.sources.jniLibs?.let {
|
||||
it.addGeneratedSourceDirectory(syncLibs, SyncWithDir::outputFolder)
|
||||
}
|
||||
|
||||
val syncResources = tasks.register("sync${variantCapped}Resources", SyncWithDir::class) {
|
||||
outputFolder.set(layout.buildDirectory.dir("$variantName/resources"))
|
||||
into(outputFolder)
|
||||
|
||||
into("META-INF/com/google/android") {
|
||||
from(rootFile("scripts/update_binary.sh")) {
|
||||
rename { "update-binary" }
|
||||
}
|
||||
from(rootFile("scripts/flash_script.sh")) {
|
||||
rename { "updater-script" }
|
||||
}
|
||||
}
|
||||
}
|
||||
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
|
||||
include(abiList.map { "$it/libbusybox.so" })
|
||||
onlyIf {
|
||||
if (inputs.sourceFiles.files.size != abiList.size * 6)
|
||||
throw StopExecutionException("Please build binaries first! (./build.py binary)")
|
||||
true
|
||||
|
||||
variant.sources.resources?.let {
|
||||
it.addGeneratedSourceDirectory(syncResources, SyncWithDir::outputFolder)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(syncLibs)
|
||||
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
||||
val syncAssets = tasks.register("sync${variantCapped}Assets", SyncWithDir::class) {
|
||||
outputFolder.set(layout.buildDirectory.dir("$variantName/assets"))
|
||||
into(outputFolder)
|
||||
|
||||
val syncResources = tasks.register("sync${variantCapped}Resources", Sync::class) {
|
||||
into("src/$variant/resources/META-INF/com/google/android")
|
||||
from(rootFile("scripts/update_binary.sh")) {
|
||||
rename { "update-binary" }
|
||||
}
|
||||
from(rootFile("scripts/flash_script.sh")) {
|
||||
rename { "updater-script" }
|
||||
}
|
||||
}
|
||||
|
||||
processJavaResourcesProvider.configure { dependsOn(syncResources) }
|
||||
|
||||
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
||||
val stubApk = stubTask.outputs.files.asFileTree.filter {
|
||||
it.name.endsWith(".apk")
|
||||
}
|
||||
|
||||
val syncAssets = tasks.register("sync${variantCapped}Assets", Sync::class) {
|
||||
dependsOn(stubTask)
|
||||
inputs.property("version", Config.version)
|
||||
inputs.property("versionCode", Config.versionCode)
|
||||
into("src/$variant/assets")
|
||||
from(rootFile("scripts")) {
|
||||
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
|
||||
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
|
||||
}
|
||||
from(rootFile("tools/bootctl"))
|
||||
into("chromeos") {
|
||||
from(rootFile("tools/futility"))
|
||||
from(rootFile("tools/keys")) {
|
||||
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||
inputs.property("version", Config.version)
|
||||
inputs.property("versionCode", Config.versionCode)
|
||||
from(rootFile("scripts")) {
|
||||
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
|
||||
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
|
||||
}
|
||||
from(rootFile("tools/bootctl"))
|
||||
into("chromeos") {
|
||||
from(rootFile("tools/futility"))
|
||||
from(rootFile("tools/keys")) {
|
||||
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||
}
|
||||
}
|
||||
from(stubTask) {
|
||||
include { it.name.endsWith(".apk") }
|
||||
rename { "stub.apk" }
|
||||
}
|
||||
filesMatching("**/util_functions.sh") {
|
||||
filter {
|
||||
it.replace(
|
||||
"#MAGISK_VERSION_STUB",
|
||||
"MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}"
|
||||
)
|
||||
}
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
}
|
||||
from(stubApk) {
|
||||
rename { "stub.apk" }
|
||||
}
|
||||
filesMatching("**/util_functions.sh") {
|
||||
filter {
|
||||
it.replace(
|
||||
"#MAGISK_VERSION_STUB",
|
||||
"MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}"
|
||||
)
|
||||
}
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
|
||||
variant.sources.assets?.let {
|
||||
it.addGeneratedSourceDirectory(syncAssets, SyncWithDir::outputFolder)
|
||||
}
|
||||
}
|
||||
mergeAssetsProvider.configure { dependsOn(syncAssets) }
|
||||
}
|
||||
|
||||
tasks.named<Delete>("clean") {
|
||||
delete.addAll(listOf("src/main/jniLibs", "src/main/resources", "src/debug", "src/release"))
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.setupAppCommon() {
|
||||
setupCommon()
|
||||
|
||||
android {
|
||||
androidApp {
|
||||
signingConfigs {
|
||||
Config["keyStore"]?.also {
|
||||
create("config") {
|
||||
@@ -254,22 +267,25 @@ fun Project.setupAppCommon() {
|
||||
}
|
||||
}
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
val commentTask = tasks.register(
|
||||
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
|
||||
AddCommentTask::class.java
|
||||
)
|
||||
val transformationRequest = variant.artifacts.use(commentTask)
|
||||
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
|
||||
.toTransformMany(SingleArtifact.APK)
|
||||
val signingConfig = androidApp.buildTypes.getByName(variant.buildType!!).signingConfig
|
||||
commentTask.configure {
|
||||
this.transformationRequest = transformationRequest
|
||||
this.signingConfig = signingConfig
|
||||
this.comment = "version=${Config.version}\n" +
|
||||
"versionCode=${Config.versionCode}\n" +
|
||||
"stubVersion=${Config.stubVersion}\n"
|
||||
this.outFolder.set(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
|
||||
androidAppComponents {
|
||||
onVariants { variant ->
|
||||
val commentTask = tasks.register(
|
||||
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
|
||||
AddCommentTask::class.java
|
||||
)
|
||||
val transformationRequest = variant.artifacts.use(commentTask)
|
||||
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
|
||||
.toTransformMany(SingleArtifact.APK)
|
||||
val signingConfig = androidApp.buildTypes.getByName(variant.buildType!!).signingConfig
|
||||
commentTask.configure {
|
||||
this.transformationRequest = transformationRequest
|
||||
this.signingConfig = signingConfig
|
||||
this.comment = "version=${Config.version}\n" +
|
||||
"versionCode=${Config.versionCode}\n" +
|
||||
"stubVersion=${Config.stubVersion}\n"
|
||||
this.outFolder.set(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,7 +293,7 @@ fun Project.setupAppCommon() {
|
||||
fun Project.setupMainApk() {
|
||||
setupAppCommon()
|
||||
|
||||
android {
|
||||
androidApp {
|
||||
namespace = "com.topjohnwu.magisk"
|
||||
|
||||
defaultConfig {
|
||||
@@ -290,8 +306,10 @@ fun Project.setupMainApk() {
|
||||
debugSymbolLevel = "FULL"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
variant.instrumentation.apply {
|
||||
setAsmFramesComputationMode(COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)
|
||||
transformClassesWith(
|
||||
@@ -314,17 +332,26 @@ const val SHAMIKO_CHECKSUM =
|
||||
fun Project.setupTestApk() {
|
||||
setupAppCommon()
|
||||
|
||||
androidApp.applicationVariants.all {
|
||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||
val dlTask by tasks.register("download${variantCapped}Lsposed", Sync::class) {
|
||||
from(downloadFile(LSPOSED_DOWNLOAD_URL, LSPOSED_CHECKSUM)) {
|
||||
rename { "lsposed.zip" }
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
val variantName = variant.name
|
||||
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
||||
|
||||
val dlTask = tasks.register("download${variantCapped}Lsposed", SyncWithDir::class) {
|
||||
outputFolder.set(layout.buildDirectory.dir("$variantName/lsposed"))
|
||||
into(outputFolder)
|
||||
|
||||
from(downloadFile(LSPOSED_DOWNLOAD_URL, LSPOSED_CHECKSUM)) {
|
||||
rename { "lsposed.zip" }
|
||||
}
|
||||
from(downloadFile(SHAMIKO_DOWNLOAD_URL, SHAMIKO_CHECKSUM)) {
|
||||
rename { "shamiko.zip" }
|
||||
}
|
||||
}
|
||||
from(downloadFile(SHAMIKO_DOWNLOAD_URL, SHAMIKO_CHECKSUM)) {
|
||||
rename { "shamiko.zip" }
|
||||
|
||||
variant.sources.assets?.let {
|
||||
it.addGeneratedSourceDirectory(dlTask, SyncWithDir::outputFolder)
|
||||
}
|
||||
into("src/${this@all.name}/assets")
|
||||
}
|
||||
mergeAssetsProvider.configure { dependsOn(dlTask) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@ import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.assign
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
@@ -82,18 +83,16 @@ private abstract class ManifestUpdater: DefaultTask() {
|
||||
@get:Input
|
||||
abstract val applicationId: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val factoryClass: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val appClass: Property<String>
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val mergedManifest: RegularFileProperty
|
||||
|
||||
@get:InputFiles
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val factoryClassDir: DirectoryProperty
|
||||
|
||||
@get:InputFiles
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val appClassDir: DirectoryProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputManifest: RegularFileProperty
|
||||
|
||||
@@ -170,25 +169,18 @@ private abstract class ManifestUpdater: DefaultTask() {
|
||||
|
||||
// Shuffle the order of the components
|
||||
cmpList.shuffle(RANDOM)
|
||||
val (factoryPkg, factoryClass) = factoryClassDir.asFileTree.firstNotNullOf {
|
||||
it.parentFile!!.name to it.name.removeSuffix(".java")
|
||||
}
|
||||
val (appPkg, appClass) = appClassDir.asFileTree.firstNotNullOf {
|
||||
it.parentFile!!.name to it.name.removeSuffix(".java")
|
||||
}
|
||||
val components = cmpList.joinToString("\n\n")
|
||||
.replace("\${applicationId}", applicationId.get())
|
||||
val manifest = mergedManifest.asFile.get().readText().replace(Regex(".*\\<application"), """
|
||||
|<application
|
||||
| android:appComponentFactory="$factoryPkg.$factoryClass"
|
||||
| android:name="$appPkg.$appClass"""".ind(1)
|
||||
| android:appComponentFactory="${factoryClass.get()}"
|
||||
| android:name="${appClass.get()}"""".ind(1)
|
||||
).replace(Regex(".*\\<\\/application"), "$components\n </application")
|
||||
outputManifest.get().asFile.writeText(manifest)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||
private fun genStubClasses(outDir: File): Pair<String, String> {
|
||||
val classNameGenerator = sequence {
|
||||
fun notJavaKeyword(name: String) = when (name) {
|
||||
"do", "if", "for", "int", "new", "try" -> false
|
||||
@@ -217,7 +209,7 @@ private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||
}
|
||||
}.distinct().iterator()
|
||||
|
||||
fun genClass(type: String, outDir: File) {
|
||||
fun genClass(type: String, outDir: File): String {
|
||||
val clzName = classNameGenerator.next()
|
||||
val (pkg, name) = clzName.split('.')
|
||||
val pkgDir = File(outDir, pkg)
|
||||
@@ -226,10 +218,12 @@ private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||
it.println("package $pkg;")
|
||||
it.println("public class $name extends com.topjohnwu.magisk.$type {}")
|
||||
}
|
||||
return clzName
|
||||
}
|
||||
|
||||
genClass("DelegateComponentFactory", factoryOutDir)
|
||||
genClass("StubApplication", appOutDir)
|
||||
val factory = genClass("DelegateComponentFactory", outDir)
|
||||
val app = genClass("StubApplication", outDir)
|
||||
return Pair(factory, app)
|
||||
}
|
||||
|
||||
private fun genEncryptedResources(res: ByteArray, outDir: File) {
|
||||
@@ -264,74 +258,76 @@ private fun genEncryptedResources(res: ByteArray, outDir: File) {
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class TaskWithDir : DefaultTask() {
|
||||
@get:OutputDirectory
|
||||
abstract val outputFolder: DirectoryProperty
|
||||
}
|
||||
|
||||
fun Project.setupStubApk() {
|
||||
setupAppCommon()
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
val variantName = variant.name
|
||||
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
||||
val manifestUpdater =
|
||||
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
|
||||
dependsOn("generate${variantCapped}ObfuscatedClass")
|
||||
applicationId = variant.applicationId
|
||||
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
|
||||
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
|
||||
}
|
||||
variant.artifacts.use(manifestUpdater)
|
||||
.wiredWithFiles(
|
||||
ManifestUpdater::mergedManifest,
|
||||
ManifestUpdater::outputManifest)
|
||||
.toTransform(SingleArtifact.MERGED_MANIFEST)
|
||||
}
|
||||
androidAppComponents {
|
||||
onVariants { variant ->
|
||||
val variantName = variant.name
|
||||
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
||||
val variantLowered = variantName.lowercase()
|
||||
|
||||
androidApp.applicationVariants.all {
|
||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||
val variantLowered = name.lowercase()
|
||||
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
|
||||
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
|
||||
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
|
||||
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
|
||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
|
||||
val componentJavaOutDir = layout.buildDirectory
|
||||
.dir("generated/${variantLowered}/components").get().asFile
|
||||
|
||||
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
|
||||
inputs.property("seed", RAND_SEED)
|
||||
outputs.dirs(outFactoryClassDir, outAppClassDir)
|
||||
doLast {
|
||||
outFactoryClassDir.mkdirs()
|
||||
outAppClassDir.mkdirs()
|
||||
genStubClasses(outFactoryClassDir, outAppClassDir)
|
||||
}
|
||||
}
|
||||
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
|
||||
val (factory, app) = genStubClasses(componentJavaOutDir)
|
||||
|
||||
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
|
||||
outputs.dir(outResDir)
|
||||
doLast {
|
||||
val apkTmp = File("${apk}.tmp")
|
||||
providers.exec {
|
||||
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||
}.result.get()
|
||||
|
||||
val bos = ByteArrayOutputStream()
|
||||
ZipFile(apkTmp).use { src ->
|
||||
ZipOutputStream(apk.outputStream()).use {
|
||||
it.setLevel(Deflater.BEST_COMPRESSION)
|
||||
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
||||
it.closeEntry()
|
||||
}
|
||||
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
||||
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
||||
}
|
||||
val manifestUpdater =
|
||||
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
|
||||
applicationId = variant.applicationId
|
||||
factoryClass.set(factory)
|
||||
appClass.set(app)
|
||||
}
|
||||
apkTmp.delete()
|
||||
genEncryptedResources(bos.toByteArray(), outResDir)
|
||||
variant.artifacts.use(manifestUpdater)
|
||||
.wiredWithFiles(
|
||||
ManifestUpdater::mergedManifest,
|
||||
ManifestUpdater::outputManifest)
|
||||
.toTransform(SingleArtifact.MERGED_MANIFEST)
|
||||
|
||||
val aapt = sdkComponents.aapt2.get().executable.get().asFile
|
||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||
"${variantLowered}/process${variantCapped}Resources/" +
|
||||
"linked-resources-binary-format-${variantLowered}.ap_").get().asFile
|
||||
|
||||
val genResourcesTask = tasks.register("generate${variantCapped}BundledResources", TaskWithDir::class) {
|
||||
dependsOn("process${variantCapped}Resources")
|
||||
outputFolder.set(layout.buildDirectory.dir("generated/${variantLowered}/resources"))
|
||||
|
||||
doLast {
|
||||
val apkTmp = File("${apk}.tmp")
|
||||
providers.exec {
|
||||
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||
}.result.get()
|
||||
|
||||
val bos = ByteArrayOutputStream()
|
||||
ZipFile(apkTmp).use { src ->
|
||||
ZipOutputStream(apk.outputStream()).use {
|
||||
it.setLevel(Deflater.BEST_COMPRESSION)
|
||||
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
||||
it.closeEntry()
|
||||
}
|
||||
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
||||
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
||||
}
|
||||
}
|
||||
apkTmp.delete()
|
||||
genEncryptedResources(bos.toByteArray(), outputFolder.get().asFile)
|
||||
}
|
||||
}
|
||||
|
||||
variant.sources.java?.let {
|
||||
it.addStaticSourceDirectory(componentJavaOutDir.path)
|
||||
it.addGeneratedSourceDirectory(genResourcesTask, TaskWithDir::outputFolder)
|
||||
}
|
||||
}
|
||||
|
||||
registerJavaGeneratingTask(processResourcesTask, outResDir)
|
||||
}
|
||||
|
||||
// Override optimizeReleaseResources task
|
||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
|
||||
|
||||
3
app/core/.gitignore
vendored
3
app/core/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
/build
|
||||
src/debug
|
||||
src/release
|
||||
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
kotlin("plugin.parcelize")
|
||||
id("dev.zacsweers.moshix")
|
||||
id("com.google.devtools.ksp")
|
||||
|
||||
4
app/core/proguard-rules.pro
vendored
4
app/core/proguard-rules.pro
vendored
@@ -33,9 +33,5 @@
|
||||
# is used.
|
||||
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
|
||||
|
||||
# Excessive obfuscation
|
||||
-flattenpackagehierarchy
|
||||
-allowaccessmodification
|
||||
|
||||
-dontwarn org.junit.**
|
||||
-dontwarn org.apache.**
|
||||
|
||||
@@ -4,9 +4,12 @@ import android.annotation.SuppressLint
|
||||
import android.app.LocaleConfig
|
||||
import android.app.LocaleManager
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.LocaleList
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.topjohnwu.magisk.core.AppApkPath
|
||||
import com.topjohnwu.magisk.core.AppContext
|
||||
@@ -30,8 +33,8 @@ interface LocaleSetting {
|
||||
|
||||
private val systemLocale: Locale = Locale.getDefault()
|
||||
|
||||
override var currentLocale: Locale = systemLocale
|
||||
override var appLocale: Locale? = null
|
||||
override var currentLocale: Locale = systemLocale
|
||||
|
||||
init {
|
||||
setLocale(Config.locale)
|
||||
@@ -164,9 +167,18 @@ interface LocaleSetting {
|
||||
LocaleConfig.fromContextIgnoringOverride(context)
|
||||
}
|
||||
|
||||
val useLocaleManager get() =
|
||||
if (isRunningAsStub) Build.VERSION.SDK_INT >= 34
|
||||
else Build.VERSION.SDK_INT >= 33
|
||||
private val localeManagerUsable get() =
|
||||
if (isRunningAsStub) Build.VERSION.SDK_INT >= 34 else Build.VERSION.SDK_INT >= 33
|
||||
|
||||
val useLocaleManager by lazy {
|
||||
localeManagerUsable &&
|
||||
localeSettingsIntent.resolveActivity(AppContext.packageManager) != null
|
||||
}
|
||||
|
||||
val localeSettingsIntent get() = Intent(
|
||||
Settings.ACTION_APP_LOCALE_SETTINGS,
|
||||
Uri.fromParts("package", AppContext.packageName, null),
|
||||
)
|
||||
|
||||
val instance: LocaleSetting by lazy {
|
||||
// Initialize available locale list
|
||||
|
||||
@@ -15,18 +15,18 @@
|
||||
<string name="app_changelog">앱 변경 사항</string>
|
||||
<string name="loading">로딩중…</string>
|
||||
<string name="update">업데이트</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="not_available">알 수 없음</string>
|
||||
<string name="hide">숨기기</string>
|
||||
<string name="home_package">패키지</string>
|
||||
<string name="home_app_title">앱</string>
|
||||
|
||||
<string name="home_notice_content">공식 Github 페이지에서 Magisk를 다운로드하십시오. 알 수 없는 소스의 파일이 악의적일 수 있습니다!</string>
|
||||
<string name="home_notice_content">공식 Github 페이지에서 Magisk를 다운로드하십시오. 알 수 없는 출처에서 받은 파일이 위험할 수 있습니다!</string>
|
||||
<string name="home_support_title">후원하기</string>
|
||||
<string name="home_item_source">소스</string>
|
||||
<string name="home_support_content">Magisk는 항상 무료일 것이며, 오픈소스일 것입니다. 그러나 소액의 후원을 통해 관심을 표할 수 있습니다.</string>
|
||||
<string name="home_installed_version">설치됨</string>
|
||||
<string name="home_latest_version">최신</string>
|
||||
<string name="invalid_update_channel">올바르지 않은 업데이트 채널</string>
|
||||
<string name="invalid_update_channel">잘못된 업데이트 채널</string>
|
||||
<string name="uninstall_magisk_title">Magisk 제거</string>
|
||||
<string name="uninstall_magisk_msg">모든 모듈이 비활성화/제거됩니다. 루트도 제거될 것이며, 데이터도 암호화 되어있지 않으면 암호화될 수도 있습니다.</string>
|
||||
|
||||
@@ -51,11 +51,11 @@
|
||||
<!--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>
|
||||
<string name="su_warning">기기에 대한 전체 액세스 권한을 부여합니다.\n확실하지 않은 경우 거부하세요!</string>
|
||||
<string name="forever">영구적으로</string>
|
||||
<string name="deny">모두 거부</string>
|
||||
<string name="prompt">물어보기</string>
|
||||
<string name="grant">모두 허용</string>
|
||||
<string name="su_warning">기기에 대한 슈퍼유저 권한을 부여합니다.\n확실하지 않은 경우 거부하세요!</string>
|
||||
<string name="forever">영구</string>
|
||||
<string name="once">한 번만</string>
|
||||
<string name="tenmin">10분</string>
|
||||
<string name="twentymin">20분</string>
|
||||
@@ -69,20 +69,20 @@
|
||||
<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_revoke_title">취소하시겠습니까?</string>
|
||||
<string name="su_revoke_msg">정말 %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="superuser_toggle_notification">알림</string>
|
||||
<string name="superuser_toggle_revoke">권한삭제</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="menuClearLog">로그 삭제</string>
|
||||
<string name="logs_cleared">로그 삭제 완료.</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Target UID: %1$d</string>
|
||||
@@ -96,9 +96,9 @@
|
||||
<string name="hide_search">검색</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(제공된 정보 없음)</string>
|
||||
<string name="no_info_provided">(정보 없음)</string>
|
||||
<string name="reboot_userspace">조용히 다시 시작</string>
|
||||
<string name="reboot_recovery">리커버리로 다시 시작</string>
|
||||
<string name="reboot_recovery">복구 모드로 다시 시작</string>
|
||||
<string name="reboot_bootloader">부트로더로 다시 시작</string>
|
||||
<string name="reboot_download">다운로드 모드로 다시 시작</string>
|
||||
<string name="reboot_edl">EDL로 다시 시작</string>
|
||||
@@ -107,20 +107,20 @@
|
||||
<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="suspend_text_riru">%1$s 가 활성화 되어있어 모듈이 로드되지 않았습니다.</string>
|
||||
<string name="suspend_text_zygisk">%1$s 가 활성화되어 있지 않아 모듈이 로드되지 않았습니다.</string>
|
||||
<string name="zygisk_module_unloaded">호환성 문제로 인해 Zygisk 모듈이 로드되지 않았습니다.</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_system">자동</string>
|
||||
<string name="settings_dark_mode_dark">다크 모드</string>
|
||||
<string name="settings_download_path_title">다운로드 경로</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">랜덤 패키지 ID와 커스텀 앱 이름으로 Magisk 프록시 앱을 설치합니다.</string>
|
||||
<string name="settings_hide_app_summary">무작위 패키지명과 사용자 지정 앱 이름으로 Magisk 프록시 앱을 설치합니다.</string>
|
||||
<string name="settings_restore_app_title">Magisk 앱 복원</string>
|
||||
<string name="settings_restore_app_summary">앱 숨기기를 해제하고 원래 APK로 복원합니다.</string>
|
||||
<string name="language">언어</string>
|
||||
@@ -167,12 +167,12 @@
|
||||
<string name="settings_doh_description">일부 국가에 존재하는 DNS 포이즈닝을 해결합니다.</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="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>
|
||||
@@ -187,7 +187,7 @@
|
||||
<string name="progress_channel">진행 상황</string>
|
||||
<string name="updated_channel">업데이트 완료</string>
|
||||
<string name="download_complete">다운로드 완료</string>
|
||||
<string name="download_file_error">파일 다운로드 오류</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>
|
||||
@@ -208,7 +208,7 @@
|
||||
<string name="restore_img">이미지 복구</string>
|
||||
<string name="restore_img_msg">복구하는 중…</string>
|
||||
<string name="restore_done">복구 완료!</string>
|
||||
<string name="restore_fail">원 백업이 존재하지 않습니다!</string>
|
||||
<string name="restore_fail">백업이 존재하지 않습니다!</string>
|
||||
<string name="setup_fail">설치 실패</string>
|
||||
<string name="env_fix_title">추가 설정 필요</string>
|
||||
<string name="env_fix_msg">Magisk가 제대로 작동하려면 추가 설정이 필요합니다. 다시 시작 하시겠습니까?</string>
|
||||
@@ -219,10 +219,10 @@
|
||||
<string name="unsupport_system_app_msg">해당 앱을 시스템 앱으로 실행하는 것은 지원되지 않습니다. 앱을 일반 사용자 앱으로 실행해 주세요.</string>
|
||||
<string name="unsupport_other_su_msg">Magisk으로 부터 설치되지 않은 \"su\" 바이너리가 감지되었습니다. 다른 루팅 방법을 제거하거나, Magisk 를 다시 설치해주세요.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk 가 외부 저장소에 설치되어 있습니다. Magisk 를 내부 저장소에 설치 해주세요.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">숨겨진 Magisk 앱은 루팅이 풀려 더이상 작동하지 못합니다. 본래 APK 를 복원하거나 재설치 해주세요.</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="install_unknown_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>
|
||||
|
||||
@@ -2,242 +2,255 @@
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">Modulet</string>
|
||||
<string name="superuser">Super-përdoruesi</string>
|
||||
<string name="logs">Regjistrat</string>
|
||||
<string name="settings">Cilësimet</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="logs">Regjistrimet</string>
|
||||
<string name="settings">Parametrat</string>
|
||||
<string name="install">Instalo</string>
|
||||
<string name="section_home">Shtëpi</string>
|
||||
<string name="section_theme">Tema</string>
|
||||
<string name="denylist">Lista e mohimit</string>
|
||||
<string name="section_home">Shtëpia</string>
|
||||
<string name="section_theme">Temat</string>
|
||||
<string name="denylist">Lista e ndaluar</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Nuk ka lidhje interneti</string>
|
||||
<string name="app_changelog">Ndryshimet</string>
|
||||
<string name="loading">Po ngarkohet…</string>
|
||||
<string name="no_connection">Nuk ka lidhje të disponueshme</string>
|
||||
<string name="app_changelog">Shënimet e ndryshimeve</string>
|
||||
<string name="loading">Duke u ngarkuar…</string>
|
||||
<string name="update">Përditëso</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Fshih</string>
|
||||
<string name="home_package">Paketa</string>
|
||||
<string name="home_app_title">App</string>
|
||||
|
||||
<string name="home_notice_content">Shkarkoni Magisk VETEM nga faqja zyrtare e GitHub. Skedarët nga burime të panjohura mund të jenë me qëllim të keq! </string>
|
||||
<string name="home_app_title">Aplikacioni</string>
|
||||
<string name="home_notice_content">Shkarkoni Magisk VETËM nga faqja zyrtare në GitHub. Skedarët nga burime të panjohura mund të jenë të dëmshëm!</string>
|
||||
<string name="home_support_title">Na mbështetni</string>
|
||||
<string name="home_follow_title">Na ndiqni</string>
|
||||
<string name="home_item_source">Burimi</string>
|
||||
<string name="home_support_content">Magisk është, dhe gjithmonë do të jetë, falas dhe me burim të hapur. Sidoqoftë, mund të na tregoni se kujdeseni duke dërguar një donacion të vogël.</string>
|
||||
<string name="home_support_content">Magisk është dhe do të mbetet gjithmonë falas dhe me burim të hapur. Megjithatë, mund të na mbështesni duke bërë një donacion.</string>
|
||||
<string name="home_installed_version">Instaluar</string>
|
||||
<string name="home_latest_version">E fundit</string>
|
||||
<string name="invalid_update_channel">Kanali i përditësimit i pavlefshëm</string>
|
||||
<string name="home_latest_version">Më i fundit</string>
|
||||
<string name="invalid_update_channel">Kanal i pavlefshëm për përditësime</string>
|
||||
<string name="uninstall_magisk_title">Çinstalo Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Të gjitha modulet do të çaktivizohen/hiqen!\nRrënja do të hiqet!\nTë dhënat tuaja potencialisht të koduara nëse jo tashmë!</string>
|
||||
<string name="uninstall_magisk_msg">Të gjitha modulet do të çaktivizohen/hiqen!
|
||||
Root-i do të hiqet!
|
||||
Çdo memorie e brendshme që është çenkriptuar përmes Magisk do të rikriptohet!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Ruaj kriptimin me forcë</string>
|
||||
<string name="keep_force_encryption">Ruaj enkriptimin e detyruar</string>
|
||||
<string name="keep_dm_verity">Ruaj AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">Recovery Mode</string>
|
||||
<string name="recovery_mode">Mënyra Recovery</string>
|
||||
<string name="install_options_title">Opsionet</string>
|
||||
<string name="install_method_title">Metoda</string>
|
||||
<string name="install_next">Tjetër</string>
|
||||
<string name="install_start">Shkojme</string>
|
||||
<string name="manager_download_install">Shtypni për ta shkarkuar dhe instaluarl</string>
|
||||
<string name="direct_install">Instalimi i direkt (Rekomandohet)</string>
|
||||
<string name="install_inactive_slot">Instaloni në slotin joaktiv(Pas OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Pajisja juaj do të detyrohet të fillojë në folenë aktuale joaktive pas një rindezje!\nPërdoreni këtë opsion vetëm pasi të keni përfunduar OTA.\nVazhdo?</string>
|
||||
<string name="setup_title">Konfigurimet shtesë</string>
|
||||
<string name="select_patch_file">Zgjidhni dhe Patch një skader</string>
|
||||
<string name="patch_file_msg">Zgjidhni një imazh të papërpunuar (*.img) ose një skedar ODIN (*.tar) ose një payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Rinisje pas 5 sekondash…</string>
|
||||
<string name="install_next">Vazhdoni</string>
|
||||
<string name="install_start">Le të fillojmë</string>
|
||||
<string name="manager_download_install">Shtypni për të shkarkuar dhe instaluar</string>
|
||||
<string name="direct_install">Instalim i drejtpërdrejtë (Rekomandohet)</string>
|
||||
<string name="install_inactive_slot">Instalo në slot-in joaktiv (Pas OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Pajisja juaj do të detyrohet të niset në slot-in joaktiv pas rinisjes!
|
||||
Përdorni këtë opsion vetëm pasi OTA të ketë përfunduar.
|
||||
Të vazhdoj?</string>
|
||||
<string name="setup_title">Konfigurim shtesë</string>
|
||||
<string name="select_patch_file">Zgjidh dhe përpuno një skedar</string>
|
||||
<string name="patch_file_msg">Zgjidh një imazh të papërpunuar (*.img) ose një skedar ODIN (*.tar) ose një payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Rinisja pas 5 sekondash…</string>
|
||||
<string name="flash_screen_title">Instalimi</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Kërkesë nga superpërdoruesi</string>
|
||||
<string name="touch_filtered_warning">Për shkak se një aplikacion po errëson një kërkesë të superpërdoruesit, Magisk nuk mund të verifikojë përgjigjen tuaj</string>
|
||||
<string name="su_request_title">Kërkesë Superuser</string>
|
||||
<string name="touch_filtered_warning">Për shkak se një aplikacion po mbivendos kërkesën Superuser, Magisk nuk mund të verifikojë përgjigjen tuaj.</string>
|
||||
<string name="deny">Refuzo</string>
|
||||
<string name="prompt">Pyet</string>
|
||||
<string name="prompt">Pyete</string>
|
||||
<string name="restrict">Kufizo</string>
|
||||
<string name="grant">Lejo</string>
|
||||
<string name="su_warning">Jep akses të plotë në pajisjen tuaj.\nRefuzo nëse nuk jeni të sigurt!</string>
|
||||
<string name="forever">Gjithmonë</string>
|
||||
<string name="su_warning">Jep akses të plotë në pajisjen tuaj.
|
||||
Refuzoni nëse nuk jeni të sigurt!</string>
|
||||
<string name="forever">Përgjithmonë</string>
|
||||
<string name="once">Një herë</string>
|
||||
<string name="tenmin">10 minuta</string>
|
||||
<string name="twentymin">20 minuta</string>
|
||||
<string name="thirtymin">30 minuta</string>
|
||||
<string name="sixtymin">60 minuta</string>
|
||||
<string name="su_allow_toast">%1$s iu dha aksesi te Super-përdoruesi</string>
|
||||
<string name="su_deny_toast">%1$s iu refuzua aksesi te Super -përdoruesi</string>
|
||||
<string name="su_snack_grant">Aksesi i super-përdoruesit te %1$s është lenuar</string>
|
||||
<string name="su_snack_deny">Aksesi i super-përdoruesit te %1$s është refuzuar</string>
|
||||
<string name="su_snack_notif_on">Njoftimet e %1$s janë aktivizuar</string>
|
||||
<string name="su_snack_notif_off">Njoftimet e %1$s janë çaktivizuar</string>
|
||||
<string name="su_snack_log_on">Regjistrat e %1$s janë aktivizuar</string>
|
||||
<string name="su_snack_log_off">Regjistrat e %1$s janë çaktivizuar</string>
|
||||
<string name="su_revoke_title">Të drejtat?</string>
|
||||
<string name="su_revoke_msg">Konfirmo për të hequr të drejtat e %1$s?</string>
|
||||
<string name="toast">Dolli</string>
|
||||
<string name="su_allow_toast">%1$s mori të drejtat Superuser</string>
|
||||
<string name="su_deny_toast">%1$s u refuzua të drejtat Superuser</string>
|
||||
<string name="su_snack_grant">%1$s mori të drejtat Superuser</string>
|
||||
<string name="su_snack_deny">%1$s u refuzua të drejtat Superuser</string>
|
||||
<string name="su_snack_notif_on">Njoftimet për %1$s u aktivizuan</string>
|
||||
<string name="su_snack_notif_off">Njoftimet për %1$s u çaktivizuan</string>
|
||||
<string name="su_snack_log_on">Regjistrimi për %1$s u aktivizua</string>
|
||||
<string name="su_snack_log_off">Regjistrimi për %1$s u çaktivizua</string>
|
||||
<string name="su_revoke_title">Të hiqen?</string>
|
||||
<string name="su_revoke_msg">Konfirmoni heqjen e të drejtave Superuser për %1$s</string>
|
||||
<string name="toast">Njoftim</string>
|
||||
<string name="none">Asnjë</string>
|
||||
<string name="superuser_toggle_notification">Njoftimet</string>
|
||||
<string name="superuser_toggle_revoke">Të drejtat</string>
|
||||
<string name="superuser_policy_none">Asnjë aplikacion nuk ka kërkuar akoma akses për super-përdoruesin.</string>
|
||||
<string name="superuser_toggle_revoke">Hiq</string>
|
||||
<string name="superuser_policy_none">Asnjë aplikacion nuk ka kërkuar ende leje Superuser.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Nuk ka regjistra, provoni të përdorni më shumë aplikacionet tuaja me SU</string>
|
||||
<string name="log_data_magisk_none">Regjistrat Magisk janë bosh, kjo është e çuditshme</string>
|
||||
<string name="menuSaveLog">Ruaj regjistrar</string>
|
||||
<string name="menuClearLog">Pastro regjistrat tani</string>
|
||||
<string name="logs_cleared">Regjistrat u pastuan me sukses</string>
|
||||
<string name="log_data_none">Nuk keni regjistrime. Provojeni të përdorni më shumë aplikacionet me root.</string>
|
||||
<string name="log_data_magisk_none">Regjistrimet e Magisk janë bosh — çuditërisht.</string>
|
||||
<string name="menuSaveLog">Ruaj regjistrimin</string>
|
||||
<string name="menuClearLog">Pastro regjistrimin tani</string>
|
||||
<string name="logs_cleared">Regjistrimet u pastruan me sukses</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Target UID: %1$d</string>
|
||||
<string name="target_pid">Montoni PID të synuar ns: %s</string>
|
||||
<string name="target_uid">UID i synuar: %1$d</string>
|
||||
<string name="target_pid">PID i synuar: %s</string>
|
||||
<string name="selinux_context">Konteksti SELinux: %s</string>
|
||||
<string name="supp_group">Grupi suplementar: %s</string>
|
||||
<string name="supp_group">Grupi shtesë: %s</string>
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Shfaq aplikacionet e sistemit</string>
|
||||
<string name="show_os_app">Shfaq aplikacionet e sistemit operativ</string>
|
||||
<string name="hide_filter_hint">Kërko sipas emrit</string>
|
||||
<string name="show_os_app">Shfaq aplikacionet e OS</string>
|
||||
<string name="hide_filter_hint">Filtro sipas emrit</string>
|
||||
<string name="hide_search">Kërko</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Nuk ka asnjë informacion)</string>
|
||||
<string name="reboot_userspace">Rinisje e shpejtë</string>
|
||||
<string name="reboot_recovery">Rinis te Recovery</string>
|
||||
<string name="reboot_bootloader">Rinis te Bootloader</string>
|
||||
<string name="reboot_download">Rinis te Download</string>
|
||||
<string name="reboot_edl">Rinis te EDL</string>
|
||||
<string name="reboot_safe_mode">Rinis në safe mode</string>
|
||||
<string name="no_info_provided">(Nuk u dha informacion)</string>
|
||||
<string name="reboot_userspace">Rinisje Normale</string>
|
||||
<string name="reboot_recovery">Rinis në Recovery</string>
|
||||
<string name="reboot_bootloader">Rinis në Bootloader</string>
|
||||
<string name="reboot_download">Rinis në Download</string>
|
||||
<string name="reboot_edl">Rinis në EDL</string>
|
||||
<string name="reboot_safe_mode">Mënyra e sigurt</string>
|
||||
<string name="module_version_author">%1$s nga %2$s</string>
|
||||
<string name="module_state_remove">Hiqe</string>
|
||||
<string name="module_action">Veprim</string>
|
||||
<string name="module_state_restore">Rikëthe</string>
|
||||
<string name="module_action_install_external">Instaloni nga sdcard</string>
|
||||
<string name="update_available">Përditësimi në dispozicion</string>
|
||||
<string name="suspend_text_riru">Moduli u pezullua sepse %1$s është aktivizuar</string>
|
||||
<string name="suspend_text_zygisk">Moduli është pezulluar sepse %1$s nuk është i aktivizuar</string>
|
||||
<string name="zygisk_module_unloaded">Moduli Zygisk nuk është ngarkuar për shkak të papajtueshmërisë</string>
|
||||
<string name="module_empty">Ska module të instaluar</string>
|
||||
<string name="module_action">Veprimi</string>
|
||||
<string name="module_state_restore">Rikthe</string>
|
||||
<string name="module_action_install_external">Instalo nga memoria</string>
|
||||
<string name="update_available">Përditësim i disponueshëm</string>
|
||||
<string name="suspend_text_riru">Moduli u pezullua sepse %1$s është aktiv</string>
|
||||
<string name="suspend_text_zygisk">Moduli u pezullua sepse %1$s nuk është aktiv</string>
|
||||
<string name="zygisk_module_unloaded">Moduli Zygisk nuk u ngarkua për shkak të mospërputhjes</string>
|
||||
<string name="module_empty">Nuk ka module të instaluara</string>
|
||||
<string name="confirm_install">Të instalohet moduli %1$s?</string>
|
||||
<string name="confirm_install_title">Konfirmo instalimin</string>
|
||||
<string name="confirm_install_title">Konfirmim instalimi</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Mënyra e temës</string>
|
||||
<string name="settings_dark_mode_message">Zgjidhni mënyrën që i përshtatet më shumë stilit tuaj!</string>
|
||||
<string name="settings_dark_mode_light">Gjithmonë e bardhë</string>
|
||||
<string name="settings_dark_mode_system">Sipas sistemit</string>
|
||||
<string name="settings_dark_mode_dark">Gjithmonë e zezë</string>
|
||||
<string name="settings_download_path_title">Vendodhje e shkarkimit</string>
|
||||
<string name="settings_download_path_message">Shkarkimet do të ruhen në %1$s</string>
|
||||
<string name="settings_hide_app_title">Fsheh aplikacionin Magisk</string>
|
||||
<string name="settings_hide_app_summary">Instaloni një aplikacion përfaqësues me ID të paketës të rastësishme dhe etiketë të personalizuar të aplikacionitl</string>
|
||||
<string name="settings_restore_app_title">Rivendosni aplikacionin Magisk</string>
|
||||
<string name="settings_restore_app_summary">un-fsheh aplikacionin dhe riktheni atë në APK origjinale</string>
|
||||
<string name="settings_dark_mode_message">Zgjidh mënyrën që i përshtatet më shumë stilit tënd!</string>
|
||||
<string name="settings_dark_mode_light">Gjithmonë e ndritshme</string>
|
||||
<string name="settings_dark_mode_system">Ndiq sistemin</string>
|
||||
<string name="settings_dark_mode_dark">Gjithmonë e errët</string>
|
||||
<string name="settings_download_path_title">Rruga e shkarkimit</string>
|
||||
<string name="settings_download_path_message">Skedarët do të ruhen në %1$s</string>
|
||||
<string name="settings_hide_app_title">Fshi aplikacionin Magisk</string>
|
||||
<string name="settings_hide_app_summary">Instalo një aplikacion proxy me një ID pakete të rastësishme dhe emër të personalizuar</string>
|
||||
<string name="settings_restore_app_title">Rikthe aplikacionin Magisk</string>
|
||||
<string name="settings_restore_app_summary">Zbulo aplikacionin dhe rikthe APK-në origjinale</string>
|
||||
<string name="language">Gjuha</string>
|
||||
<string name="system_default">(Parazgjedhja e sistemit)</string>
|
||||
<string name="settings_check_update_title">Kontrollo për përditësime</string>
|
||||
<string name="settings_check_update_summary">Kontrolloni automatikisht për përditësime në sfond</string>
|
||||
<string name="settings_update_channel_title">Perditeso kanalin</string>
|
||||
<string name="settings_update_stable">E qëndrueshme</string>
|
||||
<string name="settings_check_update_summary">Kontrollo periodikisht për përditësimet në sfond</string>
|
||||
<string name="settings_update_channel_title">Kanal për përditësime</string>
|
||||
<string name="settings_update_stable">Stable</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Kanal me porosi</string>
|
||||
<string name="settings_update_custom_msg">Fut një URL të personalizuar</string>
|
||||
<string name="settings_zygisk_summary">Drejtoni pjesë të Magisk në demonin zygote</string>
|
||||
<string name="settings_denylist_title">Zbato Listën e Mohimit</string>
|
||||
<string name="settings_denylist_summary">Proceset në listën e mohimit do të kenë të gjitha modifikimet e Magisk</string>
|
||||
<string name="settings_denylist_config_title">Konfiguro Listën e Mohimit</string>
|
||||
<string name="settings_denylist_config_summary">Zgjidhni proceset që do të përfshihen në listën e mohimit</string>
|
||||
<string name="settings_hosts_title">Pritësit pa sistem</string>
|
||||
<string name="settings_hosts_summary">Pritësit pa sistem mbështesin aplikacionet Adblock</string>
|
||||
<string name="settings_hosts_toast">Moduli i hosteve pa sistem u shtua</string>
|
||||
<string name="settings_app_name_hint">Emri i ri</string>
|
||||
<string name="settings_app_name_helper">Aplikacioni do të ripaketohet me këtë emër</string>
|
||||
<string name="settings_update_debug">Debug</string>
|
||||
<string name="settings_update_custom">Custom</string>
|
||||
<string name="settings_update_custom_msg">Fut një URL të personalizuar të kanalit</string>
|
||||
<string name="settings_zygisk_summary">Ekzekuto pjesë të Magisk në demonin Zygote</string>
|
||||
<string name="settings_denylist_title">Zbato listën e ndaluar</string>
|
||||
<string name="settings_denylist_summary">Proceset në listën e ndaluar do të rikthehen pa modifikimet e Magisk</string>
|
||||
<string name="settings_denylist_config_title">Konfiguro listën e ndaluar</string>
|
||||
<string name="settings_denylist_config_summary">Zgjidh proceset që do të përfshihen në listën e ndaluar</string>
|
||||
<string name="settings_hosts_title">Systemless hosts</string>
|
||||
<string name="settings_hosts_summary">Mbështetje për systemless hosts për aplikacionet që bllokojnë reklamat</string>
|
||||
<string name="settings_hosts_toast">U shtua moduli systemless hosts</string>
|
||||
<string name="settings_app_name_hint">Emër i ri</string>
|
||||
<string name="settings_app_name_helper">Aplikacioni do të ripaketizohet me këtë emër</string>
|
||||
<string name="settings_app_name_error">Format i pavlefshëm</string>
|
||||
<string name="settings_su_app_adb">Aplikacionet dhe ADB</string>
|
||||
<string name="settings_su_app">Vetëm aplikacionet</string>
|
||||
<string name="settings_su_adb">Vetëm ADB</string>
|
||||
<string name="settings_su_disable">Çaktivizuar</string>
|
||||
<string name="settings_su_request_10">10 Sekonda</string>
|
||||
<string name="settings_su_request_15">15 Sekonda</string>
|
||||
<string name="settings_su_request_20">20 Sekonda</string>
|
||||
<string name="settings_su_request_30">30 Sekonda</string>
|
||||
<string name="settings_su_request_45">45 Sekonda</string>
|
||||
<string name="settings_su_request_60">60 Sekonda</string>
|
||||
<string name="superuser_access">Aksesi i Super-përdorues</string>
|
||||
<string name="settings_su_request_10">10 sekonda</string>
|
||||
<string name="settings_su_request_15">15 sekonda</string>
|
||||
<string name="settings_su_request_20">20 sekonda</string>
|
||||
<string name="settings_su_request_30">30 sekonda</string>
|
||||
<string name="settings_su_request_45">45 sekonda</string>
|
||||
<string name="settings_su_request_60">60 sekonda</string>
|
||||
<string name="superuser_access">Akses Superuser</string>
|
||||
<string name="auto_response">Përgjigje automatike</string>
|
||||
<string name="request_timeout">Koha për mbarimit të Kërkesës</string>
|
||||
<string name="superuser_notification">Njoftimi i Super-përdoruesit</string>
|
||||
<string name="settings_su_reauth_title">Ri-vërtetimi pas azhurnimit</string>
|
||||
<string name="settings_su_reauth_summary">Ri-vërtetoni lejet e super-përdoruesit pas azhurnimit të aplikacionit</string>
|
||||
<string name="settings_su_tapjack_title">Aktivizo mbrojtjen tapjacking</string>
|
||||
<string name="settings_su_tapjack_summary">Dialogu i menjëhershëm i super-përdoruesit nuk do ti përgjigjet hyrjes ndërsa është i errësuar nga ndonjë dritare ose mbivendosje tjetër</string>
|
||||
<string name="request_timeout">Koha e skadimit të kërkesës</string>
|
||||
<string name="superuser_notification">Njoftimi Superuser</string>
|
||||
<string name="settings_su_reauth_title">Riautentifikimi pas përditësimit</string>
|
||||
<string name="settings_su_reauth_summary">Kërko sërish lejet Superuser pas përditësimit të aplikacioneve</string>
|
||||
<string name="settings_su_tapjack_title">Mbrojtje nga mbivendosja e klikimeve</string>
|
||||
<string name="settings_su_tapjack_summary">Dritarja e kërkesës Superuser nuk do të pranojë input kur është e mbuluar nga ndonjë dritare tjetër</string>
|
||||
<string name="settings_su_auth_title">Autentifikimi i përdoruesit</string>
|
||||
<string name="settings_su_auth_summary">Kërko autentifikim të përdoruesit gjatë kërkesave Superuser</string>
|
||||
<string name="settings_su_auth_insecure">Nuk ka asnjë metodë autentifikimi të konfiguruar në pajisje</string>
|
||||
<string name="settings_su_restrict_title">Kufizo aftësitë e root</string>
|
||||
<string name="settings_su_restrict_summary">Do të kufizojë aplikacionet e reja Superuser si parazgjedhje. Kujdes: kjo mund të prishë shumicën e aplikacioneve. Mos e aktivizoni nëse nuk dini çfarë bëni.</string>
|
||||
<string name="settings_customization">Personalizimi</string>
|
||||
<string name="setting_add_shortcut_summary">Shtoni një shkurtore mjaft të mirë në ekranin fillestar në rast se emri dhe ikona janë të vështira për tu njohur pasi keni fshehur aplikacionin</string>
|
||||
<string name="setting_add_shortcut_summary">Shto një shkurtore në ekranin bazë nëse emri/ikona bëhen të vështira për t’u dalluar pas fshehjes së aplikacionit</string>
|
||||
<string name="settings_doh_title">DNS mbi HTTPS</string>
|
||||
<string name="settings_doh_description">Helmimi i paqartë nga DNS në disa kombe</string>
|
||||
<string name="multiuser_mode">Mënyra Multi-përdoruesit</string>
|
||||
<string name="settings_owner_only">Vetëm pronari i paisjes</string>
|
||||
<string name="settings_owner_manage">Pronari i paisjes që e manaxhon</string>
|
||||
<string name="settings_user_independent">I pavarur nga përdoruesi</string>
|
||||
<string name="owner_only_summary">Vetëm pronari ka akses në rrënjë</string>
|
||||
<string name="owner_manage_summary">Vetëm pronari mund të menaxhojë aksesin në rrënjë dhe të marrë kërkesat</string>
|
||||
<string name="user_independent_summary">Çdo përdorues ka rregullat e veta të veçanta rrënjësore</string>
|
||||
<string name="mount_namespace_mode">Mënyra e Montimit të Hapësirës Emërore</string>
|
||||
<string name="settings_ns_global">Hapësira globale e emrave</string>
|
||||
<string name="settings_ns_requester">Trashëgoni hapësirën e emrave</string>
|
||||
<string name="settings_ns_isolate">Hapësira e izoluar e emrave</string>
|
||||
<string name="global_summary">Të gjitha sesionet rrënjë përdorin hapësirën globale të emrave të montimit</string>
|
||||
<string name="requester_summary">Seancat rrënjësore do të trashëgojnë hapësirën e emrave të kërkuesit të tyre</string>
|
||||
<string name="isolate_summary">Çdo sesion rrënjë do të ketë hapësirën e vet të izoluar të emrave</string>
|
||||
<string name="settings_su_auth_title">Vërtetimi i përdoruesit</string>
|
||||
<string name="settings_su_auth_summary">Kërkoni vërtetimin e përdoruesit gjatë kërkesave të Superpërdoruesit</string>
|
||||
<string name="settings_su_auth_insecure">Asnjë metodë vërtetimi nuk është konfiguruar në pajisje</string>
|
||||
|
||||
<string name="settings_doh_description">Zgjidhje për helmimin e DNS në disa shtete</string>
|
||||
<string name="settings_random_name_title">Emër i rastësishëm</string>
|
||||
<string name="settings_random_name_description">Rastësizo emrin e skedarit të daljes për imazhet e patch-uara dhe skedarët tar për të shmangur detektimin</string>
|
||||
<string name="multiuser_mode">Mënyra multi-përdorues</string>
|
||||
<string name="settings_owner_only">Vetëm pronari i pajisjes</string>
|
||||
<string name="settings_owner_manage">Menaxhuar nga pronari</string>
|
||||
<string name="settings_user_independent">I pavarur për përdoruesit</string>
|
||||
<string name="owner_only_summary">Vetëm pronari ka akses root</string>
|
||||
<string name="owner_manage_summary">Vetëm pronari mund të menaxhojë aksesin root dhe të marrë kërkesat</string>
|
||||
<string name="user_independent_summary">Çdo përdorues ka rregullat e veta të root</string>
|
||||
<string name="mount_namespace_mode">Mënyra e mount namespace</string>
|
||||
<string name="settings_ns_global">Namespace global</string>
|
||||
<string name="settings_ns_requester">Trashëgo namespace</string>
|
||||
<string name="settings_ns_isolate">Namespace i izoluar</string>
|
||||
<string name="global_summary">Të gjitha sesionet root përdorin namespace global</string>
|
||||
<string name="requester_summary">Sesioni root trashëgon namespace-in e kërkuesit</string>
|
||||
<string name="isolate_summary">Çdo sesion root do të ketë namespace të izoluar</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Përditësimet e magisk</string>
|
||||
<string name="updated_channel">Përditësimi përfundoi</string>
|
||||
<string name="update_channel">Përditësimet e Magisk</string>
|
||||
<string name="progress_channel">Njoftimet e progresit</string>
|
||||
<string name="updated_channel">Përditësimi përfundoi</string>
|
||||
<string name="download_complete">Shkarkimi përfundoi</string>
|
||||
<string name="download_file_error">Gabim në shkarkimin e skedarit</string>
|
||||
<string name="magisk_update_title">Përditësimi Magisk i disponueshëm!</string>
|
||||
<string name="download_file_error">Gabim gjatë shkarkimit të skedarit</string>
|
||||
<string name="magisk_update_title">Përditësim i ri i Magisk!</string>
|
||||
<string name="updated_title">Magisk u përditësua</string>
|
||||
<string name="updated_text">Prekni për të hapur aplikacionin</string>
|
||||
<string name="updated_text">Shtypni për të hapur aplikacionin</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Po</string>
|
||||
<string name="no">Jo</string>
|
||||
<string name="repo_install_title">Instalo %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Shkarko</string>
|
||||
<string name="reboot">Rinis</string>
|
||||
<string name="close">Mbylle</string>
|
||||
<string name="release_notes">Shënimet e lëshimit</string>
|
||||
<string name="flashing">Duke flashuar…</string>
|
||||
<string name="running">Duke vepruar...</string>
|
||||
<string name="reboot">Rinise</string>
|
||||
<string name="close">Mbyll</string>
|
||||
<string name="release_notes">Shënimet e versionit</string>
|
||||
<string name="flashing">Duke flashuar..</string>
|
||||
<string name="running">Duke u ekzekutuar..</string>
|
||||
<string name="done">U krye!</string>
|
||||
<string name="done_action">Veprimi i ekzekutimit të %1$s u krye</string>
|
||||
<string name="done_action">Veprimi i %1$s u krye</string>
|
||||
<string name="failure">Dështoi!</string>
|
||||
<string name="hide_app_title">Fshehja e aplikacionit Magisk…</string>
|
||||
<string name="hide_app_title">Duke fshehur aplikacionin Magisk..</string>
|
||||
<string name="open_link_failed_toast">Nuk u gjet asnjë aplikacion për të hapur lidhjen</string>
|
||||
<string name="complete_uninstall">Çinstalimi i plotë</string>
|
||||
<string name="restore_img">Rivendosni imazhet</string>
|
||||
<string name="restore_img_msg">Duke rivendosur…</string>
|
||||
<string name="restore_done">Rivendosja u krye!</string>
|
||||
<string name="restore_fail">Rezervimi i aksioneve nuk ekziston!</string>
|
||||
<string name="complete_uninstall">Çinstalim i plotë</string>
|
||||
<string name="restore_img">Rikthe imazhet</string>
|
||||
<string name="restore_img_msg">Duke rikthyer..</string>
|
||||
<string name="restore_done">Rikthimi u krye!</string>
|
||||
<string name="restore_fail">Backup-i origjinal nuk ekziston!</string>
|
||||
<string name="setup_fail">Konfigurimi dështoi</string>
|
||||
<string name="env_fix_title">Kërkon Konfigurim shtesë</string>
|
||||
<string name="env_fix_msg">Pajisja juaj ka nevojë për konfigurim shtesë që Magisk të funksionojë siç duhet. Dëshironi të vazhdoni dhe rindizni?</string>
|
||||
<string name="env_full_fix_msg">Pajisja juaj ka nevojë për re-flashuar Magisk që të funksionojë siç duhet. Ju lutemi ri-instaloni Magisk brenda aplikacionit, modaliteti i rikuperimit nuk mund të marrë informacionin e saktë të pajisjes.</string>
|
||||
<string name="setup_msg">Konfigurimi i mjedisit të funksionimit…</string>
|
||||
<string name="unsupport_magisk_title">Version Magjik i Pambështetur</string>
|
||||
<string name="unsupport_magisk_msg">Ky version i aplikacionit nuk e mbështet versionin Magisk më të ulët se %1$s.\n\nAplikacioni do të sillet sikur të mos jetë i instaluar Magisk, ju lutemi azhurnoni Magisk sa më shpejt të jetë e mundur.</string>
|
||||
<string name="env_fix_title">Kërkohet konfigurim shtesë</string>
|
||||
<string name="env_fix_msg">Pajisja ka nevojë për konfigurim shtesë që Magisk të funksionojë si duhet. Dëshironi të vazhdoni dhe të rinisni pajisjen?</string>
|
||||
<string name="env_full_fix_msg">Pajisja ka nevojë për ri-flash të Magisk për të funksionuar saktë. Ju lutemi riinstaloni Magisk brenda aplikacionit; Recovery nuk mund të marrë informacionet e sakta të pajisjes.</string>
|
||||
<string name="setup_msg">Duke ekzekutuar konfigurimin e mjedisit..</string>
|
||||
<string name="unsupport_magisk_title">Version i Magisk i pambështetur</string>
|
||||
<string name="unsupport_magisk_msg">Ky version i aplikacionit nuk mbështet versione të Magisk më të ulëta se %1$s.
|
||||
|
||||
Aplikacioni do të sillet sikur Magisk nuk është i instaluar. Ju lutemi përditësoni Magisk sa më shpejt të jetë e mundur.</string>
|
||||
<string name="unsupport_general_title">Gjendje jonormale</string>
|
||||
<string name="unsupport_system_app_msg">Drejtimi i këtij aplikacioni si një aplikacion sistemi nuk mbështetet. Ju lutemi kthejeni aplikacionin në një aplikacion përdoruesi.</string>
|
||||
<string name="unsupport_other_su_msg">Një komandë \"su"\ që nuk i përket Magisk është zbuluar. Ju lutemi hiqni SU-në tjetër të pambështetur.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk është instaluar në ruajtjen e jashtme. Ju lutemi zhvendosni aplikacionin në ruajtjen e brendshme.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Aplikacioni nuk mund të vazhdojë të punojë në gjendjen e fshehur pasi rrënja ishte e humbur. Ju lutemi rivendoseni përsëri në APK-në origjinale.</string>
|
||||
<string name="unsupport_system_app_msg">Ekzekutimi i këtij aplikacioni si aplikacion sistemi nuk mbështetet. Ju lutemi kthejeni në aplikacion përdoruesi.</string>
|
||||
<string name="unsupport_other_su_msg">Është zbuluar një binar "su" që nuk është nga Magisk. Ju lutemi hiqni çdo zgjidhje tjetër root dhe/ose riinstaloni Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk është instaluar në memorien e jashtme. Lëvizni aplikacionin në memorien e brendshme.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Aplikacioni i fshehur i Magisk nuk mund të vazhdojë të funksionojë sepse root u humb. Ju lutemi riktheni APK-në origjinale.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Jepni lejen e ruajtjes për të aktivizuar këtë funksion</string>
|
||||
<string name="external_rw_permission_denied">Jepni lejen e magazinimit për të aktivizuar këtë funksion</string>
|
||||
<string name="post_notifications_denied">Jepni lejen e njoftimeve për të aktivizuar këtë funksion</string>
|
||||
<string name="install_unknown_denied">Lejo "instaloni aplikacione të panjohura" për të aktivizuar këtë funksion</string>
|
||||
<string name="install_unknown_denied">Lejoni "Instalo aplikacione të panjohura" për të aktivizuar këtë funksion</string>
|
||||
<string name="add_shortcut_title">Shto shkurtore në ekranin bazë</string>
|
||||
<string name="add_shortcut_msg">Pas fshehjes së këtij aplikacioni, emri dhe ikona e tij mund të bëhen të vështira për tu njohur. Dëshironi të shtoni një shkurtore mjaft të bukur në ekranin bazë?</string>
|
||||
<string name="app_not_found">Asnjë aplikacion nuk u gjet për të trajtuar këtë veprim</string>
|
||||
<string name="add_shortcut_msg">Pas fshehjes së aplikacionit, emri dhe ikona mund të jenë të vështira për t’u njohur. Dëshironi të shtoni një shkurtore të bukur në ekranin bazë?</string>
|
||||
<string name="app_not_found">Nuk u gjet aplikacion për të kryer këtë veprim</string>
|
||||
<string name="reboot_apply_change">Rinisni për të aplikuar ndryshimet</string>
|
||||
<string name="restore_app_confirmation">Kjo do të rivendosë aplikacionin e fshehur në aplikacionin origjinal. A dëshironi vërtet ta bëni këtë?</string>
|
||||
<string name="restore_app_confirmation">Kjo do të rikthejë aplikacionin e fshehur në gjendjen origjinale. Jeni të sigurt që dëshironi ta bëni këtë?</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -24,10 +24,8 @@ org.gradle.caching=true
|
||||
kapt.use.k2=true
|
||||
|
||||
# Android
|
||||
android.useAndroidX=true
|
||||
android.injected.testOnly=false
|
||||
android.nonFinalResIds=false
|
||||
|
||||
# Magisk
|
||||
magisk.stubVersion=40
|
||||
magisk.versionCode=30500
|
||||
magisk.versionCode=30700
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[versions]
|
||||
kotlin = "2.2.21"
|
||||
android = "8.13.1"
|
||||
ksp = "2.3.3"
|
||||
kotlin = "2.3.0"
|
||||
android = "9.0.1"
|
||||
ksp = "2.3.4"
|
||||
rikka = "1.3.0"
|
||||
navigation = "2.9.6"
|
||||
navigation = "2.9.7"
|
||||
libsu = "6.0.0"
|
||||
okhttp = "5.3.2"
|
||||
retrofit = "3.0.0"
|
||||
@@ -23,7 +23,7 @@ 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.12.0" }
|
||||
activity = { module = "androidx.activity:activity", version = "1.12.4" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.1" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.17.0" }
|
||||
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.2.0" }
|
||||
@@ -36,8 +36,8 @@ recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.4.0
|
||||
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.6.0" }
|
||||
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.2.0" }
|
||||
transition = { module = "androidx.transition:transition", version = "1.7.0" }
|
||||
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.5.0" }
|
||||
material = { module = "com.google.android.material:material", version = "1.13.0" }
|
||||
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.5" }
|
||||
@@ -59,9 +59,10 @@ rikka-insets = { module = "dev.rikka.rikkax.insets:insets", version.ref = "rikka
|
||||
|
||||
# Build plugins
|
||||
android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "android" }
|
||||
android-kapt-plugin = { module = "com.android.legacy-kapt:com.android.legacy-kapt.gradle.plugin", version.ref = "android" }
|
||||
ksp-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
|
||||
navigation-safe-args-plugin = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "navigation" }
|
||||
lsparanoid-plugin = { module = "org.lsposed.lsparanoid:gradle-plugin", version = "0.6.0" }
|
||||
moshi-plugin = { module = "dev.zacsweers.moshix:dev.zacsweers.moshix.gradle.plugin", version = "0.34.1" }
|
||||
moshi-plugin = { module = "dev.zacsweers.moshix:dev.zacsweers.moshix.gradle.plugin", version = "0.34.4" }
|
||||
|
||||
[plugins]
|
||||
|
||||
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-9.0.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
1
app/shared/.gitignore
vendored
1
app/shared/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -6,4 +6,5 @@ setupCommon()
|
||||
|
||||
android {
|
||||
namespace = "com.topjohnwu.shared"
|
||||
enableKotlin = false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
3
build.py
3
build.py
@@ -80,7 +80,7 @@ support_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy", "resetp
|
||||
default_targets = support_targets - {"resetprop"}
|
||||
rust_targets = default_targets.copy()
|
||||
clean_targets = {"native", "cpp", "rust", "app"}
|
||||
ondk_version = "r29.3"
|
||||
ondk_version = "r29.5"
|
||||
|
||||
# Global vars
|
||||
config = {}
|
||||
@@ -406,6 +406,7 @@ def build_apk(module: str):
|
||||
gradlew,
|
||||
f"{module}:assemble{build_type}",
|
||||
f"-PconfigPath={props}",
|
||||
f"-PabiList={','.join(build_abis.keys())}",
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# Magisk Changelog
|
||||
|
||||
### v30.7 (2026.2.23)
|
||||
|
||||
- [MagiskInit] Support Android 16 QPR2 sepolicy format
|
||||
- [MagiskInit] Support using klogdump partition as pre-init storage
|
||||
- [Zygisk] Support Android 16 QPR2 and higher
|
||||
- [Zygisk] Support Android XR and some Nubia devices
|
||||
- [MagiskSU] Do not drop capabilities by default, even when switching to non-root UIDs. Explicitly use the `--drop-cap` argument to drop capabilities.
|
||||
- [MagiskBoot] Better lzma format detection
|
||||
- [MagiskBoot] Fix various commandline argument parsing bugs
|
||||
|
||||
### v30.6 (2025.12.1)
|
||||
|
||||
- [MagiskInit] Revert a change that could result in bootloops
|
||||
|
||||
### v30.5 (2025.12.1)
|
||||
|
||||
- [General] Improve commandline argument parsing logic
|
||||
|
||||
@@ -21,7 +21,7 @@ It is possible to have the OTA installed to the inactive slot and have the Magis
|
||||
|
||||
<p align="center"><img src="images/ota_done.png" width="250"/> <img src="images/install_inactive_slot.png" width="250"/></p>
|
||||
|
||||
- After installation is done, press the reboot button in the Magisk app. Under-the-hood, the Magisk app forces your device to switch to the updated slot, bypassing any possible post-OTA verifications.
|
||||
- After installation is done, follow the final instructions at the end of the Magisk installation on how to reboot into the new slot, currently involving going back to the regular system update and hitting "restart now" (previous method of using reboot within Magisk App may not result in rebooting to new slot). Under-the-hood, the Magisk app tracks your device switch to the updated slot, bypassing any possible post-OTA verifications.
|
||||
|
||||
<p align="center"><img src="images/manager_reboot.png" width="250"/></p>
|
||||
|
||||
|
||||
@@ -3,12 +3,19 @@
|
||||
# The actual compilation will have the target overriden by command-line.
|
||||
target = "aarch64-linux-android"
|
||||
# Enable cross language LTO, and explicitly set dwarf-version for ThinLTO
|
||||
rustflags = ["-Z", "dwarf-version=4", "-C", "linker-plugin-lto"]
|
||||
rustflags = [
|
||||
"-Z",
|
||||
"dwarf-version=4",
|
||||
"-C",
|
||||
"linker-plugin-lto",
|
||||
"-C",
|
||||
"force-unwind-tables=no",
|
||||
]
|
||||
target-dir = "../out/rust"
|
||||
|
||||
[unstable]
|
||||
build-std = ["std", "panic_abort"]
|
||||
build-std-features = ["panic_immediate_abort", "optimize_for_size"]
|
||||
build-std-features = ["optimize_for_size"]
|
||||
profile-rustflags = true
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -79,7 +79,6 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskboot
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libbase \
|
||||
liblz4 \
|
||||
libboot-rs
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
|
||||
563
native/src/Cargo.lock
generated
563
native/src/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
cargo-features = ["panic-immediate-abort"]
|
||||
|
||||
[workspace]
|
||||
exclude = ["external"]
|
||||
members = ["base", "base/derive", "boot", "core", "init", "sepolicy"]
|
||||
@@ -13,55 +15,57 @@ derive = { path = "base/derive" }
|
||||
magiskpolicy = { path = "sepolicy" }
|
||||
cxx = { path = "external/cxx-rs" }
|
||||
cxx-gen = { path = "external/cxx-rs/gen/lib" }
|
||||
libc = "0.2.177"
|
||||
libc = "0.2.182"
|
||||
cfg-if = "1.0.4"
|
||||
num-traits = "0.2.19"
|
||||
num-derive = "0.4.2"
|
||||
thiserror = "2.0.17"
|
||||
thiserror = "2.0.18"
|
||||
byteorder = "1.5.0"
|
||||
size = "0.5.0"
|
||||
bytemuck = "1.24.0"
|
||||
bytemuck = "1.25.0"
|
||||
fdt = "0.1.5"
|
||||
const_format = "0.2.35"
|
||||
bit-set = "0.8.0"
|
||||
syn = "2.0.111"
|
||||
quote = "1.0.42"
|
||||
proc-macro2 = "1.0.103"
|
||||
syn = "2.0.117"
|
||||
quote = "1.0.44"
|
||||
proc-macro2 = "1.0.106"
|
||||
pb-rs = { version = "0.10.0", default-features = false }
|
||||
quick-protobuf = "0.8.1"
|
||||
flate2 = { version = "1.1.5", default-features = false }
|
||||
flate2 = { version = "1.1.9", default-features = false }
|
||||
bzip2 = "0.6.1"
|
||||
zopfli = "0.8.3"
|
||||
lz4 = "1.28.1"
|
||||
lzma-rust2 = { version = "0.15.2", default-features = false }
|
||||
lz4_flex = { git = "https://github.com/PSeitz/lz4_flex.git", rev = "e3e5138cff0cad6ca87d09b9d03b1f6017bbc370", default-features = false, features = ["hc", "std", "frame"] }
|
||||
lzma-rust2 = { version = "0.16.2", default-features = false }
|
||||
nix = "0.30.1"
|
||||
bitflags = "2.10.0"
|
||||
bitflags = "2.11.0"
|
||||
|
||||
# Rust crypto crates are tied together
|
||||
sha1 = "0.11.0-rc.3"
|
||||
sha2 = "0.11.0-rc.3"
|
||||
digest = "0.11.0-rc.4"
|
||||
p256 = "0.14.0-rc.1"
|
||||
p384 = "0.14.0-rc.1"
|
||||
p521 = "0.14.0-rc.1"
|
||||
rsa = "0.10.0-rc.10"
|
||||
x509-cert = "0.3.0-rc.2"
|
||||
der = "0.8.0-rc.10"
|
||||
sha1 = "0.11.0-rc.5"
|
||||
sha2 = "0.11.0-rc.5"
|
||||
digest = "0.11.0"
|
||||
p256 = "0.14.0-rc.7"
|
||||
p384 = "0.14.0-rc.7"
|
||||
p521 = "0.14.0-rc.7"
|
||||
rsa = "0.10.0-rc.15"
|
||||
x509-cert = "0.3.0-rc.4"
|
||||
der = "0.8.0"
|
||||
|
||||
[patch.crates-io]
|
||||
pb-rs = { git = "https://github.com/topjohnwu/quick-protobuf.git" }
|
||||
quick-protobuf = { git = "https://github.com/topjohnwu/quick-protobuf.git" }
|
||||
lz4-sys = { path = "external/lz4-sys" }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
unwrap_used = "deny"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = "z"
|
||||
lto = "thin"
|
||||
panic = "abort"
|
||||
panic = "immediate-abort"
|
||||
debug = "none"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
panic = "immediate-abort"
|
||||
strip = true
|
||||
|
||||
@@ -9,6 +9,9 @@ path = "lib.rs"
|
||||
[features]
|
||||
selinux = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cxx-gen = { workspace = true }
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <android/log.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/fs.h>
|
||||
#include <syscall.h>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <flags.h>
|
||||
@@ -18,6 +14,12 @@ using namespace std;
|
||||
#define __call_bypassing_fortify(fn) (&fn)
|
||||
#endif
|
||||
|
||||
#ifdef __LP64__
|
||||
static_assert(BLKGETSIZE64 == 0x80081272);
|
||||
#else
|
||||
static_assert(BLKGETSIZE64 == 0x80041272);
|
||||
#endif
|
||||
|
||||
// Override libc++ new implementation to optimize final build size
|
||||
|
||||
void* operator new(std::size_t s) { return std::malloc(s); }
|
||||
@@ -29,32 +31,19 @@ void* operator new[](std::size_t s, const std::nothrow_t&) noexcept { return std
|
||||
void operator delete(void *p, const std::nothrow_t&) noexcept { std::free(p); }
|
||||
void operator delete[](void *p, const std::nothrow_t&) noexcept { std::free(p); }
|
||||
|
||||
bool byte_view::contains(byte_view pattern) const {
|
||||
return _buf != nullptr && memmem(_buf, _sz, pattern._buf, pattern._sz) != nullptr;
|
||||
}
|
||||
|
||||
bool byte_view::operator==(byte_view rhs) const {
|
||||
return _sz == rhs._sz && memcmp(_buf, rhs._buf, _sz) == 0;
|
||||
}
|
||||
|
||||
void byte_data::swap(byte_data &o) {
|
||||
std::swap(_buf, o._buf);
|
||||
std::swap(_sz, o._sz);
|
||||
}
|
||||
|
||||
rust::Vec<size_t> byte_data::patch(byte_view from, byte_view to) const {
|
||||
rust::Vec<size_t> v;
|
||||
if (_buf == nullptr)
|
||||
if (ptr == nullptr)
|
||||
return v;
|
||||
auto p = _buf;
|
||||
auto eof = _buf + _sz;
|
||||
auto p = ptr;
|
||||
auto eof = ptr + sz;
|
||||
while (p < eof) {
|
||||
p = static_cast<uint8_t *>(memmem(p, eof - p, from.data(), from.size()));
|
||||
if (p == nullptr)
|
||||
return v;
|
||||
memset(p, 0, from.size());
|
||||
memcpy(p, to.data(), to.size());
|
||||
v.push_back(p - _buf);
|
||||
v.push_back(p - ptr);
|
||||
p += from.size();
|
||||
}
|
||||
return v;
|
||||
@@ -374,30 +363,34 @@ sFILE make_file(FILE *fp) {
|
||||
mmap_data::mmap_data(const char *name, bool rw) {
|
||||
auto slice = rust::map_file(name, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
this->ptr = slice.data();
|
||||
this->sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(int dirfd, const char *name, bool rw) {
|
||||
auto slice = rust::map_file_at(dirfd, name, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
this->ptr = slice.data();
|
||||
this->sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(int fd, size_t sz, bool rw) {
|
||||
auto slice = rust::map_fd(fd, sz, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
this->ptr = slice.data();
|
||||
this->sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::~mmap_data() {
|
||||
if (_buf)
|
||||
munmap(_buf, _sz);
|
||||
if (ptr) munmap(ptr, sz);
|
||||
}
|
||||
|
||||
void mmap_data::swap(mmap_data &o) {
|
||||
std::swap(ptr, o.ptr);
|
||||
std::swap(sz, o.sz);
|
||||
}
|
||||
|
||||
string resolve_preinit_dir(const char *base_dir) {
|
||||
@@ -420,7 +413,7 @@ extern "C" void cxx$utf8str$new(Utf8CStr *self, const void *s, size_t len);
|
||||
extern "C" const char *cxx$utf8str$ptr(const Utf8CStr *self);
|
||||
extern "C" size_t cxx$utf8str$len(const Utf8CStr *self);
|
||||
|
||||
Utf8CStr::Utf8CStr(const char *s, size_t len) {
|
||||
Utf8CStr::Utf8CStr(const char *s, size_t len) : repr{} {
|
||||
cxx$utf8str$new(this, s, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@ use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::fd::{BorrowedFd, FromRawFd, OwnedFd, RawFd};
|
||||
use std::os::fd::{BorrowedFd, FromRawFd, RawFd};
|
||||
|
||||
use crate::ffi::{FnBoolStr, FnBoolStrStr};
|
||||
use crate::files::map_file_at;
|
||||
pub(crate) use crate::xwrap::*;
|
||||
use crate::{
|
||||
BufReadExt, Directory, LoggedResult, ResultExt, Utf8CStr, clone_attr, cstr, fclone_attr,
|
||||
map_fd, map_file, slice_from_ptr,
|
||||
BufReadExt, ResultExt, Utf8CStr, clone_attr, cstr, fclone_attr, map_fd, map_file,
|
||||
slice_from_ptr,
|
||||
};
|
||||
use cfg_if::cfg_if;
|
||||
use libc::{c_char, mode_t};
|
||||
@@ -52,14 +52,6 @@ unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
||||
fn inner(fd: OwnedFd) -> LoggedResult<()> {
|
||||
Directory::try_from(fd)?.remove_all()
|
||||
}
|
||||
inner(fd).is_ok()
|
||||
}
|
||||
|
||||
pub(crate) fn map_file_for_cxx(path: &Utf8CStr, rw: bool) -> &'static mut [u8] {
|
||||
map_file(path, rw).log().unwrap_or(&mut [])
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <linux/fs.h>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
#include "base-rs.hpp"
|
||||
|
||||
struct mmap_data : public byte_data {
|
||||
static_assert((sizeof(void *) == 8 && BLKGETSIZE64 == 0x80081272) ||
|
||||
(sizeof(void *) == 4 && BLKGETSIZE64 == 0x80041272));
|
||||
ALLOW_MOVE_ONLY(mmap_data)
|
||||
|
||||
mmap_data() = default;
|
||||
explicit mmap_data(const char *name, bool rw = false);
|
||||
mmap_data(int dirfd, const char *name, bool rw = false);
|
||||
mmap_data(int fd, size_t sz, bool rw = false);
|
||||
~mmap_data();
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
int mkdirs(const char *path, mode_t mode);
|
||||
ssize_t canonical_path(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
|
||||
bool rm_rf(const char *path);
|
||||
bool frm_rf(int dirfd);
|
||||
bool cp_afc(const char *src, const char *dest);
|
||||
bool mv_path(const char *src, const char *dest);
|
||||
bool link_path(const char *src, const char *dest);
|
||||
bool clone_attr(const char *src, const char *dest);
|
||||
bool fclone_attr(int src, int dest);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
std::string full_read(int fd);
|
||||
std::string full_read(const char *filename);
|
||||
void write_zero(int fd, size_t size);
|
||||
std::string resolve_preinit_dir(const char *base_dir);
|
||||
|
||||
// Functor = function<bool(Utf8CStr, Utf8CStr)>
|
||||
template <typename Functor>
|
||||
void parse_prop_file(const char *file, Functor &&fn) {
|
||||
parse_prop_file_rs(file, [&](rust::Str key, rust::Str val) -> bool {
|
||||
// We perform the null termination here in C++ because it's very difficult to do it
|
||||
// right in Rust due to pointer provenance. Trying to dereference a pointer without
|
||||
// the correct provenance in Rust, even in unsafe code, is undefined behavior.
|
||||
// However on the C++ side, there are fewer restrictions on pointers, so the const_cast here
|
||||
// will not trigger UB in the compiler.
|
||||
*(const_cast<char *>(key.data()) + key.size()) = '\0';
|
||||
*(const_cast<char *>(val.data()) + val.size()) = '\0';
|
||||
return fn(Utf8CStr(key.data(), key.size() + 1), Utf8CStr(val.data(), val.size() + 1));
|
||||
});
|
||||
}
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
sDIR make_dir(DIR *dp);
|
||||
sFILE make_file(FILE *fp);
|
||||
|
||||
static inline sDIR open_dir(const char *path) {
|
||||
return make_dir(opendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(const char *path) {
|
||||
return make_dir(xopendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(int dirfd) {
|
||||
return make_dir(xfdopendir(dirfd));
|
||||
}
|
||||
|
||||
static inline sFILE open_file(const char *path, const char *mode) {
|
||||
return make_file(fopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(const char *path, const char *mode) {
|
||||
return make_file(xfopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(int fd, const char *mode) {
|
||||
return make_file(xfdopen(fd, mode));
|
||||
}
|
||||
@@ -1,10 +1,353 @@
|
||||
#pragma once
|
||||
|
||||
#include "../xwrap.hpp"
|
||||
#include "../misc.hpp"
|
||||
#include "../base-rs.hpp"
|
||||
#include "../files.hpp"
|
||||
#include "../logging.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <functional>
|
||||
|
||||
using rust::xpipe2;
|
||||
using kv_pairs = std::vector<std::pair<std::string, std::string>>;
|
||||
#include <rust/cxx.h>
|
||||
|
||||
void LOGD(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGI(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGW(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGE(const char *fmt, ...) __printflike(1, 2);
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s\n", ##args, errno, std::strerror(errno))
|
||||
|
||||
extern "C" {
|
||||
|
||||
// xwraps
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode);
|
||||
FILE *xfdopen(int fd, const char *mode);
|
||||
int xopen(const char *pathname, int flags, mode_t mode = 0);
|
||||
int xopenat(int dirfd, const char *pathname, int flags, mode_t mode = 0);
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||
ssize_t xread(int fd, void *buf, size_t count);
|
||||
ssize_t xxread(int fd, void *buf, size_t count);
|
||||
int xsetns(int fd, int nstype);
|
||||
int xunshare(int flags);
|
||||
DIR *xopendir(const char *name);
|
||||
DIR *xfdopendir(int fd);
|
||||
dirent *xreaddir(DIR *dirp);
|
||||
pid_t xsetsid();
|
||||
int xfstat(int fd, struct stat *buf);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
ssize_t xreadlinkat(
|
||||
int dirfd, const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||
int xsymlink(const char *target, const char *linkpath);
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data);
|
||||
int xumount2(const char *target, int flags);
|
||||
int xrename(const char *oldpath, const char *newpath);
|
||||
int xmkdir(const char *pathname, mode_t mode);
|
||||
int xmkdirs(const char *pathname, mode_t mode);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
pid_t xfork();
|
||||
ssize_t xrealpath(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
|
||||
int xmknod(const char * pathname, mode_t mode, dev_t dev);
|
||||
|
||||
// Utils
|
||||
|
||||
int mkdirs(const char *path, mode_t mode);
|
||||
ssize_t canonical_path(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
|
||||
bool rm_rf(const char *path);
|
||||
bool cp_afc(const char *src, const char *dest);
|
||||
bool mv_path(const char *src, const char *dest);
|
||||
bool link_path(const char *src, const char *dest);
|
||||
bool clone_attr(const char *src, const char *dest);
|
||||
bool fclone_attr(int src, int dest);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#define DISALLOW_COPY_AND_MOVE(clazz) \
|
||||
clazz(const clazz&) = delete; \
|
||||
clazz(clazz &&) = delete;
|
||||
|
||||
#define ALLOW_MOVE_ONLY(clazz) \
|
||||
clazz(const clazz&) = delete; \
|
||||
clazz(clazz &&o) : clazz() { swap(o); } \
|
||||
clazz& operator=(clazz &&o) { swap(o); return *this; }
|
||||
|
||||
struct Utf8CStr;
|
||||
|
||||
class mutex_guard {
|
||||
DISALLOW_COPY_AND_MOVE(mutex_guard)
|
||||
public:
|
||||
explicit mutex_guard(pthread_mutex_t &m): mutex(&m) {
|
||||
pthread_mutex_lock(mutex);
|
||||
}
|
||||
void unlock() {
|
||||
pthread_mutex_unlock(mutex);
|
||||
mutex = nullptr;
|
||||
}
|
||||
~mutex_guard() {
|
||||
if (mutex) pthread_mutex_unlock(mutex);
|
||||
}
|
||||
private:
|
||||
pthread_mutex_t *mutex;
|
||||
};
|
||||
|
||||
template <class Func>
|
||||
class run_finally {
|
||||
DISALLOW_COPY_AND_MOVE(run_finally)
|
||||
public:
|
||||
explicit run_finally(Func &&fn) : fn(std::move(fn)) {}
|
||||
~run_finally() { fn(); }
|
||||
private:
|
||||
Func fn;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
static void default_new(T *&p) { p = new T(); }
|
||||
|
||||
template<class T>
|
||||
static void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
|
||||
|
||||
struct StringCmp {
|
||||
using is_transparent = void;
|
||||
bool operator()(std::string_view a, std::string_view b) const { return a < b; }
|
||||
};
|
||||
|
||||
using ByteSlice = rust::Slice<const uint8_t>;
|
||||
using MutByteSlice = rust::Slice<uint8_t>;
|
||||
|
||||
// Interchangeable as `&[u8]` in Rust
|
||||
struct byte_view {
|
||||
byte_view() : ptr(nullptr), sz(0) {}
|
||||
byte_view(const void *buf, size_t sz) : ptr((uint8_t *) buf), sz(sz) {}
|
||||
|
||||
// byte_view, or any of its subclasses, can be copied as byte_view
|
||||
byte_view(const byte_view &o) : ptr(o.ptr), sz(o.sz) {}
|
||||
|
||||
// Transparent conversion to Rust slice
|
||||
byte_view(const ByteSlice o) : byte_view(o.data(), o.size()) {}
|
||||
operator ByteSlice() const { return {ptr, sz}; }
|
||||
|
||||
// String as bytes, including null terminator
|
||||
byte_view(const char *s) : byte_view(s, strlen(s) + 1) {}
|
||||
|
||||
const uint8_t *data() const { return ptr; }
|
||||
size_t size() const { return sz; }
|
||||
|
||||
protected:
|
||||
uint8_t *ptr;
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
// Interchangeable as `&mut [u8]` in Rust
|
||||
struct byte_data : public byte_view {
|
||||
byte_data() = default;
|
||||
byte_data(void *buf, size_t sz) : byte_view(buf, sz) {}
|
||||
|
||||
// byte_data, or any of its subclasses, can be copied as byte_data
|
||||
byte_data(const byte_data &o) : byte_data(o.ptr, o.sz) {}
|
||||
|
||||
// Transparent conversion to Rust slice
|
||||
byte_data(const MutByteSlice o) : byte_data(o.data(), o.size()) {}
|
||||
operator MutByteSlice() const { return {ptr, sz}; }
|
||||
|
||||
using byte_view::data;
|
||||
uint8_t *data() const { return ptr; }
|
||||
|
||||
rust::Vec<size_t> patch(byte_view from, byte_view to) const;
|
||||
};
|
||||
|
||||
struct mmap_data : public byte_data {
|
||||
ALLOW_MOVE_ONLY(mmap_data)
|
||||
|
||||
mmap_data() = default;
|
||||
explicit mmap_data(const char *name, bool rw = false);
|
||||
mmap_data(int dirfd, const char *name, bool rw = false);
|
||||
mmap_data(int fd, size_t sz, bool rw = false);
|
||||
~mmap_data();
|
||||
private:
|
||||
void swap(mmap_data &o);
|
||||
};
|
||||
|
||||
|
||||
struct owned_fd {
|
||||
ALLOW_MOVE_ONLY(owned_fd)
|
||||
|
||||
owned_fd() : fd(-1) {}
|
||||
owned_fd(int fd) : fd(fd) {}
|
||||
~owned_fd() { close(fd); fd = -1; }
|
||||
|
||||
operator int() { return fd; }
|
||||
int release() { int f = fd; fd = -1; return f; }
|
||||
void swap(owned_fd &owned) { std::swap(fd, owned.fd); }
|
||||
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
rust::Vec<size_t> mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to);
|
||||
|
||||
uint32_t parse_uint32_hex(std::string_view s);
|
||||
int parse_int(std::string_view s);
|
||||
|
||||
using thread_entry = void *(*)(void *);
|
||||
extern "C" int new_daemon_thread(thread_entry entry, void *arg = nullptr);
|
||||
|
||||
static inline std::string rtrim(std::string &&s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch) && ch != '\0';
|
||||
}).base(), s.end());
|
||||
return std::move(s);
|
||||
}
|
||||
|
||||
int fork_dont_care();
|
||||
int fork_no_orphan();
|
||||
void init_argv0(int argc, char **argv);
|
||||
void set_nice_name(Utf8CStr name);
|
||||
int switch_mnt_ns(int pid);
|
||||
std::string &replace_all(std::string &str, std::string_view from, std::string_view to);
|
||||
std::vector<std::string> split(std::string_view s, std::string_view delims);
|
||||
|
||||
// Similar to vsnprintf, but the return value is the written number of bytes
|
||||
__printflike(3, 0) int vssprintf(char *dest, size_t size, const char *fmt, va_list ap);
|
||||
// Similar to snprintf, but the return value is the written number of bytes
|
||||
__printflike(3, 4) int ssprintf(char *dest, size_t size, const char *fmt, ...);
|
||||
// This is not actually the strscpy from the Linux kernel.
|
||||
// Silently truncates, and returns the number of bytes written.
|
||||
extern "C" size_t strscpy(char *dest, const char *src, size_t size);
|
||||
|
||||
// Ban usage of unsafe cstring functions
|
||||
#define vsnprintf __use_vssprintf_instead__
|
||||
#define snprintf __use_ssprintf_instead__
|
||||
#define strlcpy __use_strscpy_instead__
|
||||
|
||||
struct exec_t {
|
||||
bool err = false;
|
||||
int fd = -2;
|
||||
void (*pre_exec)() = nullptr;
|
||||
int (*fork)() = xfork;
|
||||
const char **argv = nullptr;
|
||||
};
|
||||
|
||||
int exec_command(exec_t &exec);
|
||||
template <class ...Args>
|
||||
int exec_command(exec_t &exec, Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec.argv = argv;
|
||||
return exec_command(exec);
|
||||
}
|
||||
int exec_command_sync(exec_t &exec);
|
||||
template <class ...Args>
|
||||
int exec_command_sync(exec_t &exec, Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec.argv = argv;
|
||||
return exec_command_sync(exec);
|
||||
}
|
||||
template <class ...Args>
|
||||
int exec_command_sync(Args &&...args) {
|
||||
exec_t exec;
|
||||
return exec_command_sync(exec, args...);
|
||||
}
|
||||
template <class ...Args>
|
||||
void exec_command_async(Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec_t exec {
|
||||
.fork = fork_dont_care,
|
||||
.argv = argv,
|
||||
};
|
||||
exec_command(exec);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr auto operator+(T e) noexcept ->
|
||||
std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>> {
|
||||
return static_cast<std::underlying_type_t<T>>(e);
|
||||
}
|
||||
|
||||
std::string full_read(int fd);
|
||||
std::string full_read(const char *filename);
|
||||
void write_zero(int fd, size_t size);
|
||||
std::string resolve_preinit_dir(const char *base_dir);
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
sDIR make_dir(DIR *dp);
|
||||
sFILE make_file(FILE *fp);
|
||||
|
||||
static inline sDIR open_dir(const char *path) {
|
||||
return make_dir(opendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(const char *path) {
|
||||
return make_dir(xopendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(int dirfd) {
|
||||
return make_dir(xfdopendir(dirfd));
|
||||
}
|
||||
|
||||
static inline sFILE open_file(const char *path, const char *mode) {
|
||||
return make_file(fopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(const char *path, const char *mode) {
|
||||
return make_file(xfopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(int fd, const char *mode) {
|
||||
return make_file(xfdopen(fd, mode));
|
||||
}
|
||||
|
||||
// Bindings to &Utf8CStr in Rust
|
||||
struct Utf8CStr {
|
||||
const char *data() const;
|
||||
size_t length() const;
|
||||
Utf8CStr(const char *s, size_t len);
|
||||
|
||||
Utf8CStr() : Utf8CStr("", 1) {};
|
||||
Utf8CStr(const Utf8CStr &o) = default;
|
||||
Utf8CStr(const char *s) : Utf8CStr(s, strlen(s) + 1) {};
|
||||
Utf8CStr(std::string s) : Utf8CStr(s.data(), s.length() + 1) {};
|
||||
const char *c_str() const { return this->data(); }
|
||||
size_t size() const { return this->length(); }
|
||||
bool empty() const { return this->length() == 0 ; }
|
||||
std::string_view sv() const { return {data(), length()}; }
|
||||
operator std::string_view() const { return sv(); }
|
||||
bool operator==(std::string_view rhs) const { return sv() == rhs; }
|
||||
|
||||
private:
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-private-field"
|
||||
std::array<std::uintptr_t, 2> repr;
|
||||
#pragma clang diagnostic pop
|
||||
};
|
||||
|
||||
// Bindings for std::function to be callable from Rust
|
||||
using CxxFnBoolStrStr = std::function<bool(rust::Str, rust::Str)>;
|
||||
struct FnBoolStrStr : public CxxFnBoolStrStr {
|
||||
using CxxFnBoolStrStr::function;
|
||||
bool call(rust::Str a, rust::Str b) const {
|
||||
return operator()(a, b);
|
||||
}
|
||||
};
|
||||
using CxxFnBoolStr = std::function<bool(Utf8CStr)>;
|
||||
struct FnBoolStr : public CxxFnBoolStr {
|
||||
using CxxFnBoolStr::function;
|
||||
bool call(Utf8CStr s) const {
|
||||
return operator()(s);
|
||||
}
|
||||
};
|
||||
|
||||
#include "../base-rs.hpp"
|
||||
|
||||
// Functor = function<bool(Utf8CStr, Utf8CStr)>
|
||||
template <typename Functor>
|
||||
void parse_prop_file(const char *file, Functor &&fn) {
|
||||
parse_prop_file_rs(file, [&](rust::Str key, rust::Str val) -> bool {
|
||||
// We perform the null termination here in C++ because it's very difficult to do it
|
||||
// right in Rust due to pointer provenance. Trying to dereference a pointer without
|
||||
// the correct provenance in Rust, even in unsafe code, is undefined behavior.
|
||||
// However on the C++ side, there are fewer restrictions on pointers, so the const_cast here
|
||||
// will not trigger UB in the compiler.
|
||||
*(const_cast<char *>(key.data()) + key.size()) = '\0';
|
||||
*(const_cast<char *>(val.data()) + val.size()) = '\0';
|
||||
return fn(Utf8CStr(key.data(), key.size() + 1), Utf8CStr(val.data(), val.size() + 1));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![feature(vec_into_raw_parts)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
pub use {const_format, libc, nix};
|
||||
@@ -39,7 +38,7 @@ mod ffi {
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("misc.hpp");
|
||||
include!("base.hpp");
|
||||
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
|
||||
@@ -62,11 +61,11 @@ mod ffi {
|
||||
fn parse_prop_file_rs(name: Utf8CStrRef, f: &FnBoolStrStr);
|
||||
#[cxx_name = "file_readline"]
|
||||
fn file_readline_for_cxx(fd: i32, f: &FnBoolStr);
|
||||
fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32;
|
||||
}
|
||||
|
||||
#[namespace = "rust"]
|
||||
extern "Rust" {
|
||||
fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32;
|
||||
#[cxx_name = "map_file"]
|
||||
fn map_file_for_cxx(path: Utf8CStrRef, rw: bool) -> &'static mut [u8];
|
||||
#[cxx_name = "map_file_at"]
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdarg>
|
||||
|
||||
void LOGD(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGI(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGW(const char *fmt, ...) __printflike(1, 2);
|
||||
void LOGE(const char *fmt, ...) __printflike(1, 2);
|
||||
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s\n", ##args, errno, std::strerror(errno))
|
||||
@@ -1,250 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <bitset>
|
||||
#include <rust/cxx.h>
|
||||
|
||||
#include "xwrap.hpp"
|
||||
|
||||
#define DISALLOW_COPY_AND_MOVE(clazz) \
|
||||
clazz(const clazz&) = delete; \
|
||||
clazz(clazz &&) = delete;
|
||||
|
||||
#define ALLOW_MOVE_ONLY(clazz) \
|
||||
clazz(const clazz&) = delete; \
|
||||
clazz(clazz &&o) : clazz() { swap(o); } \
|
||||
clazz& operator=(clazz &&o) { swap(o); return *this; }
|
||||
|
||||
struct Utf8CStr;
|
||||
|
||||
class mutex_guard {
|
||||
DISALLOW_COPY_AND_MOVE(mutex_guard)
|
||||
public:
|
||||
explicit mutex_guard(pthread_mutex_t &m): mutex(&m) {
|
||||
pthread_mutex_lock(mutex);
|
||||
}
|
||||
void unlock() {
|
||||
pthread_mutex_unlock(mutex);
|
||||
mutex = nullptr;
|
||||
}
|
||||
~mutex_guard() {
|
||||
if (mutex) pthread_mutex_unlock(mutex);
|
||||
}
|
||||
private:
|
||||
pthread_mutex_t *mutex;
|
||||
};
|
||||
|
||||
template <class Func>
|
||||
class run_finally {
|
||||
DISALLOW_COPY_AND_MOVE(run_finally)
|
||||
public:
|
||||
explicit run_finally(Func &&fn) : fn(std::move(fn)) {}
|
||||
~run_finally() { fn(); }
|
||||
private:
|
||||
Func fn;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
static void default_new(T *&p) { p = new T(); }
|
||||
|
||||
template<class T>
|
||||
static void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
|
||||
|
||||
struct StringCmp {
|
||||
using is_transparent = void;
|
||||
bool operator()(std::string_view a, std::string_view b) const { return a < b; }
|
||||
};
|
||||
|
||||
struct heap_data;
|
||||
|
||||
using ByteSlice = rust::Slice<const uint8_t>;
|
||||
using MutByteSlice = rust::Slice<uint8_t>;
|
||||
|
||||
// Interchangeable as `&[u8]` in Rust
|
||||
struct byte_view {
|
||||
byte_view() : _buf(nullptr), _sz(0) {}
|
||||
byte_view(const void *buf, size_t sz) : _buf((uint8_t *) buf), _sz(sz) {}
|
||||
|
||||
// byte_view, or any of its subclasses, can be copied as byte_view
|
||||
byte_view(const byte_view &o) : _buf(o._buf), _sz(o._sz) {}
|
||||
|
||||
// Transparent conversion to Rust slice
|
||||
byte_view(const ByteSlice o) : byte_view(o.data(), o.size()) {}
|
||||
operator ByteSlice() const { return {_buf, _sz}; }
|
||||
|
||||
// String as bytes, including null terminator
|
||||
byte_view(const char *s) : byte_view(s, strlen(s) + 1) {}
|
||||
|
||||
const uint8_t *data() const { return _buf; }
|
||||
size_t size() const { return _sz; }
|
||||
bool contains(byte_view pattern) const;
|
||||
bool operator==(byte_view rhs) const;
|
||||
|
||||
protected:
|
||||
uint8_t *_buf;
|
||||
size_t _sz;
|
||||
};
|
||||
|
||||
// Interchangeable as `&mut [u8]` in Rust
|
||||
struct byte_data : public byte_view {
|
||||
byte_data() = default;
|
||||
byte_data(void *buf, size_t sz) : byte_view(buf, sz) {}
|
||||
|
||||
// byte_data, or any of its subclasses, can be copied as byte_data
|
||||
byte_data(const byte_data &o) : byte_data(o._buf, o._sz) {}
|
||||
|
||||
// Transparent conversion to Rust slice
|
||||
byte_data(const MutByteSlice o) : byte_data(o.data(), o.size()) {}
|
||||
operator MutByteSlice() const { return {_buf, _sz}; }
|
||||
|
||||
using byte_view::data;
|
||||
uint8_t *data() const { return _buf; }
|
||||
|
||||
void swap(byte_data &o);
|
||||
rust::Vec<size_t> patch(byte_view from, byte_view to) const;
|
||||
};
|
||||
|
||||
struct heap_data : public byte_data {
|
||||
ALLOW_MOVE_ONLY(heap_data)
|
||||
|
||||
heap_data() = default;
|
||||
explicit heap_data(size_t sz) : byte_data(calloc(sz, 1), sz) {}
|
||||
~heap_data() { free(_buf); }
|
||||
};
|
||||
|
||||
struct owned_fd {
|
||||
ALLOW_MOVE_ONLY(owned_fd)
|
||||
|
||||
owned_fd() : fd(-1) {}
|
||||
owned_fd(int fd) : fd(fd) {}
|
||||
~owned_fd() { close(fd); fd = -1; }
|
||||
|
||||
operator int() { return fd; }
|
||||
int release() { int f = fd; fd = -1; return f; }
|
||||
void swap(owned_fd &owned) { std::swap(fd, owned.fd); }
|
||||
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
rust::Vec<size_t> mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to);
|
||||
|
||||
uint32_t parse_uint32_hex(std::string_view s);
|
||||
int parse_int(std::string_view s);
|
||||
|
||||
using thread_entry = void *(*)(void *);
|
||||
extern "C" int new_daemon_thread(thread_entry entry, void *arg = nullptr);
|
||||
|
||||
static inline std::string rtrim(std::string &&s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch) && ch != '\0';
|
||||
}).base(), s.end());
|
||||
return std::move(s);
|
||||
}
|
||||
|
||||
int fork_dont_care();
|
||||
int fork_no_orphan();
|
||||
void init_argv0(int argc, char **argv);
|
||||
void set_nice_name(Utf8CStr name);
|
||||
int switch_mnt_ns(int pid);
|
||||
std::string &replace_all(std::string &str, std::string_view from, std::string_view to);
|
||||
std::vector<std::string> split(std::string_view s, std::string_view delims);
|
||||
|
||||
// Similar to vsnprintf, but the return value is the written number of bytes
|
||||
__printflike(3, 0) int vssprintf(char *dest, size_t size, const char *fmt, va_list ap);
|
||||
// Similar to snprintf, but the return value is the written number of bytes
|
||||
__printflike(3, 4) int ssprintf(char *dest, size_t size, const char *fmt, ...);
|
||||
// This is not actually the strscpy from the Linux kernel.
|
||||
// Silently truncates, and returns the number of bytes written.
|
||||
extern "C" size_t strscpy(char *dest, const char *src, size_t size);
|
||||
|
||||
// Ban usage of unsafe cstring functions
|
||||
#define vsnprintf __use_vssprintf_instead__
|
||||
#define snprintf __use_ssprintf_instead__
|
||||
#define strlcpy __use_strscpy_instead__
|
||||
|
||||
struct exec_t {
|
||||
bool err = false;
|
||||
int fd = -2;
|
||||
void (*pre_exec)() = nullptr;
|
||||
int (*fork)() = xfork;
|
||||
const char **argv = nullptr;
|
||||
};
|
||||
|
||||
int exec_command(exec_t &exec);
|
||||
template <class ...Args>
|
||||
int exec_command(exec_t &exec, Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec.argv = argv;
|
||||
return exec_command(exec);
|
||||
}
|
||||
int exec_command_sync(exec_t &exec);
|
||||
template <class ...Args>
|
||||
int exec_command_sync(exec_t &exec, Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec.argv = argv;
|
||||
return exec_command_sync(exec);
|
||||
}
|
||||
template <class ...Args>
|
||||
int exec_command_sync(Args &&...args) {
|
||||
exec_t exec;
|
||||
return exec_command_sync(exec, args...);
|
||||
}
|
||||
template <class ...Args>
|
||||
void exec_command_async(Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec_t exec {
|
||||
.fork = fork_dont_care,
|
||||
.argv = argv,
|
||||
};
|
||||
exec_command(exec);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr auto operator+(T e) noexcept ->
|
||||
std::enable_if_t<std::is_enum<T>::value, std::underlying_type_t<T>> {
|
||||
return static_cast<std::underlying_type_t<T>>(e);
|
||||
}
|
||||
|
||||
struct Utf8CStr {
|
||||
const char *data() const;
|
||||
size_t length() const;
|
||||
Utf8CStr(const char *s, size_t len);
|
||||
|
||||
Utf8CStr() : Utf8CStr("", 1) {};
|
||||
Utf8CStr(const Utf8CStr &o) = default;
|
||||
Utf8CStr(const char *s) : Utf8CStr(s, strlen(s) + 1) {};
|
||||
Utf8CStr(std::string s) : Utf8CStr(s.data(), s.length() + 1) {};
|
||||
const char *c_str() const { return this->data(); }
|
||||
size_t size() const { return this->length(); }
|
||||
bool empty() const { return this->length() == 0 ; }
|
||||
std::string_view sv() const { return {data(), length()}; }
|
||||
operator std::string_view() const { return sv(); }
|
||||
bool operator==(std::string_view rhs) const { return sv() == rhs; }
|
||||
|
||||
private:
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-private-field"
|
||||
std::array<std::uintptr_t, 2> repr;
|
||||
#pragma clang diagnostic pop
|
||||
};
|
||||
|
||||
// Bindings for std::function to be callable from Rust
|
||||
|
||||
using CxxFnBoolStrStr = std::function<bool(rust::Str, rust::Str)>;
|
||||
struct FnBoolStrStr : public CxxFnBoolStrStr {
|
||||
using CxxFnBoolStrStr::function;
|
||||
bool call(rust::Str a, rust::Str b) const {
|
||||
return operator()(a, b);
|
||||
}
|
||||
};
|
||||
using CxxFnBoolStr = std::function<bool(Utf8CStr)>;
|
||||
struct FnBoolStr : public CxxFnBoolStr {
|
||||
using CxxFnBoolStr::function;
|
||||
bool call(Utf8CStr s) const {
|
||||
return operator()(s);
|
||||
}
|
||||
};
|
||||
@@ -132,13 +132,9 @@ impl PositionalArgParser<'_> {
|
||||
}
|
||||
|
||||
fn ensure_end(&mut self) -> Result<(), EarlyExit> {
|
||||
if self.0.len() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EarlyExit::from(format!(
|
||||
"Unrecognized argument: {}\n",
|
||||
self.0.next().unwrap()
|
||||
)))
|
||||
match self.0.next() {
|
||||
None => Ok(()),
|
||||
Some(s) => Err(EarlyExit::from(format!("Unrecognized argument: {s}\n"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode);
|
||||
FILE *xfdopen(int fd, const char *mode);
|
||||
int xopen(const char *pathname, int flags, mode_t mode = 0);
|
||||
int xopenat(int dirfd, const char *pathname, int flags, mode_t mode = 0);
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||
ssize_t xread(int fd, void *buf, size_t count);
|
||||
ssize_t xxread(int fd, void *buf, size_t count);
|
||||
int xsetns(int fd, int nstype);
|
||||
int xunshare(int flags);
|
||||
DIR *xopendir(const char *name);
|
||||
DIR *xfdopendir(int fd);
|
||||
dirent *xreaddir(DIR *dirp);
|
||||
pid_t xsetsid();
|
||||
int xstat(const char *pathname, struct stat *buf);
|
||||
int xfstat(int fd, struct stat *buf);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
ssize_t xreadlink(const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||
ssize_t xreadlinkat(
|
||||
int dirfd, const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||
int xsymlink(const char *target, const char *linkpath);
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data);
|
||||
int xumount2(const char *target, int flags);
|
||||
int xrename(const char *oldpath, const char *newpath);
|
||||
int xmkdir(const char *pathname, mode_t mode);
|
||||
int xmkdirs(const char *pathname, mode_t mode);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
pid_t xfork();
|
||||
ssize_t xrealpath(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
|
||||
int xmknod(const char * pathname, mode_t mode, dev_t dev);
|
||||
|
||||
} // extern "C"
|
||||
@@ -35,21 +35,6 @@ unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(path) => {
|
||||
let mut buf = cstr::buf::wrap_ptr(buf, bufsz);
|
||||
path.read_link(&mut buf)
|
||||
.log()
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xreadlinkat(
|
||||
dirfd: RawFd,
|
||||
@@ -201,16 +186,6 @@ extern "C" fn xsetsid() -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xstat(path: *const c_char, buf: *mut libc::stat) -> i32 {
|
||||
unsafe {
|
||||
libc::stat(path, buf)
|
||||
.into_os_result("stat", ptr_to_str(path), None)
|
||||
.log()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfstat(fd: RawFd, buf: *mut libc::stat) -> i32 {
|
||||
unsafe {
|
||||
|
||||
@@ -7,6 +7,9 @@ edition.workspace = true
|
||||
crate-type = ["staticlib"]
|
||||
path = "lib.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cxx-gen = { workspace = true }
|
||||
pb-rs = { workspace = true }
|
||||
@@ -31,6 +34,6 @@ bytemuck = { workspace = true, features = ["derive", "min_const_generics"] }
|
||||
num-traits = { workspace = true }
|
||||
flate2 = { workspace = true, features = ["zlib-rs"] }
|
||||
bzip2 = { workspace = true }
|
||||
lz4 = { workspace = true }
|
||||
lz4_flex = { workspace = true, features = ["hc", "std", "frame"]}
|
||||
lzma-rust2 = { workspace = true, features = ["xz", "std", "encoder", "optimization"] }
|
||||
zopfli = { workspace = true, features = ["gzip"] }
|
||||
|
||||
@@ -53,6 +53,19 @@ static bool check_env(const char *name) {
|
||||
return val != nullptr && val == "true"sv;
|
||||
}
|
||||
|
||||
static bool guess_lzma(const uint8_t *buf, size_t len) {
|
||||
// 0 : (pb * 5 + lp) * 9 + lc
|
||||
// 1 - 4 : dict size, must be 2^n
|
||||
// 5 - 12: all 0xFF
|
||||
if (len <= 13) return false;
|
||||
if (memcmp(buf, "\x5d", 1) != 0) return false;
|
||||
uint32_t dict_sz = 0;
|
||||
memcpy(&dict_sz, buf + 1, sizeof(dict_sz));
|
||||
if (dict_sz == 0 || (dict_sz & (dict_sz - 1)) != 0) return false;
|
||||
if (memcmp(buf + 5, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
FileFormat check_fmt(const void *buf, size_t len) {
|
||||
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
|
||||
return FileFormat::CHROMEOS;
|
||||
@@ -66,8 +79,7 @@ FileFormat check_fmt(const void *buf, size_t len) {
|
||||
return FileFormat::LZOP;
|
||||
} else if (CHECKED_MATCH(XZ_MAGIC)) {
|
||||
return FileFormat::XZ;
|
||||
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
||||
} else if (guess_lzma(static_cast<const uint8_t *>(buf), len)) {
|
||||
return FileFormat::LZMA;
|
||||
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
|
||||
return FileFormat::BZIP2;
|
||||
@@ -280,7 +292,7 @@ static int find_dtb_offset(const uint8_t *buf, unsigned sz) {
|
||||
auto fdt_hdr = reinterpret_cast<const fdt_header *>(curr);
|
||||
|
||||
// Check that fdt_header.totalsize does not overflow kernel image size or is empty dtb
|
||||
// https://github.com/torvalds/linux/commit/7b937cc243e5b1df8780a0aa743ce800df6c68d1
|
||||
// https://github.com/torvalds/linux/commit/7b937cc243e5b1df8780a0aa743ce800df6c68d1
|
||||
uint32_t totalsize = fdt_hdr->totalsize;
|
||||
if (totalsize > end - curr || totalsize <= 0x48)
|
||||
continue;
|
||||
@@ -599,7 +611,9 @@ bool boot_img::parse_image(const uint8_t *addr, FileFormat type) {
|
||||
int split_image_dtb(Utf8CStr filename, bool skip_decomp) {
|
||||
mmap_data img(filename.c_str());
|
||||
|
||||
if (size_t off = find_dtb_offset(img.data(), img.size()); off > 0) {
|
||||
if (int offset = find_dtb_offset(img.data(), img.size()); offset > 0) {
|
||||
size_t off = (size_t) offset;
|
||||
|
||||
FileFormat fmt = check_fmt_lg(img.data(), img.size());
|
||||
if (!skip_decomp && fmt_compressed(fmt)) {
|
||||
int fd = creat(KERNEL_FILE, 0644);
|
||||
@@ -901,8 +915,6 @@ void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) {
|
||||
file_align();
|
||||
}
|
||||
|
||||
off.tail = lseek(fd, 0, SEEK_CUR);
|
||||
|
||||
// Proprietary stuffs
|
||||
if (boot.flags[SEANDROID_FLAG]) {
|
||||
xwrite(fd, SEANDROID_MAGIC, 16);
|
||||
@@ -913,6 +925,7 @@ void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) {
|
||||
xwrite(fd, LG_BUMP_MAGIC, 16);
|
||||
}
|
||||
|
||||
off.tail = lseek(fd, 0, SEEK_CUR);
|
||||
file_align();
|
||||
|
||||
// vbmeta
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::codegen::gen_cxx_binding;
|
||||
#[path = "../include/codegen.rs"]
|
||||
mod codegen;
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=proto/update_metadata.proto");
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ Supported actions:
|
||||
dumped to the file 'header', which can be used to modify header
|
||||
configurations during repacking.
|
||||
Return values:
|
||||
0:valid 1:error 2:chromeos
|
||||
0:valid 1:error 2:chromeos 3:vendor_boot
|
||||
|
||||
repack [-n] <origbootimg> [outbootimg]
|
||||
Repack boot image components using files from the current directory
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
use crate::ffi::{FileFormat, check_fmt};
|
||||
use base::nix::fcntl::OFlag;
|
||||
use base::{
|
||||
Chunker, FileOrStd, LoggedResult, ReadExt, ResultExt, Utf8CStr, Utf8CString, WriteExt, log_err,
|
||||
};
|
||||
use base::{Chunker, FileOrStd, LoggedResult, ReadExt, Utf8CStr, Utf8CString, WriteExt, log_err};
|
||||
use bzip2::Compression as BzCompression;
|
||||
use bzip2::read::BzDecoder;
|
||||
use bzip2::write::BzEncoder;
|
||||
use flate2::Compression as GzCompression;
|
||||
use flate2::read::MultiGzDecoder;
|
||||
use flate2::write::GzEncoder;
|
||||
use lz4::block::CompressionMode;
|
||||
use lz4::liblz4::BlockChecksum;
|
||||
use lz4::{
|
||||
BlockMode, BlockSize, ContentChecksum, Decoder as LZ4FrameDecoder, Encoder as LZ4FrameEncoder,
|
||||
EncoderBuilder as LZ4FrameEncoderBuilder,
|
||||
use lz4_flex::frame::{
|
||||
BlockSize as LZ4BlockSize, FrameDecoder as LZ4FrameDecoder, FrameEncoder as LZ4FrameEncoder,
|
||||
FrameInfo as LZ4FrameInfo,
|
||||
};
|
||||
use lzma_rust2::{CheckType, LzmaOptions, LzmaReader, LzmaWriter, XzOptions, XzReader, XzWriter};
|
||||
use std::cmp::min;
|
||||
@@ -53,9 +49,7 @@ impl<W: Write> WriteFinish<W> for BufWriter<ZopFliEncoder<W>> {
|
||||
|
||||
impl<W: Write> WriteFinish<W> for LZ4FrameEncoder<W> {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W> {
|
||||
let (w, r) = Self::finish(*self);
|
||||
r?;
|
||||
Ok(w)
|
||||
Ok(Self::finish(*self)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,40 +61,31 @@ impl<W: Write> WriteFinish<W> for LZ4FrameEncoder<W> {
|
||||
// LZ4BlockEncoder
|
||||
|
||||
const LZ4_BLOCK_SIZE: usize = 0x800000;
|
||||
const LZ4HC_CLEVEL_MAX: i32 = 12;
|
||||
const LZ4HC_CLEVEL_MAX: u8 = 12;
|
||||
const LZ4_MAGIC: u32 = 0x184c2102;
|
||||
|
||||
struct LZ4BlockEncoder<W: Write> {
|
||||
write: W,
|
||||
chunker: Chunker,
|
||||
out_buf: Box<[u8]>,
|
||||
total: u32,
|
||||
is_lg: bool,
|
||||
}
|
||||
|
||||
impl<W: Write> LZ4BlockEncoder<W> {
|
||||
fn new(write: W, is_lg: bool) -> Self {
|
||||
let out_sz = lz4::block::compress_bound(LZ4_BLOCK_SIZE).unwrap_or(LZ4_BLOCK_SIZE);
|
||||
LZ4BlockEncoder {
|
||||
write,
|
||||
chunker: Chunker::new(LZ4_BLOCK_SIZE),
|
||||
// SAFETY: all bytes will be initialized before it is used
|
||||
out_buf: unsafe { Box::new_uninit_slice(out_sz).assume_init() },
|
||||
total: 0,
|
||||
is_lg,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_block(write: &mut W, out_buf: &mut [u8], chunk: &[u8]) -> std::io::Result<()> {
|
||||
let compressed_size = lz4::block::compress_to_buffer(
|
||||
chunk,
|
||||
Some(CompressionMode::HIGHCOMPRESSION(LZ4HC_CLEVEL_MAX)),
|
||||
false,
|
||||
out_buf,
|
||||
)?;
|
||||
let block_size = compressed_size as u32;
|
||||
fn encode_block(write: &mut W, chunk: &[u8]) -> std::io::Result<()> {
|
||||
let compressed = lz4_flex::block::compress_hc_to_vec(chunk, LZ4HC_CLEVEL_MAX);
|
||||
let block_size = compressed.len() as u32;
|
||||
write.write_pod(&block_size)?;
|
||||
write.write_all(&out_buf[..compressed_size])
|
||||
write.write_all(&compressed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +110,7 @@ impl<W: Write> Write for LZ4BlockEncoder<W> {
|
||||
let (b, chunk) = self.chunker.add_data(buf);
|
||||
buf = b;
|
||||
if let Some(chunk) = chunk {
|
||||
Self::encode_block(&mut self.write, &mut self.out_buf, chunk)?;
|
||||
Self::encode_block(&mut self.write, chunk)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -136,7 +121,7 @@ impl<W: Write> WriteFinish<W> for LZ4BlockEncoder<W> {
|
||||
fn finish(mut self: Box<Self>) -> std::io::Result<W> {
|
||||
let chunk = self.chunker.get_available();
|
||||
if !chunk.is_empty() {
|
||||
Self::encode_block(&mut self.write, &mut self.out_buf, chunk)?;
|
||||
Self::encode_block(&mut self.write, chunk)?;
|
||||
}
|
||||
if self.is_lg {
|
||||
self.write.write_pod(&self.total)?;
|
||||
@@ -157,10 +142,12 @@ struct LZ4BlockDecoder<R: Read> {
|
||||
|
||||
impl<R: Read> LZ4BlockDecoder<R> {
|
||||
fn new(read: R) -> Self {
|
||||
let compressed_sz = lz4::block::compress_bound(LZ4_BLOCK_SIZE).unwrap_or(LZ4_BLOCK_SIZE);
|
||||
Self {
|
||||
read,
|
||||
in_buf: unsafe { Box::new_uninit_slice(compressed_sz).assume_init() },
|
||||
in_buf: unsafe {
|
||||
Box::new_uninit_slice(lz4_flex::block::get_maximum_output_size(LZ4_BLOCK_SIZE))
|
||||
.assume_init()
|
||||
},
|
||||
out_buf: unsafe { Box::new_uninit_slice(LZ4_BLOCK_SIZE).assume_init() },
|
||||
out_len: 0,
|
||||
out_pos: 0,
|
||||
@@ -202,11 +189,8 @@ impl<R: Read> Read for LZ4BlockDecoder<R> {
|
||||
}
|
||||
}
|
||||
|
||||
self.out_len = lz4::block::decompress_to_buffer(
|
||||
compressed_block,
|
||||
Some(LZ4_BLOCK_SIZE as i32),
|
||||
&mut self.out_buf,
|
||||
)?;
|
||||
self.out_len = lz4_flex::block::decompress_into(compressed_block, &mut self.out_buf)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
|
||||
self.out_pos = 0;
|
||||
}
|
||||
let copy_len = min(buf.len(), self.out_len - self.out_pos);
|
||||
@@ -218,55 +202,59 @@ impl<R: Read> Read for LZ4BlockDecoder<R> {
|
||||
|
||||
// Top-level APIs
|
||||
|
||||
pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn WriteFinish<W> + 'a> {
|
||||
match format {
|
||||
pub fn get_encoder<'a, W: Write + 'a>(
|
||||
format: FileFormat,
|
||||
w: W,
|
||||
) -> std::io::Result<Box<dyn WriteFinish<W> + 'a>> {
|
||||
Ok(match format {
|
||||
FileFormat::XZ => {
|
||||
let mut opt = XzOptions::with_preset(9);
|
||||
opt.set_check_sum_type(CheckType::Crc32);
|
||||
Box::new(XzWriter::new(w, opt).unwrap())
|
||||
}
|
||||
FileFormat::LZMA => {
|
||||
Box::new(LzmaWriter::new_use_header(w, &LzmaOptions::with_preset(9), None).unwrap())
|
||||
Box::new(XzWriter::new(w, opt)?)
|
||||
}
|
||||
FileFormat::LZMA => Box::new(LzmaWriter::new_use_header(
|
||||
w,
|
||||
&LzmaOptions::with_preset(9),
|
||||
None,
|
||||
)?),
|
||||
FileFormat::BZIP2 => Box::new(BzEncoder::new(w, BzCompression::best())),
|
||||
FileFormat::LZ4 => {
|
||||
let encoder = LZ4FrameEncoderBuilder::new()
|
||||
.block_size(BlockSize::Max4MB)
|
||||
.block_mode(BlockMode::Independent)
|
||||
.checksum(ContentChecksum::ChecksumEnabled)
|
||||
.block_checksum(BlockChecksum::BlockChecksumEnabled)
|
||||
.level(9)
|
||||
.auto_flush(true)
|
||||
.build(w)
|
||||
.unwrap();
|
||||
Box::new(encoder)
|
||||
}
|
||||
FileFormat::LZ4 => Box::new(LZ4FrameEncoder::with_compression_level(
|
||||
LZ4FrameInfo::new()
|
||||
.block_size(LZ4BlockSize::Max4MB)
|
||||
.block_checksums(true)
|
||||
.content_checksum(true),
|
||||
w,
|
||||
LZ4HC_CLEVEL_MAX,
|
||||
)),
|
||||
FileFormat::LZ4_LEGACY => Box::new(LZ4BlockEncoder::new(w, false)),
|
||||
FileFormat::LZ4_LG => Box::new(LZ4BlockEncoder::new(w, true)),
|
||||
FileFormat::ZOPFLI => {
|
||||
// These options are already better than gzip -9
|
||||
let opt = ZopfliOptions {
|
||||
iteration_count: NonZeroU64::new(1).unwrap(),
|
||||
iteration_count: unsafe { NonZeroU64::new_unchecked(1) },
|
||||
maximum_block_splits: 1,
|
||||
..Default::default()
|
||||
};
|
||||
Box::new(ZopFliEncoder::new_buffered(opt, BlockType::Dynamic, w).unwrap())
|
||||
Box::new(ZopFliEncoder::new_buffered(opt, BlockType::Dynamic, w)?)
|
||||
}
|
||||
FileFormat::GZIP => Box::new(GzEncoder::new(w, GzCompression::best())),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_decoder<'a, R: Read + 'a>(format: FileFormat, r: R) -> Box<dyn Read + 'a> {
|
||||
match format {
|
||||
pub fn get_decoder<'a, R: Read + 'a>(
|
||||
format: FileFormat,
|
||||
r: R,
|
||||
) -> std::io::Result<Box<dyn Read + 'a>> {
|
||||
Ok(match format {
|
||||
FileFormat::XZ => Box::new(XzReader::new(r, true)),
|
||||
FileFormat::LZMA => Box::new(LzmaReader::new_mem_limit(r, u32::MAX, None).unwrap()),
|
||||
FileFormat::LZMA => Box::new(LzmaReader::new_mem_limit(r, u32::MAX, None)?),
|
||||
FileFormat::BZIP2 => Box::new(BzDecoder::new(r)),
|
||||
FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(r).unwrap()),
|
||||
FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(r)),
|
||||
FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(r)),
|
||||
FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(r)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// C++ FFI
|
||||
@@ -274,18 +262,22 @@ pub fn get_decoder<'a, R: Read + 'a>(format: FileFormat, r: R) -> Box<dyn Read +
|
||||
pub fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut encoder = get_encoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
encoder.write_all(in_bytes)?;
|
||||
let _ = || -> LoggedResult<()> {
|
||||
let mut encoder = get_encoder(format, out_file.deref_mut())?;
|
||||
std::io::copy(&mut Cursor::new(in_bytes), encoder.deref_mut())?;
|
||||
encoder.finish()?;
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
}
|
||||
|
||||
pub fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut decoder = get_decoder(format, in_bytes);
|
||||
std::io::copy(decoder.as_mut(), out_file.deref_mut()).log_ok();
|
||||
let _ = || -> LoggedResult<()> {
|
||||
let mut decoder = get_decoder(format, in_bytes)?;
|
||||
std::io::copy(decoder.as_mut(), out_file.deref_mut())?;
|
||||
Ok(())
|
||||
}();
|
||||
}
|
||||
|
||||
// Command-line entry points
|
||||
@@ -341,7 +333,7 @@ pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> L
|
||||
FileOrStd::File(outfile.create(OFlag::O_WRONLY | OFlag::O_TRUNC, 0o644)?)
|
||||
};
|
||||
|
||||
let mut decoder = get_decoder(format, Cursor::new(buf).chain(input.as_file()));
|
||||
let mut decoder = get_decoder(format, Cursor::new(buf).chain(input.as_file()))?;
|
||||
std::io::copy(decoder.as_mut(), &mut output.as_file())?;
|
||||
|
||||
if rm_in {
|
||||
@@ -384,7 +376,7 @@ pub(crate) fn compress_cmd(
|
||||
FileOrStd::File(outfile)
|
||||
};
|
||||
|
||||
let mut encoder = get_encoder(method, output.as_file());
|
||||
let mut encoder = get_encoder(method, output.as_file())?;
|
||||
std::io::copy(&mut input.as_file(), encoder.as_mut())?;
|
||||
encoder.finish()?;
|
||||
|
||||
|
||||
@@ -74,19 +74,19 @@ struct Exists {
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "backup")]
|
||||
struct Backup {
|
||||
#[argh(positional, arg_name = "orig")]
|
||||
origin: String,
|
||||
#[argh(switch, short = 'n')]
|
||||
skip_compress: bool,
|
||||
#[argh(positional, arg_name = "orig")]
|
||||
origin: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "rm")]
|
||||
struct Remove {
|
||||
#[argh(positional, arg_name = "entry")]
|
||||
path: String,
|
||||
#[argh(switch, short = 'r')]
|
||||
recursive: bool,
|
||||
#[argh(positional, arg_name = "entry")]
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
@@ -137,10 +137,10 @@ struct Add {
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "ls")]
|
||||
struct List {
|
||||
#[argh(positional, default = r#"String::from("/")"#)]
|
||||
path: String,
|
||||
#[argh(switch, short = 'r')]
|
||||
recursive: bool,
|
||||
#[argh(positional, default = r#"String::from("/")"#)]
|
||||
path: String,
|
||||
}
|
||||
|
||||
pub(crate) fn print_cpio_usage() {
|
||||
@@ -484,10 +484,9 @@ impl Cpio {
|
||||
};
|
||||
for (name, entry) in &self.entries {
|
||||
let p = "/".to_string() + name.as_str();
|
||||
if !p.starts_with(&path) {
|
||||
let Some(p) = p.strip_prefix(&path) else {
|
||||
continue;
|
||||
}
|
||||
let p = p.strip_prefix(&path).unwrap();
|
||||
};
|
||||
if !p.is_empty() && !p.starts_with('/') {
|
||||
continue;
|
||||
}
|
||||
@@ -614,8 +613,11 @@ impl Cpio {
|
||||
o.rm(".backup", true);
|
||||
self.rm(".backup", true);
|
||||
|
||||
let mut lhs = o.entries.into_iter().peekable();
|
||||
let mut rhs = self.entries.iter().peekable();
|
||||
let mut left_iter = o.entries.into_iter();
|
||||
let mut right_iter = self.entries.iter();
|
||||
|
||||
let mut lhs = left_iter.next();
|
||||
let mut rhs = right_iter.next();
|
||||
|
||||
loop {
|
||||
enum Action<'a> {
|
||||
@@ -623,32 +625,38 @@ impl Cpio {
|
||||
Record(&'a String),
|
||||
Noop,
|
||||
}
|
||||
let action = match (lhs.peek(), rhs.peek()) {
|
||||
(Some((l, _)), Some((r, re))) => match l.as_str().cmp(r.as_str()) {
|
||||
|
||||
// Move the iterator forward if needed
|
||||
if lhs.is_none() {
|
||||
lhs = left_iter.next();
|
||||
}
|
||||
if rhs.is_none() {
|
||||
rhs = right_iter.next();
|
||||
}
|
||||
|
||||
let action = match (lhs.take(), rhs.take()) {
|
||||
(Some((ln, le)), Some((rn, re))) => match ln.as_str().cmp(rn.as_str()) {
|
||||
Ordering::Less => {
|
||||
let (l, le) = lhs.next().unwrap();
|
||||
Action::Backup(l, le)
|
||||
// Put rhs back
|
||||
rhs = Some((rn, re));
|
||||
Action::Backup(ln, le)
|
||||
}
|
||||
Ordering::Greater => {
|
||||
// Put lhs back
|
||||
lhs = Some((ln, le));
|
||||
Action::Record(rn)
|
||||
}
|
||||
Ordering::Greater => Action::Record(rhs.next().unwrap().0),
|
||||
Ordering::Equal => {
|
||||
let (l, le) = lhs.next().unwrap();
|
||||
let action = if re.data != le.data {
|
||||
Action::Backup(l, le)
|
||||
if re.data != le.data {
|
||||
Action::Backup(ln, le)
|
||||
} else {
|
||||
Action::Noop
|
||||
};
|
||||
rhs.next();
|
||||
action
|
||||
}
|
||||
}
|
||||
},
|
||||
(Some(_), None) => {
|
||||
let (l, le) = lhs.next().unwrap();
|
||||
Action::Backup(l, le)
|
||||
}
|
||||
(None, Some(_)) => Action::Record(rhs.next().unwrap().0),
|
||||
(None, None) => {
|
||||
break;
|
||||
}
|
||||
(Some((ln, le)), None) => Action::Backup(ln, le),
|
||||
(None, Some((rn, _))) => Action::Record(rn),
|
||||
(None, None) => break,
|
||||
};
|
||||
match action {
|
||||
Action::Backup(name, mut entry) => {
|
||||
@@ -691,11 +699,11 @@ impl CpioEntry {
|
||||
if self.mode & S_IFMT != S_IFREG {
|
||||
return false;
|
||||
}
|
||||
let mut encoder = get_encoder(FileFormat::XZ, Vec::new());
|
||||
let Ok(data): std::io::Result<Vec<u8>> = (try {
|
||||
let Ok(data) = || -> std::io::Result<Vec<u8>> {
|
||||
let mut encoder = get_encoder(FileFormat::XZ, Vec::new())?;
|
||||
encoder.write_all(&self.data)?;
|
||||
encoder.finish()?
|
||||
}) else {
|
||||
encoder.finish()
|
||||
}() else {
|
||||
eprintln!("xz compression failed");
|
||||
return false;
|
||||
};
|
||||
@@ -709,12 +717,12 @@ impl CpioEntry {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Ok(data): std::io::Result<Vec<u8>> = (try {
|
||||
let mut decoder = get_decoder(FileFormat::XZ, Cursor::new(&self.data));
|
||||
let Ok(data) = || -> std::io::Result<Vec<u8>> {
|
||||
let mut decoder = get_decoder(FileFormat::XZ, Cursor::new(&self.data))?;
|
||||
let mut data = Vec::new();
|
||||
std::io::copy(decoder.as_mut(), &mut data)?;
|
||||
data
|
||||
}) else {
|
||||
Ok(data)
|
||||
}() else {
|
||||
eprintln!("xz compression failed");
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(try_blocks)]
|
||||
|
||||
pub use base;
|
||||
use compress::{compress_bytes, decompress_bytes};
|
||||
|
||||
@@ -103,7 +103,7 @@ fn hex2byte(hex: &[u8]) -> Vec<u8> {
|
||||
}
|
||||
|
||||
pub fn hexpatch(file: &Utf8CStr, from: &Utf8CStr, to: &Utf8CStr) -> bool {
|
||||
let res: LoggedResult<bool> = try {
|
||||
let res = || -> LoggedResult<bool> {
|
||||
let mut map = MappedFile::open_rw(file)?;
|
||||
let pattern = hex2byte(from.as_bytes());
|
||||
let patch = hex2byte(to.as_bytes());
|
||||
@@ -112,7 +112,7 @@ pub fn hexpatch(file: &Utf8CStr, from: &Utf8CStr, to: &Utf8CStr) -> bool {
|
||||
for off in &v {
|
||||
eprintln!("Patch @ {off:#010X} [{from}] -> [{to}]");
|
||||
}
|
||||
!v.is_empty()
|
||||
};
|
||||
Ok(!v.is_empty())
|
||||
}();
|
||||
res.unwrap_or(false)
|
||||
}
|
||||
|
||||
@@ -164,10 +164,11 @@ pub fn extract_boot_from_payload(
|
||||
out_file.seek(SeekFrom::Start(out_offset))?;
|
||||
let fmt = check_fmt(data);
|
||||
|
||||
let mut decoder = get_decoder(fmt, Cursor::new(data));
|
||||
let Ok(_): std::io::Result<()> = (try {
|
||||
let Ok(_) = || -> std::io::Result<()> {
|
||||
let mut decoder = get_decoder(fmt, Cursor::new(data))?;
|
||||
std::io::copy(decoder.as_mut(), &mut out_file)?;
|
||||
}) else {
|
||||
Ok(())
|
||||
}() else {
|
||||
return Err(bad_payload!("decompression failed"));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ check-signature = []
|
||||
check-client = []
|
||||
su-check-db = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cxx-gen = { workspace = true }
|
||||
pb-rs = { workspace = true }
|
||||
|
||||
@@ -82,7 +82,7 @@ impl MagiskD {
|
||||
Command::new(&tmp_bb)
|
||||
.arg("--install")
|
||||
.arg("-s")
|
||||
.arg(tmp_bb.parent_dir().unwrap())
|
||||
.arg(tmp_bb.parent_dir().unwrap_or_default())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status()
|
||||
@@ -186,13 +186,13 @@ impl MagiskD {
|
||||
setup_preinit_dir();
|
||||
self.ensure_manager();
|
||||
if self.zygisk_enabled.load(Ordering::Relaxed) {
|
||||
self.zygisk.lock().unwrap().reset(true);
|
||||
self.zygisk.lock().reset(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boot_stage_handler(&self, client: UnixStream, code: RequestCode) {
|
||||
// Make sure boot stage execution is always serialized
|
||||
let mut state = self.boot_stage_lock.lock().unwrap();
|
||||
let mut state = self.boot_stage_lock.lock();
|
||||
|
||||
match code {
|
||||
RequestCode::POST_FS_DATA => {
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::codegen::gen_cxx_binding;
|
||||
#[path = "../include/codegen.rs"]
|
||||
mod codegen;
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=resetprop/proto/persistent_properties.proto");
|
||||
|
||||
|
||||
@@ -31,8 +31,9 @@ use std::io::{BufReader, Write};
|
||||
use std::os::fd::{AsFd, AsRawFd, IntoRawFd, RawFd};
|
||||
use std::os::unix::net::{UCred, UnixListener, UnixStream};
|
||||
use std::process::{Command, exit};
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::sync::nonpoison::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
// Global magiskd singleton
|
||||
@@ -105,7 +106,7 @@ impl MagiskD {
|
||||
denylist_handler(-1);
|
||||
|
||||
// Restore native bridge property
|
||||
self.zygisk.lock().unwrap().restore_prop();
|
||||
self.zygisk.lock().restore_prop();
|
||||
|
||||
client.write_pod(&0).log_ok();
|
||||
|
||||
@@ -129,7 +130,7 @@ impl MagiskD {
|
||||
self.prune_su_access();
|
||||
scan_deny_apps();
|
||||
if self.zygisk_enabled.load(Ordering::Relaxed) {
|
||||
self.zygisk.lock().unwrap().reset(false);
|
||||
self.zygisk.lock().reset(false);
|
||||
}
|
||||
}
|
||||
RequestCode::SQLITE_CMD => {
|
||||
|
||||
@@ -187,7 +187,7 @@ unsafe extern "C" fn read_db_row<T: SqlTable>(
|
||||
|
||||
impl MagiskD {
|
||||
fn with_db<F: FnOnce(*mut sqlite3) -> i32>(&self, f: F) -> i32 {
|
||||
let mut db = self.sql_connection.lock().unwrap();
|
||||
let mut db = self.sql_connection.lock();
|
||||
if db.is_none() {
|
||||
let raw_db = open_and_init_db();
|
||||
*db = NonNull::new(raw_db).map(Sqlite3);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#![feature(try_blocks)]
|
||||
#![feature(fn_traits)]
|
||||
#![feature(unix_socket_ancillary_data)]
|
||||
#![feature(unix_socket_peek)]
|
||||
#![feature(default_field_values)]
|
||||
#![feature(peer_credentials_unix_socket)]
|
||||
#![feature(sync_nonpoison)]
|
||||
#![feature(nonpoison_mutex)]
|
||||
#![feature(nonpoison_condvar)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use crate::ffi::SuRequest;
|
||||
|
||||
@@ -20,9 +20,10 @@ use std::io::{IoSlice, Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::sync::nonpoison::Mutex;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use std::{fs, io};
|
||||
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
@@ -117,12 +118,12 @@ fn write_log_to_pipe(mut logd: &File, prio: i32, msg: &Utf8CStr) -> io::Result<u
|
||||
static MAGISK_LOGD_FD: Mutex<Option<Arc<File>>> = Mutex::new(None);
|
||||
|
||||
fn with_logd_fd<R, F: FnOnce(&File) -> io::Result<R>>(f: F) {
|
||||
let fd = MAGISK_LOGD_FD.lock().unwrap().clone();
|
||||
let fd = MAGISK_LOGD_FD.lock().clone();
|
||||
if let Some(logd) = fd
|
||||
&& f(&logd).is_err()
|
||||
{
|
||||
// If any error occurs, shut down the logd pipe
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = None;
|
||||
*MAGISK_LOGD_FD.lock() = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +266,9 @@ fn logfile_write_loop(mut pipe: File) -> io::Result<()> {
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or(Duration::ZERO);
|
||||
|
||||
// Note: the obvious better implementation is to use the rust chrono crate, however
|
||||
// the crate cannot fetch the proper local timezone without pulling in a bunch of
|
||||
@@ -322,18 +325,19 @@ pub fn start_log_daemon() {
|
||||
let file = unsafe { File::from_raw_fd(arg as RawFd) };
|
||||
logfile_write_loop(file).ok();
|
||||
// If any error occurs, shut down the logd pipe
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = None;
|
||||
*MAGISK_LOGD_FD.lock() = None;
|
||||
0
|
||||
}
|
||||
|
||||
let _: LoggedResult<()> = try {
|
||||
let _ = || -> LoggedResult<()> {
|
||||
path.mkfifo(0o666).log_ok();
|
||||
chown(path.as_utf8_cstr(), Some(Uid::from(0)), Some(Gid::from(0)))?;
|
||||
let read = path.open(OFlag::O_RDWR | OFlag::O_CLOEXEC)?;
|
||||
let write = path.open(OFlag::O_WRONLY | OFlag::O_CLOEXEC)?;
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = Some(Arc::new(write));
|
||||
*MAGISK_LOGD_FD.lock() = Some(Arc::new(write));
|
||||
unsafe {
|
||||
new_daemon_thread(logfile_writer_thread, read.into_raw_fd() as usize);
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
}
|
||||
|
||||
@@ -889,7 +889,7 @@ impl MagiskD {
|
||||
|
||||
// Handle zygisk
|
||||
if self.zygisk_enabled.load(Ordering::Acquire) {
|
||||
let mut zygisk = self.zygisk.lock().unwrap();
|
||||
let mut zygisk = self.zygisk.lock();
|
||||
zygisk.set_prop();
|
||||
inject_zygisk_bins(&zygisk.lib_name, &mut system);
|
||||
}
|
||||
|
||||
@@ -5,13 +5,18 @@ use base::{
|
||||
FsPathBuilder, LibcReturn, LoggedResult, MountInfo, ResultExt, Utf8CStr, Utf8CStrBuf, cstr,
|
||||
debug, info, libc, parse_mount_info, warn,
|
||||
};
|
||||
use libc::{c_uint, dev_t};
|
||||
use libc::{c_uint, dev_t, major};
|
||||
use nix::mount::MsFlags;
|
||||
use nix::sys::stat::{Mode, SFlag, mknod};
|
||||
use num_traits::AsPrimitive;
|
||||
use std::cmp::Ordering::{Greater, Less};
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// Linux allocated devices: 240-254 are reserved for LOCAL/EXPERIMENTAL use.
|
||||
const DYNAMIC_MAJOR_MIN: u32 = 240;
|
||||
const DYNAMIC_MAJOR_MAX: u32 = 254;
|
||||
|
||||
pub fn setup_preinit_dir() {
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
|
||||
@@ -41,12 +46,13 @@ pub fn setup_preinit_dir() {
|
||||
let target = Utf8CStr::from_string(&mut target);
|
||||
let mut preinit_dir = resolve_preinit_dir(target);
|
||||
let preinit_dir = Utf8CStr::from_string(&mut preinit_dir);
|
||||
let r: LoggedResult<()> = try {
|
||||
let r = || -> LoggedResult<()> {
|
||||
preinit_dir.mkdir(0o700)?;
|
||||
mnt_path.mkdirs(0o755)?;
|
||||
mnt_path.remove().ok();
|
||||
mnt_path.create_symlink_to(preinit_dir)?;
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if r.is_ok() {
|
||||
info!("* Found preinit dir: {}", preinit_dir);
|
||||
return;
|
||||
@@ -63,11 +69,12 @@ pub fn setup_module_mount() {
|
||||
let module_mnt = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(MODULEMNT);
|
||||
let _: LoggedResult<()> = try {
|
||||
let _ = || -> LoggedResult<()> {
|
||||
module_mnt.mkdir(0o755)?;
|
||||
cstr!(MODULEROOT).bind_mount_to(&module_mnt, false)?;
|
||||
module_mnt.remount_mount_point_flags(MsFlags::MS_RDONLY)?;
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
}
|
||||
|
||||
pub fn clean_mounts() {
|
||||
@@ -80,21 +87,24 @@ pub fn clean_mounts() {
|
||||
buf.clear();
|
||||
|
||||
let worker_dir = buf.append_path(magisk_tmp).append_path(WORKERDIR);
|
||||
let _: LoggedResult<()> = try {
|
||||
let _ = || -> LoggedResult<()> {
|
||||
worker_dir.set_mount_private(true)?;
|
||||
worker_dir.unmount()?;
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
}
|
||||
|
||||
// when partitions have the same fs type, the order is:
|
||||
// - data: it has sufficient space and can be safely written
|
||||
// - cache: size is limited, but still can be safely written
|
||||
// - klogdump: available on some Smartisan devices and can be safely written
|
||||
// - metadata: size is limited, and it might cause unexpected behavior if written
|
||||
// - persist: it's the last resort, as it's dangerous to write to it
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum PartId {
|
||||
Data,
|
||||
Cache,
|
||||
Klogdump,
|
||||
Metadata,
|
||||
Persist,
|
||||
}
|
||||
@@ -137,11 +147,20 @@ pub fn find_preinit_device() -> String {
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
// use device major number to filter out device-mapper
|
||||
let maj = major(info.device as dev_t) as u32;
|
||||
if (DYNAMIC_MAJOR_MIN..=DYNAMIC_MAJOR_MAX).contains(&maj)
|
||||
&& !info.source.contains("/vd")
|
||||
&& !info.source.contains("/by-name/")
|
||||
{
|
||||
return None;
|
||||
}
|
||||
// take data iff it's not encrypted or file-based encrypted without metadata
|
||||
// other partitions are always taken
|
||||
match info.target.as_str() {
|
||||
"/persist" | "/mnt/vendor/persist" => Some((PartId::Persist, info)),
|
||||
"/metadata" => Some((PartId::Metadata, info)),
|
||||
"/klogdump" => Some((PartId::Klogdump, info)),
|
||||
"/cache" => Some((PartId::Cache, info)),
|
||||
"/data" => Some((PartId::Data, info))
|
||||
.take_if(|_| matches!(encrypt_type, EncryptType::None | EncryptType::File)),
|
||||
@@ -181,13 +200,14 @@ pub fn find_preinit_device() -> String {
|
||||
let mut buf = cstr::buf::default();
|
||||
let mirror_dir = buf.append_path(&tmp).append_path(PREINITMIRR);
|
||||
let preinit_dir = Utf8CStr::from_string(&mut preinit_dir);
|
||||
let _: LoggedResult<()> = try {
|
||||
let _ = || -> LoggedResult<()> {
|
||||
preinit_dir.mkdirs(0o700)?;
|
||||
mirror_dir.mkdirs(0o755)?;
|
||||
mirror_dir.unmount().ok();
|
||||
mirror_dir.remove().ok();
|
||||
mirror_dir.create_symlink_to(preinit_dir)?;
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if std::env::var_os("MAKEDEV").is_some() {
|
||||
buf.clear();
|
||||
let dev_path = buf.append_path(&tmp).append_path(PREINITDEV);
|
||||
@@ -203,9 +223,8 @@ pub fn find_preinit_device() -> String {
|
||||
}
|
||||
Path::new(&info.source)
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.and_then(OsStr::to_str)
|
||||
.unwrap_or_default()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
@@ -221,10 +240,7 @@ pub fn revert_unmount(pid: i32) {
|
||||
|
||||
// Unmount Magisk tmpfs and mounts from module files
|
||||
for info in parse_mount_info("self") {
|
||||
if info.source == "magisk"
|
||||
|| info.root.starts_with("/adb/modules")
|
||||
|| (info.fs_type == "rootfs" && info.root.starts_with("/magisk"))
|
||||
{
|
||||
if info.source == "magisk" || info.root.starts_with("/adb/modules") {
|
||||
targets.push(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ macro_rules! bad_apk {
|
||||
* within the APK v2 signature block.
|
||||
*/
|
||||
fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
let res: io::Result<Vec<u8>> = try {
|
||||
let res = || -> io::Result<Vec<u8>> {
|
||||
let mut u32_val = 0u32;
|
||||
let mut u64_val = 0u64;
|
||||
|
||||
@@ -137,7 +137,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
|
||||
let mut cert = vec![0; u32_val as usize];
|
||||
apk.read_exact(cert.as_mut())?;
|
||||
break cert;
|
||||
break Ok(cert);
|
||||
} else {
|
||||
// Skip this id-value pair
|
||||
apk.seek(SeekFrom::Current(
|
||||
@@ -145,7 +145,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
))?;
|
||||
}
|
||||
}
|
||||
};
|
||||
}();
|
||||
res.log().unwrap_or(vec![])
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ impl ManagerInfo {
|
||||
if let Some(ref mut stub_fd) = self.stub_apk_fd {
|
||||
// Copy the stub APK
|
||||
let tmp_apk = cstr!("/data/stub.apk");
|
||||
let result: LoggedResult<()> = try {
|
||||
let result = || -> LoggedResult<()> {
|
||||
{
|
||||
let mut tmp_apk_file = tmp_apk.create(
|
||||
OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC | OFlag::O_CLOEXEC,
|
||||
@@ -325,7 +325,8 @@ impl ManagerInfo {
|
||||
}
|
||||
// Seek the fd back to start
|
||||
stub_fd.seek(SeekFrom::Start(0))?;
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_ok() {
|
||||
install_apk(tmp_apk);
|
||||
}
|
||||
@@ -441,7 +442,7 @@ impl MagiskD {
|
||||
}
|
||||
|
||||
pub fn preserve_stub_apk(&self) {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let mut info = self.manager_info.lock();
|
||||
|
||||
let apk = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
@@ -458,19 +459,19 @@ impl MagiskD {
|
||||
}
|
||||
|
||||
pub fn get_manager_uid(&self, user: i32) -> i32 {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let mut info = self.manager_info.lock();
|
||||
let (uid, _) = info.get_manager(self, user, false);
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn get_manager(&self, user: i32, install: bool) -> (i32, String) {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let mut info = self.manager_info.lock();
|
||||
let (uid, pkg) = info.get_manager(self, user, install);
|
||||
(uid, pkg.to_string())
|
||||
}
|
||||
|
||||
pub fn ensure_manager(&self) {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let mut info = self.manager_info.lock();
|
||||
let _ = info.get_manager(self, 0, true);
|
||||
}
|
||||
|
||||
@@ -478,7 +479,7 @@ impl MagiskD {
|
||||
// app_no range: [0, 9999]
|
||||
pub fn get_app_no_list(&self) -> BitSet {
|
||||
let mut list = BitSet::new();
|
||||
let _: LoggedResult<()> = try {
|
||||
let _ = || -> LoggedResult<()> {
|
||||
let mut app_data_dir = Directory::open(self.app_data_dir())?;
|
||||
// For each user
|
||||
loop {
|
||||
@@ -507,7 +508,8 @@ impl MagiskD {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ impl Extra<'_> {
|
||||
IntList(list) => {
|
||||
cmd.args(["--es", self.key]);
|
||||
let mut tmp = String::new();
|
||||
list.iter().for_each(|i| write!(&mut tmp, "{i},").unwrap());
|
||||
list.iter().for_each(|i| {
|
||||
write!(&mut tmp, "{i},").ok();
|
||||
});
|
||||
tmp.pop();
|
||||
cmd.arg(&tmp);
|
||||
}
|
||||
@@ -67,7 +69,9 @@ impl Extra<'_> {
|
||||
IntList(list) => {
|
||||
tmp = format!("{}:s:", self.key);
|
||||
if !list.is_empty() {
|
||||
list.iter().for_each(|i| write!(&mut tmp, "{i},").unwrap());
|
||||
list.iter().for_each(|i| {
|
||||
write!(&mut tmp, "{i},").ok();
|
||||
});
|
||||
tmp.pop();
|
||||
}
|
||||
}
|
||||
@@ -171,7 +175,7 @@ impl SuAppContext<'_> {
|
||||
))
|
||||
.ok();
|
||||
|
||||
let fd: LoggedResult<File> = try {
|
||||
let fd = || -> LoggedResult<File> {
|
||||
let mut attr = FileAttr::new();
|
||||
attr.st.st_mode = 0o600;
|
||||
attr.st.st_uid = self.info.mgr_uid.as_();
|
||||
@@ -202,10 +206,13 @@ impl SuAppContext<'_> {
|
||||
let mut pfd = [PollFd::new(fd.as_fd(), PollFlags::POLLIN)];
|
||||
|
||||
// Wait for data input for at most 70 seconds
|
||||
nix::poll::poll(&mut pfd, PollTimeout::try_from(70 * 1000).unwrap())
|
||||
.check_os_err("poll", None, None)?;
|
||||
fd
|
||||
};
|
||||
nix::poll::poll(
|
||||
&mut pfd,
|
||||
PollTimeout::try_from(70 * 1000).unwrap_or(PollTimeout::NONE),
|
||||
)
|
||||
.check_os_err("poll", None, None)?;
|
||||
Ok(fd)
|
||||
}();
|
||||
|
||||
fifo.remove().log_ok();
|
||||
|
||||
|
||||
@@ -7,11 +7,12 @@ use crate::socket::IpcRead;
|
||||
use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn};
|
||||
use std::os::fd::IntoRawFd;
|
||||
use std::os::unix::net::{UCred, UnixStream};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::sync::nonpoison::Mutex;
|
||||
|
||||
const DEFAULT_SHELL: &str = "/system/bin/sh";
|
||||
|
||||
@@ -132,7 +133,7 @@ impl MagiskD {
|
||||
|
||||
let info = self.get_su_info(cred.uid as i32);
|
||||
{
|
||||
let mut access = info.access.lock().unwrap();
|
||||
let mut access = info.access.lock();
|
||||
|
||||
// Talk to su manager
|
||||
let mut app = SuAppContext {
|
||||
@@ -203,7 +204,7 @@ impl MagiskD {
|
||||
}
|
||||
|
||||
let cached = self.cached_su_info.load();
|
||||
if cached.uid == uid && cached.access.lock().unwrap().is_fresh() {
|
||||
if cached.uid == uid && cached.access.lock().is_fresh() {
|
||||
return cached;
|
||||
}
|
||||
|
||||
@@ -214,14 +215,14 @@ impl MagiskD {
|
||||
|
||||
#[cfg(feature = "su-check-db")]
|
||||
fn build_su_info(&self, uid: i32) -> Arc<SuInfo> {
|
||||
let result: LoggedResult<Arc<SuInfo>> = try {
|
||||
let result = || -> LoggedResult<Arc<SuInfo>> {
|
||||
let cfg = self.get_db_settings()?;
|
||||
|
||||
// Check multiuser settings
|
||||
let eval_uid = match cfg.multiuser_mode {
|
||||
MultiuserMode::OwnerOnly => {
|
||||
if to_user_id(uid) != 0 {
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
return Ok(Arc::new(SuInfo::deny(uid)));
|
||||
}
|
||||
uid
|
||||
}
|
||||
@@ -242,25 +243,25 @@ impl MagiskD {
|
||||
|
||||
// If it's the manager, allow it silently
|
||||
if to_app_id(uid) == to_app_id(mgr_uid) {
|
||||
return Arc::new(SuInfo::allow(uid));
|
||||
return Ok(Arc::new(SuInfo::allow(uid)));
|
||||
}
|
||||
|
||||
// Check su access settings
|
||||
match cfg.root_access {
|
||||
RootAccess::Disabled => {
|
||||
warn!("Root access is disabled!");
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
return Ok(Arc::new(SuInfo::deny(uid)));
|
||||
}
|
||||
RootAccess::AdbOnly => {
|
||||
if uid != AID_SHELL {
|
||||
warn!("Root access limited to ADB only!");
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
return Ok(Arc::new(SuInfo::deny(uid)));
|
||||
}
|
||||
}
|
||||
RootAccess::AppsOnly => {
|
||||
if uid == AID_SHELL {
|
||||
warn!("Root access is disabled for ADB!");
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
return Ok(Arc::new(SuInfo::deny(uid)));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -268,19 +269,19 @@ impl MagiskD {
|
||||
|
||||
// If still not determined, check if manager exists
|
||||
if access.policy == SuPolicy::Query && mgr_uid < 0 {
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
return Ok(Arc::new(SuInfo::deny(uid)));
|
||||
}
|
||||
|
||||
// Finally, the SuInfo
|
||||
Arc::new(SuInfo {
|
||||
Ok(Arc::new(SuInfo {
|
||||
uid,
|
||||
eval_uid,
|
||||
mgr_pkg,
|
||||
mgr_uid,
|
||||
cfg,
|
||||
access: Mutex::new(AccessInfo::new(access)),
|
||||
})
|
||||
};
|
||||
}))
|
||||
}();
|
||||
|
||||
result.unwrap_or(Arc::new(SuInfo::deny(uid)))
|
||||
}
|
||||
|
||||
@@ -99,7 +99,6 @@ fn pump_tty_impl(ptmx: File, pump_stdin: bool) -> LoggedResult<()> {
|
||||
let mut signal_fd: Option<SignalFd> = None;
|
||||
|
||||
let raw_ptmx = ptmx.as_raw_fd();
|
||||
let mut raw_sig = -1;
|
||||
|
||||
let mut poll_fds = Vec::with_capacity(3);
|
||||
poll_fds.push(PollFd::new(ptmx.as_fd(), PollFlags::POLLIN));
|
||||
@@ -111,12 +110,14 @@ fn pump_tty_impl(ptmx: File, pump_stdin: bool) -> LoggedResult<()> {
|
||||
.check_os_err("pthread_sigmask", None, None)?;
|
||||
let sig = SignalFd::with_flags(&set, SfdFlags::SFD_CLOEXEC)
|
||||
.into_os_result("signalfd", None, None)?;
|
||||
raw_sig = sig.as_raw_fd();
|
||||
signal_fd = Some(sig);
|
||||
poll_fds.push(PollFd::new(
|
||||
signal_fd.as_ref().unwrap().as_fd(),
|
||||
PollFlags::POLLIN,
|
||||
));
|
||||
unsafe {
|
||||
// SAFETY: signal_fd is always Some
|
||||
poll_fds.push(PollFd::new(
|
||||
signal_fd.as_ref().unwrap_unchecked().as_fd(),
|
||||
PollFlags::POLLIN,
|
||||
));
|
||||
}
|
||||
|
||||
// We also need to pump stdin to ptmx
|
||||
poll_fds.push(PollFd::new(
|
||||
@@ -142,9 +143,11 @@ fn pump_tty_impl(ptmx: File, pump_stdin: bool) -> LoggedResult<()> {
|
||||
pump_via_splice(FileOrStd::StdIn.as_file(), &ptmx, &pipe_fd)?;
|
||||
} else if raw_fd == raw_ptmx {
|
||||
pump_via_splice(&ptmx, FileOrStd::StdOut.as_file(), &pipe_fd)?;
|
||||
} else if raw_fd == raw_sig {
|
||||
} else if let Some(sig) = &signal_fd
|
||||
&& raw_fd == sig.as_raw_fd()
|
||||
{
|
||||
sync_winsize(raw_ptmx);
|
||||
signal_fd.as_ref().unwrap().read_signal()?;
|
||||
sig.read_signal()?;
|
||||
}
|
||||
} else if pfd
|
||||
.revents()
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -447,7 +448,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
|
||||
auto f = xopen_file("/proc/self/attr/exec", "we");
|
||||
if (f) fprintf(f.get(), "%s", req.context.c_str());
|
||||
}
|
||||
if (req.target_uid != AID_ROOT || req.drop_cap || proc_is_restricted(pid))
|
||||
if (req.drop_cap || proc_is_restricted(pid))
|
||||
drop_caps();
|
||||
if (req.target_uid != AID_ROOT || req.gids.size() > 0)
|
||||
set_identity(req.target_uid, req.gids);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use base::{ResultExt, new_daemon_thread};
|
||||
use nix::sys::signal::SigSet;
|
||||
use nix::unistd::{getpid, gettid};
|
||||
use std::sync::{Condvar, LazyLock, Mutex, WaitTimeoutResult};
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
static THREAD_POOL: LazyLock<ThreadPool> = LazyLock::new(ThreadPool::default);
|
||||
@@ -33,17 +34,15 @@ impl ThreadPool {
|
||||
|
||||
let task: Option<Box<dyn FnOnce() + Send>>;
|
||||
{
|
||||
let mut info = self.info.lock().unwrap();
|
||||
let mut info = self.info.lock();
|
||||
info.idle_threads += 1;
|
||||
if info.task.is_none() {
|
||||
if is_core_pool {
|
||||
// Core pool never closes, wait forever.
|
||||
info = self.task_is_some.wait(info).unwrap();
|
||||
self.task_is_some.wait(&mut info);
|
||||
} else {
|
||||
let dur = Duration::from_secs(THREAD_IDLE_MAX_SEC);
|
||||
let result: WaitTimeoutResult;
|
||||
(info, result) = self.task_is_some.wait_timeout(info, dur).unwrap();
|
||||
if result.timed_out() {
|
||||
if self.task_is_some.wait_timeout(&mut info, dur).timed_out() {
|
||||
// Terminate thread after timeout
|
||||
info.idle_threads -= 1;
|
||||
info.total_threads -= 1;
|
||||
@@ -72,10 +71,10 @@ impl ThreadPool {
|
||||
0
|
||||
}
|
||||
|
||||
let mut info = self.info.lock().unwrap();
|
||||
let mut info = self.info.lock();
|
||||
while info.task.is_some() {
|
||||
// Wait until task is none
|
||||
info = self.task_is_none.wait(info).unwrap();
|
||||
self.task_is_none.wait(&mut info);
|
||||
}
|
||||
info.task = Some(Box::new(f));
|
||||
if info.idle_threads == 0 {
|
||||
|
||||
@@ -236,6 +236,9 @@ struct Api {
|
||||
// or in the root companion process (assuming that you sent the fd over the socket).
|
||||
// Both restrictions are due to SELinux and UID.
|
||||
//
|
||||
// Module should also make sure zygote is allowed to read module dir (e.g. module dir has
|
||||
// system_file context) due to SELinux restrictions on socket messages.
|
||||
//
|
||||
// Returns -1 if errors occurred.
|
||||
int getModuleDir();
|
||||
|
||||
|
||||
@@ -87,25 +87,23 @@ impl ZygiskState {
|
||||
}
|
||||
}
|
||||
|
||||
let socket = if let Some(fd) = socket {
|
||||
fd
|
||||
if let Some(fd) = socket {
|
||||
fd.send_fds(&[client.as_raw_fd()])?;
|
||||
} else {
|
||||
// Create a new socket pair and fork zygiskd process
|
||||
let (local, remote) = UnixStream::pair()?;
|
||||
let (mut local, remote) = UnixStream::pair()?;
|
||||
if fork_dont_care() == 0 {
|
||||
exec_zygiskd(is_64_bit, remote);
|
||||
}
|
||||
*socket = Some(local);
|
||||
let local = socket.as_mut().unwrap();
|
||||
if let Some(module_fds) = daemon.get_module_fds(is_64_bit) {
|
||||
local.send_fds(&module_fds)?;
|
||||
}
|
||||
if local.read_decodable::<i32>()? != 0 {
|
||||
return log_err!();
|
||||
}
|
||||
local
|
||||
};
|
||||
socket.send_fds(&[client.as_raw_fd()])?;
|
||||
local.send_fds(&[client.as_raw_fd()])?;
|
||||
*socket = Some(local);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -159,7 +157,7 @@ impl ZygiskState {
|
||||
|
||||
impl MagiskD {
|
||||
pub fn zygisk_handler(&self, mut client: UnixStream) {
|
||||
let _: LoggedResult<()> = try {
|
||||
let _ = || -> LoggedResult<()> {
|
||||
let code = ZygiskRequest {
|
||||
repr: client.read_decodable()?,
|
||||
};
|
||||
@@ -168,13 +166,13 @@ impl MagiskD {
|
||||
ZygiskRequest::ConnectCompanion => self
|
||||
.zygisk
|
||||
.lock()
|
||||
.unwrap()
|
||||
.connect_zygiskd(client, self)
|
||||
.log_with_msg(|w| w.write_str("zygiskd startup error"))?,
|
||||
ZygiskRequest::GetModDir => self.get_mod_dir(client)?,
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
}
|
||||
|
||||
fn get_module_fds(&self, is_64_bit: bool) -> Option<Vec<RawFd>> {
|
||||
@@ -222,9 +220,12 @@ impl MagiskD {
|
||||
let failed_ids: Vec<i32> = client.read_decodable()?;
|
||||
if let Some(module_list) = self.module_list.get() {
|
||||
for id in failed_ids {
|
||||
let Some(module) = module_list.get(id as usize) else {
|
||||
continue;
|
||||
};
|
||||
let path = cstr::buf::default()
|
||||
.join_path(MODULEROOT)
|
||||
.join_path(&module_list[id as usize].name)
|
||||
.join_path(&module.name)
|
||||
.join_path("zygisk");
|
||||
// Create the unloaded marker file
|
||||
if let Ok(dir) = Directory::open(&path) {
|
||||
@@ -240,7 +241,13 @@ impl MagiskD {
|
||||
|
||||
fn get_mod_dir(&self, mut client: UnixStream) -> LoggedResult<()> {
|
||||
let id: i32 = client.read_decodable()?;
|
||||
let module = &self.module_list.get().unwrap()[id as usize];
|
||||
let Some(module) = self
|
||||
.module_list
|
||||
.get()
|
||||
.and_then(|list| list.get(id as usize))
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let dir = cstr::buf::default()
|
||||
.join_path(MODULEROOT)
|
||||
.join_path(&module.name);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <sys/mount.h>
|
||||
#include <android/dlext.h>
|
||||
#include <dlfcn.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
@@ -1,238 +1,669 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
primitives = ['jint', 'jboolean', 'jlong']
|
||||
primitives = ["jint", "jboolean", "jlong"]
|
||||
|
||||
|
||||
class JType:
|
||||
def __init__(self, cpp, jni):
|
||||
def __init__(self, cpp: str, jni: str):
|
||||
self.cpp = cpp
|
||||
self.jni = jni
|
||||
|
||||
|
||||
class JArray(JType):
|
||||
def __init__(self, type):
|
||||
def __init__(self, type: JType):
|
||||
if type.cpp in primitives:
|
||||
name = type.cpp + 'Array'
|
||||
name = type.cpp + "Array"
|
||||
else:
|
||||
name = 'jobjectArray'
|
||||
super().__init__(name, '[' + type.jni)
|
||||
name = "jobjectArray"
|
||||
super().__init__(name, "[" + type.jni)
|
||||
|
||||
|
||||
class Argument:
|
||||
def __init__(self, name, type, set_arg = False):
|
||||
def __init__(self, name: str, type: JType, set_arg=False):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.set_arg = set_arg
|
||||
|
||||
def cpp(self):
|
||||
return f'{self.type.cpp} {self.name}'
|
||||
def cpp(self) -> str:
|
||||
return f"{self.type.cpp} {self.name}"
|
||||
|
||||
|
||||
# Args we don't care, give it an auto generated name
|
||||
class Anon(Argument):
|
||||
cnt = 0
|
||||
def __init__(self, type):
|
||||
super().__init__(f'_{Anon.cnt}', type)
|
||||
|
||||
def __init__(self, type: JType):
|
||||
super().__init__(f"_{Anon.cnt}", type)
|
||||
Anon.cnt += 1
|
||||
|
||||
|
||||
class Return:
|
||||
def __init__(self, value, type):
|
||||
def __init__(self, value: str, type: JType):
|
||||
self.value = value
|
||||
self.type = type
|
||||
|
||||
class Method:
|
||||
def __init__(self, name, ret, args):
|
||||
|
||||
class JNIMethod:
|
||||
def __init__(self, name: str, ret: Return, args: list[Argument]):
|
||||
self.name = name
|
||||
self.ret = ret
|
||||
self.args = args
|
||||
|
||||
def cpp(self):
|
||||
return ', '.join(map(lambda x: x.cpp(), self.args))
|
||||
def arg_list_name(self) -> str:
|
||||
return "env, clazz, " + ", ".join(map(lambda x: x.name, self.args))
|
||||
|
||||
def name_list(self):
|
||||
return ', '.join(map(lambda x: x.name, self.args))
|
||||
def arg_list_cpp(self) -> str:
|
||||
return "JNIEnv *env, jclass clazz, " + ", ".join(
|
||||
map(lambda x: x.cpp(), self.args)
|
||||
)
|
||||
|
||||
def jni(self):
|
||||
args = ''.join(map(lambda x: x.type.jni, self.args))
|
||||
return f'({args}){self.ret.type.jni}'
|
||||
def cpp_fn_type(self) -> str:
|
||||
return f"{self.ret.type.cpp}(*)({self.arg_list_cpp()}"
|
||||
|
||||
def body(self, name, i):
|
||||
return ''
|
||||
def cpp_lambda_sig(self) -> str:
|
||||
return f"[] [[clang::no_stack_protector]] ({self.arg_list_cpp()}) static -> {self.ret.type.cpp}"
|
||||
|
||||
class JNIHook(Method):
|
||||
def __init__(self, ver, ret, args):
|
||||
name = f'{self.base_name()}_{ver}'
|
||||
def jni_sig(self):
|
||||
args = "".join(map(lambda x: x.type.jni, self.args))
|
||||
return f"({args}){self.ret.type.jni}"
|
||||
|
||||
|
||||
class JNIHook(JNIMethod):
|
||||
def __init__(self, ver: str, ret: Return, args: list[Argument]):
|
||||
name = f"{self.hook_target()}_{ver}"
|
||||
super().__init__(name, ret, args)
|
||||
|
||||
def base_name(self):
|
||||
return ''
|
||||
def hook_target(self):
|
||||
return ""
|
||||
|
||||
def body(self, orig_fn_ptr: str):
|
||||
return ""
|
||||
|
||||
def orig_method(self, name, i):
|
||||
return f'reinterpret_cast<{self.ret.type.cpp}(*)(JNIEnv *env, jclass clazz, {self.cpp()})>(g_hook->{name}_methods[{i}].fnPtr)'
|
||||
|
||||
def ind(i):
|
||||
return '\n' + ' ' * i
|
||||
return "\n" + " " * i
|
||||
|
||||
|
||||
# Common types
|
||||
jint = JType('jint', 'I')
|
||||
jint = JType("jint", "I")
|
||||
jintArray = JArray(jint)
|
||||
jstring = JType('jstring', 'Ljava/lang/String;')
|
||||
jboolean = JType('jboolean', 'Z')
|
||||
jlong = JType('jlong', 'J')
|
||||
void = JType('void', 'V')
|
||||
jstring = JType("jstring", "Ljava/lang/String;")
|
||||
jboolean = JType("jboolean", "Z")
|
||||
jlong = JType("jlong", "J")
|
||||
void = JType("void", "V")
|
||||
|
||||
class ForkAndSpec(JNIHook):
|
||||
|
||||
class ForkApp(JNIHook):
|
||||
def __init__(self, ver, args):
|
||||
super().__init__(ver, Return('ctx.pid', jint), args)
|
||||
super().__init__(ver, Return("ctx.pid", jint), args)
|
||||
|
||||
def base_name(self):
|
||||
return 'nativeForkAndSpecialize'
|
||||
def hook_target(self):
|
||||
return "nativeForkAndSpecialize"
|
||||
|
||||
def init_args(self):
|
||||
return 'AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);'
|
||||
return "AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);"
|
||||
|
||||
def body(self, name, i):
|
||||
decl = ''
|
||||
def body(self, orig_fn_ptr: str):
|
||||
decl = ""
|
||||
decl += ind(3) + self.init_args()
|
||||
for a in self.args:
|
||||
if a.set_arg:
|
||||
decl += ind(3) + f'args.{a.name} = &{a.name};'
|
||||
decl += ind(3) + 'ZygiskContext ctx(env, &args);'
|
||||
decl += ind(3) + f'ctx.{self.base_name()}_pre();'
|
||||
decl += ind(3) + self.orig_method(name, i) + '('
|
||||
decl += ind(4) + f'env, clazz, {self.name_list()}'
|
||||
decl += ind(3) + ');'
|
||||
decl += ind(3) + f'ctx.{self.base_name()}_post();'
|
||||
decl += ind(3) + f"args.{a.name} = &{a.name};"
|
||||
decl += ind(3) + "ZygiskContext ctx(env, &args);"
|
||||
decl += ind(3) + f"ctx.{self.hook_target()}_pre();"
|
||||
decl += ind(3) + f"reinterpret_cast<{self.cpp_fn_type()})>({orig_fn_ptr})("
|
||||
decl += ind(4) + self.arg_list_name()
|
||||
decl += ind(3) + ");"
|
||||
decl += ind(3) + f"ctx.{self.hook_target()}_post();"
|
||||
if self.ret.value:
|
||||
decl += ind(3) + f"return {self.ret.value};"
|
||||
return decl
|
||||
|
||||
class SpecApp(ForkAndSpec):
|
||||
def __init__(self, ver, args):
|
||||
|
||||
class SpecializeApp(ForkApp):
|
||||
def __init__(self, ver: str, args: list[Argument]):
|
||||
super().__init__(ver, args)
|
||||
self.ret = Return('', void)
|
||||
self.ret = Return("", void)
|
||||
|
||||
def base_name(self):
|
||||
return 'nativeSpecializeAppProcess'
|
||||
def hook_target(self):
|
||||
return "nativeSpecializeAppProcess"
|
||||
|
||||
class ForkServer(ForkAndSpec):
|
||||
def base_name(self):
|
||||
return 'nativeForkSystemServer'
|
||||
|
||||
class ForkServer(ForkApp):
|
||||
def hook_target(self):
|
||||
return "nativeForkSystemServer"
|
||||
|
||||
def init_args(self):
|
||||
return 'ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);'
|
||||
return "ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);"
|
||||
|
||||
|
||||
# Common args
|
||||
uid = Argument('uid', jint)
|
||||
gid = Argument('gid', jint)
|
||||
gids = Argument('gids', jintArray)
|
||||
runtime_flags = Argument('runtime_flags', jint)
|
||||
rlimits = Argument('rlimits', JArray(jintArray))
|
||||
mount_external = Argument('mount_external', jint)
|
||||
se_info = Argument('se_info', jstring)
|
||||
nice_name = Argument('nice_name', jstring)
|
||||
fds_to_close = Argument('fds_to_close', jintArray)
|
||||
instruction_set = Argument('instruction_set', jstring)
|
||||
app_data_dir = Argument('app_data_dir', jstring)
|
||||
uid = Argument("uid", jint)
|
||||
gid = Argument("gid", jint)
|
||||
gids = Argument("gids", jintArray)
|
||||
runtime_flags = Argument("runtime_flags", jint)
|
||||
rlimits = Argument("rlimits", JArray(jintArray))
|
||||
mount_external = Argument("mount_external", jint)
|
||||
se_info = Argument("se_info", jstring)
|
||||
nice_name = Argument("nice_name", jstring)
|
||||
fds_to_close = Argument("fds_to_close", jintArray)
|
||||
instruction_set = Argument("instruction_set", jstring)
|
||||
app_data_dir = Argument("app_data_dir", jstring)
|
||||
|
||||
# o
|
||||
fds_to_ignore = Argument('fds_to_ignore', jintArray, True)
|
||||
fds_to_ignore = Argument("fds_to_ignore", jintArray, True)
|
||||
|
||||
# p
|
||||
is_child_zygote = Argument('is_child_zygote', jboolean, True)
|
||||
is_child_zygote = Argument("is_child_zygote", jboolean, True)
|
||||
|
||||
# q_alt
|
||||
is_top_app = Argument('is_top_app', jboolean, True)
|
||||
is_top_app = Argument("is_top_app", jboolean, True)
|
||||
|
||||
# q running on xr
|
||||
is_perception_app = Argument("is_perception_app", jboolean, False)
|
||||
|
||||
# r
|
||||
pkg_data_info_list = Argument('pkg_data_info_list', JArray(jstring), True)
|
||||
whitelisted_data_info_list = Argument('whitelisted_data_info_list', JArray(jstring), True)
|
||||
mount_data_dirs = Argument('mount_data_dirs', jboolean, True)
|
||||
mount_storage_dirs = Argument('mount_storage_dirs', jboolean, True)
|
||||
pkg_data_info_list = Argument("pkg_data_info_list", JArray(jstring), True)
|
||||
whitelisted_data_info_list = Argument(
|
||||
"whitelisted_data_info_list", JArray(jstring), True
|
||||
)
|
||||
mount_data_dirs = Argument("mount_data_dirs", jboolean, True)
|
||||
mount_storage_dirs = Argument("mount_storage_dirs", jboolean, True)
|
||||
|
||||
# u
|
||||
mount_sysprop_overrides = Argument('mount_sysprop_overrides', jboolean, True)
|
||||
# u qpr2
|
||||
mount_sysprop_overrides = Argument("mount_sysprop_overrides", jboolean, True)
|
||||
|
||||
# b qpr2
|
||||
use_fifo_ui = Argument("use_fifo_ui", jboolean, False)
|
||||
|
||||
# server
|
||||
permitted_capabilities = Argument('permitted_capabilities', jlong)
|
||||
effective_capabilities = Argument('effective_capabilities', jlong)
|
||||
permitted_capabilities = Argument("permitted_capabilities", jlong)
|
||||
effective_capabilities = Argument("effective_capabilities", jlong)
|
||||
|
||||
# Method definitions
|
||||
fas_l = ForkAndSpec('l', [uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, nice_name, fds_to_close, instruction_set, app_data_dir])
|
||||
fas_l = ForkApp(
|
||||
"l",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
fas_o = ForkAndSpec('o', [uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir])
|
||||
fas_o = ForkApp(
|
||||
"o",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
fas_p = ForkAndSpec('p', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir])
|
||||
fas_p = ForkApp(
|
||||
"p",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
fas_q_alt = ForkAndSpec('q_alt', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app])
|
||||
fas_q_alt = ForkApp(
|
||||
"q_alt",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
],
|
||||
)
|
||||
|
||||
fas_r = ForkAndSpec('r', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app,
|
||||
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs])
|
||||
fas_r = ForkApp(
|
||||
"r",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
],
|
||||
)
|
||||
|
||||
fas_u = ForkAndSpec('u', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app,
|
||||
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides])
|
||||
fas_u = ForkApp(
|
||||
"u",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
mount_sysprop_overrides,
|
||||
],
|
||||
)
|
||||
|
||||
fas_samsung_m = ForkAndSpec('samsung_m', [uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, instruction_set, app_data_dir])
|
||||
fas_b = ForkApp(
|
||||
"b",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
use_fifo_ui,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
mount_sysprop_overrides,
|
||||
],
|
||||
)
|
||||
|
||||
fas_samsung_n = ForkAndSpec('samsung_n', [uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, instruction_set, app_data_dir, Anon(jint)])
|
||||
fas_samsung_m = ForkApp(
|
||||
"samsung_m",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
Anon(jint),
|
||||
Anon(jint),
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
fas_samsung_o = ForkAndSpec('samsung_o', [uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir])
|
||||
fas_samsung_n = ForkApp(
|
||||
"samsung_n",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
Anon(jint),
|
||||
Anon(jint),
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
Anon(jint),
|
||||
],
|
||||
)
|
||||
|
||||
fas_samsung_p = ForkAndSpec('samsung_p', [uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, fds_to_ignore, is_child_zygote,
|
||||
instruction_set, app_data_dir])
|
||||
fas_samsung_o = ForkApp(
|
||||
"samsung_o",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
Anon(jint),
|
||||
Anon(jint),
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
spec_q = SpecApp('q', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, is_child_zygote, instruction_set, app_data_dir])
|
||||
fas_samsung_p = ForkApp(
|
||||
"samsung_p",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
Anon(jint),
|
||||
Anon(jint),
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
spec_q_alt = SpecApp('q_alt', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app])
|
||||
fas_nubia_u = ForkApp(
|
||||
"nubia_u",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
fds_to_close,
|
||||
fds_to_ignore,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
use_fifo_ui,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
mount_sysprop_overrides,
|
||||
Anon(jstring),
|
||||
],
|
||||
)
|
||||
|
||||
spec_r = SpecApp('r', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name,
|
||||
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list,
|
||||
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs])
|
||||
spec_q = SpecializeApp(
|
||||
"q",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
spec_u = SpecApp('u', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name,
|
||||
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list,
|
||||
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides])
|
||||
spec_q_alt = SpecializeApp(
|
||||
"q_alt",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
],
|
||||
)
|
||||
|
||||
spec_samsung_q = SpecApp('samsung_q', [uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, Anon(jint), Anon(jint), nice_name, is_child_zygote, instruction_set, app_data_dir])
|
||||
spec_r = SpecializeApp(
|
||||
"r",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
],
|
||||
)
|
||||
|
||||
server_l = ForkServer('l', [uid, gid, gids, runtime_flags, rlimits,
|
||||
permitted_capabilities, effective_capabilities])
|
||||
spec_u = SpecializeApp(
|
||||
"u",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
mount_sysprop_overrides,
|
||||
],
|
||||
)
|
||||
|
||||
server_samsung_q = ForkServer('samsung_q', [uid, gid, gids, runtime_flags, Anon(jint), Anon(jint), rlimits,
|
||||
permitted_capabilities, effective_capabilities])
|
||||
spec_xr_u = SpecializeApp(
|
||||
"xr_u",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
is_perception_app,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
],
|
||||
)
|
||||
|
||||
def gen_jni_def(name, methods):
|
||||
decl = ''
|
||||
decl += ind(0) + f'std::array<JNINativeMethod, {len(methods)}> {name}_methods = {{{{'
|
||||
spec_samsung_q = SpecializeApp(
|
||||
"samsung_q",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
Anon(jint),
|
||||
Anon(jint),
|
||||
nice_name,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
],
|
||||
)
|
||||
|
||||
spec_nubia_u = SpecializeApp(
|
||||
"nubia_u",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
mount_external,
|
||||
se_info,
|
||||
nice_name,
|
||||
is_child_zygote,
|
||||
instruction_set,
|
||||
app_data_dir,
|
||||
is_top_app,
|
||||
pkg_data_info_list,
|
||||
whitelisted_data_info_list,
|
||||
mount_data_dirs,
|
||||
mount_storage_dirs,
|
||||
mount_sysprop_overrides,
|
||||
Anon(jstring),
|
||||
],
|
||||
)
|
||||
|
||||
server_l = ForkServer(
|
||||
"l",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
rlimits,
|
||||
permitted_capabilities,
|
||||
effective_capabilities,
|
||||
],
|
||||
)
|
||||
|
||||
server_samsung_q = ForkServer(
|
||||
"samsung_q",
|
||||
[
|
||||
uid,
|
||||
gid,
|
||||
gids,
|
||||
runtime_flags,
|
||||
Anon(jint),
|
||||
Anon(jint),
|
||||
rlimits,
|
||||
permitted_capabilities,
|
||||
effective_capabilities,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def gen_jni_def(field: str, methods: list[JNIHook]):
|
||||
decl = ""
|
||||
decl += ind(0) + f"std::array<JNINativeMethod, {len(methods)}> {field} = {{{{"
|
||||
for i, m in enumerate(methods):
|
||||
decl += ind(1) + '{'
|
||||
decl += ind(2) + f'"{m.base_name()}",'
|
||||
decl += ind(2) + f'"{m.jni()}",'
|
||||
decl += ind(2) + f'(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, {m.cpp()}) static -> {m.ret.type.cpp} {{'
|
||||
decl += m.body(name, i)
|
||||
if m.ret.value:
|
||||
decl += ind(3) + f'return {m.ret.value};'
|
||||
decl += ind(2) + '}'
|
||||
decl += ind(1) + '},'
|
||||
decl += ind(0) + '}};'
|
||||
decl += ind(1) + f"// {m.name}"
|
||||
decl += ind(1) + "{"
|
||||
decl += ind(2) + f'"{m.hook_target()}",'
|
||||
decl += ind(2) + f'"{m.jni_sig()}",'
|
||||
decl += ind(2) + f"(void *) +{m.cpp_lambda_sig()} {{"
|
||||
orig_fn_ptr = f"get_defs()->{field}[{i}].fnPtr"
|
||||
decl += m.body(orig_fn_ptr)
|
||||
decl += ind(2) + "}"
|
||||
decl += ind(1) + "},"
|
||||
decl += ind(0) + "}};"
|
||||
decl += ind(0)
|
||||
|
||||
return decl
|
||||
|
||||
with open('jni_hooks.hpp', 'w') as f:
|
||||
f.write('// Generated by gen_jni_hooks.py\n')
|
||||
|
||||
f.write(gen_jni_def('zygote', [
|
||||
fas_l, fas_o, fas_p, fas_q_alt, fas_r, fas_u, fas_samsung_m, fas_samsung_n, fas_samsung_o,
|
||||
fas_samsung_p, spec_q, spec_q_alt, spec_r, spec_u, spec_samsung_q, server_l, server_samsung_q]))
|
||||
with open("jni_hooks.hpp", "w") as f:
|
||||
f.write("// Generated by gen_jni_hooks.py\n")
|
||||
f.write("#pragma once\n\n")
|
||||
f.write("struct JniHookDefinitions;\n")
|
||||
f.write("static JniHookDefinitions *get_defs();\n\n")
|
||||
f.write("struct JniHookDefinitions {\n")
|
||||
f.write(
|
||||
gen_jni_def(
|
||||
"fork_app_methods",
|
||||
[
|
||||
fas_l,
|
||||
fas_o,
|
||||
fas_p,
|
||||
fas_q_alt,
|
||||
fas_r,
|
||||
fas_u,
|
||||
fas_b,
|
||||
fas_samsung_m,
|
||||
fas_samsung_n,
|
||||
fas_samsung_o,
|
||||
fas_samsung_p,
|
||||
fas_nubia_u,
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
f.write('\n')
|
||||
f.write(
|
||||
gen_jni_def(
|
||||
"specialize_app_methods",
|
||||
[spec_q, spec_q_alt, spec_r, spec_u, spec_xr_u, spec_samsung_q, spec_nubia_u],
|
||||
)
|
||||
)
|
||||
|
||||
f.write(gen_jni_def("fork_server_methods", [server_l, server_samsung_q]))
|
||||
|
||||
f.write("\n};\n")
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
#include <lsplt.hpp>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <consts.hpp>
|
||||
|
||||
#include "zygisk.hpp"
|
||||
#include "module.hpp"
|
||||
#include "jni_hooks.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -90,25 +90,15 @@ using namespace std;
|
||||
|
||||
constexpr const char *kZygoteInit = "com.android.internal.os.ZygoteInit";
|
||||
constexpr const char *kZygote = "com/android/internal/os/Zygote";
|
||||
|
||||
// Global contexts:
|
||||
//
|
||||
// HookContext lives as long as Zygisk is loaded in memory. It tracks the process's function
|
||||
// hooking state and bootstraps code injection until we replace the process specialization methods.
|
||||
//
|
||||
// ZygiskContext lives during the process specialization process. It implements Zygisk
|
||||
// features, such as loading modules and customizing process fork/specialization.
|
||||
|
||||
ZygiskContext *g_ctx;
|
||||
struct HookContext;
|
||||
static HookContext *g_hook;
|
||||
constexpr const char *kForkApp = "nativeForkAndSpecialize";
|
||||
constexpr const char *kSpecializeApp = "nativeSpecializeAppProcess";
|
||||
constexpr const char *kForkServer = "nativeForkSystemServer";
|
||||
|
||||
using JNIMethods = std::span<JNINativeMethod>;
|
||||
using JNIMethodsDyn = std::pair<unique_ptr<JNINativeMethod[]>, size_t>;
|
||||
|
||||
struct HookContext {
|
||||
#include "jni_hooks.hpp"
|
||||
struct HookContext : JniHookDefinitions {
|
||||
|
||||
// std::array<JNINativeMethod> zygote_methods
|
||||
vector<tuple<dev_t, ino_t, const char *, void **>> plt_backup;
|
||||
const NativeBridgeRuntimeCallbacks *runtime_callbacks = nullptr;
|
||||
void *self_handle = nullptr;
|
||||
@@ -119,15 +109,34 @@ struct HookContext {
|
||||
void restore_plt_hook();
|
||||
void hook_zygote_jni();
|
||||
void restore_zygote_hook(JNIEnv *env);
|
||||
void hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods);
|
||||
void hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods) const;
|
||||
void post_native_bridge_load(void *handle);
|
||||
|
||||
private:
|
||||
void register_hook(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func);
|
||||
int hook_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods) const;
|
||||
JNIMethodsDyn get_jni_methods(JNIEnv *env, jclass clazz) const;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// Global contexts:
|
||||
//
|
||||
// HookContext lives as long as Zygisk is loaded in memory. It tracks the process's function
|
||||
// hooking state and bootstraps code injection until we replace the process specialization methods.
|
||||
//
|
||||
// ZygiskContext lives during the process specialization process. It implements Zygisk
|
||||
// features, such as loading modules and customizing process fork/specialization.
|
||||
|
||||
ZygiskContext *g_ctx;
|
||||
static HookContext *g_hook;
|
||||
|
||||
static JniHookDefinitions *get_defs() {
|
||||
return g_hook;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
#define DCL_HOOK_FUNC(ret, func, ...) \
|
||||
ret (*old_##func)(__VA_ARGS__); \
|
||||
ret new_##func(__VA_ARGS__)
|
||||
@@ -452,56 +461,74 @@ void HookContext::restore_plt_hook() {
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
void HookContext::hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods) {
|
||||
jclass clazz;
|
||||
if (!runtime_callbacks || !env || !clz || !(clazz = env->FindClass(clz))) {
|
||||
for (auto &method : methods) {
|
||||
method.fnPtr = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
JNIMethodsDyn HookContext::get_jni_methods(JNIEnv *env, jclass clazz) const {
|
||||
size_t total = runtime_callbacks->getNativeMethodCount(env, clazz);
|
||||
auto methods = std::make_unique_for_overwrite<JNINativeMethod[]>(total);
|
||||
runtime_callbacks->getNativeMethods(env, clazz, methods.get(), total);
|
||||
return std::make_pair(std::move(methods), total);
|
||||
}
|
||||
|
||||
// Backup existing methods
|
||||
auto total = runtime_callbacks->getNativeMethodCount(env, clazz);
|
||||
auto old_methods = std::make_unique_for_overwrite<JNINativeMethod[]>(total);
|
||||
runtime_callbacks->getNativeMethods(env, clazz, old_methods.get(), total);
|
||||
|
||||
// WARNING: the signature field returned from getNativeMethods is in a non-standard format.
|
||||
// DO NOT TRY TO USE IT. This is the reason why we try to call RegisterNatives on every single
|
||||
// provided JNI methods directly to be 100% sure about whether a signature matches or not.
|
||||
|
||||
// Replace methods
|
||||
static void register_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods) {
|
||||
for (auto &method : methods) {
|
||||
// It's useful to allow nullptr function pointer for restoring hook
|
||||
if (!method.fnPtr) continue;
|
||||
|
||||
// It's normal that the method is not found
|
||||
if (env->RegisterNatives(clazz, &method, 1) == JNI_ERR || env->ExceptionCheck() == JNI_TRUE) {
|
||||
if (auto exception = env->ExceptionOccurred()) {
|
||||
env->DeleteLocalRef(exception);
|
||||
}
|
||||
env->ExceptionClear();
|
||||
method.fnPtr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int HookContext::hook_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods) const {
|
||||
// Backup existing methods
|
||||
auto o = get_jni_methods(env, clazz);
|
||||
const auto old_methods = span(o.first.get(), o.second);
|
||||
|
||||
// WARNING: the signature field returned from getNativeMethods is in a non-standard format.
|
||||
// DO NOT TRY TO USE IT. This is the reason why we try to call RegisterNatives on every single
|
||||
// provided JNI methods directly to be 100% sure about whether a signature matches or not.
|
||||
|
||||
// Replace methods
|
||||
register_jni_methods(env, clazz, methods);
|
||||
|
||||
// Fetch the new set of native methods
|
||||
auto new_methods = std::make_unique_for_overwrite<JNINativeMethod[]>(total);
|
||||
runtime_callbacks->getNativeMethods(env, clazz, new_methods.get(), total);
|
||||
auto n = get_jni_methods(env, clazz);
|
||||
const auto new_methods = span(n.first.get(), n.second);
|
||||
|
||||
// Find the old function pointer and return to caller
|
||||
int hook_count = 0;
|
||||
for (auto &method : methods) {
|
||||
if (!method.fnPtr) continue;
|
||||
for (auto i = 0; i < total; ++i) {
|
||||
auto &new_method = new_methods[i];
|
||||
for (const auto &new_method : new_methods) {
|
||||
if (new_method.fnPtr == method.fnPtr) {
|
||||
auto &old_method = old_methods[i];
|
||||
ZLOGV("replace %s#%s%s %p -> %p\n", clz, method.name, method.signature, old_method.fnPtr, method.fnPtr);
|
||||
method.fnPtr = old_method.fnPtr;
|
||||
break;
|
||||
for (const auto &old_method : old_methods) {
|
||||
if (strcmp(old_method.name, new_method.name) == 0 &&
|
||||
strcmp(old_method.signature, new_method.signature) == 0) {
|
||||
ZLOGV("replace %s %s %p -> %p\n",
|
||||
method.name, method.signature, old_method.fnPtr, method.fnPtr);
|
||||
method.fnPtr = old_method.fnPtr;
|
||||
++hook_count;
|
||||
// Break 2 levels of for loop
|
||||
goto next_method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next_method:
|
||||
}
|
||||
return hook_count;
|
||||
}
|
||||
|
||||
|
||||
void HookContext::hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods) const {
|
||||
jclass clazz;
|
||||
if (!runtime_callbacks || !env || !clz || !((clazz = env->FindClass(clz)))) {
|
||||
ranges::for_each(methods, [](auto &m) { m.fnPtr = nullptr; });
|
||||
return;
|
||||
}
|
||||
hook_jni_methods(env, clazz, methods);
|
||||
}
|
||||
|
||||
void HookContext::hook_zygote_jni() {
|
||||
@@ -538,11 +565,54 @@ void HookContext::hook_zygote_jni() {
|
||||
if (res != JNI_OK || env == nullptr) {
|
||||
ZLOGW("JNIEnv not found\n");
|
||||
}
|
||||
hook_jni_methods(env, kZygote, zygote_methods);
|
||||
|
||||
JNINativeMethod missing_method{};
|
||||
bool replaced_fork_app = false;
|
||||
bool replaced_specialize_app = false;
|
||||
bool replaced_fork_server = false;
|
||||
|
||||
jclass clazz = env->FindClass(kZygote);
|
||||
auto [ptr, count] = get_jni_methods(env, clazz);
|
||||
for (const auto methods = span(ptr.get(), count); const auto &method : methods) {
|
||||
if (strcmp(method.name, kForkApp) == 0) {
|
||||
if (hook_jni_methods(env, clazz, fork_app_methods) == 0) {
|
||||
missing_method = method;
|
||||
break;
|
||||
}
|
||||
replaced_fork_app = true;
|
||||
} else if (strcmp(method.name, kSpecializeApp) == 0) {
|
||||
if (hook_jni_methods(env, clazz, specialize_app_methods) == 0) {
|
||||
missing_method = method;
|
||||
break;
|
||||
}
|
||||
replaced_specialize_app = true;
|
||||
} else if (strcmp(method.name, kForkServer) == 0) {
|
||||
if (hook_jni_methods(env, clazz, fork_server_methods) == 0) {
|
||||
missing_method = method;
|
||||
break;
|
||||
}
|
||||
replaced_fork_server = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (missing_method.name != nullptr) {
|
||||
ZLOGE("Cannot hook method: %s %s\n", missing_method.name, missing_method.signature);
|
||||
// Restore methods that were already replaced
|
||||
if (replaced_fork_app) register_jni_methods(env, clazz, fork_app_methods);
|
||||
if (replaced_specialize_app) register_jni_methods(env, clazz, specialize_app_methods);
|
||||
if (replaced_fork_server) register_jni_methods(env, clazz, fork_server_methods);
|
||||
// Clear the method lists just in case
|
||||
ranges::for_each(fork_app_methods, [](auto &m) { m.fnPtr = nullptr; });
|
||||
ranges::for_each(specialize_app_methods, [](auto &m) { m.fnPtr = nullptr; });
|
||||
ranges::for_each(fork_server_methods, [](auto &m) { m.fnPtr = nullptr; });
|
||||
}
|
||||
}
|
||||
|
||||
void HookContext::restore_zygote_hook(JNIEnv *env) {
|
||||
hook_jni_methods(env, kZygote, zygote_methods);
|
||||
jclass clazz = env->FindClass(kZygote);
|
||||
register_jni_methods(env, clazz, fork_app_methods);
|
||||
register_jni_methods(env, clazz, specialize_app_methods);
|
||||
register_jni_methods(env, clazz, fork_server_methods);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@@ -553,5 +623,5 @@ void hook_entry() {
|
||||
}
|
||||
|
||||
void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods) {
|
||||
g_hook->hook_jni_methods(env, clz, { methods, (size_t) numMethods });
|
||||
g_hook->hook_jni_methods(env, clz, { methods, static_cast<size_t>(numMethods) });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// Generated by gen_jni_hooks.py
|
||||
#pragma once
|
||||
|
||||
std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
struct JniHookDefinitions;
|
||||
static JniHookDefinitions *get_defs();
|
||||
|
||||
struct JniHookDefinitions {
|
||||
|
||||
std::array<JNINativeMethod, 12> fork_app_methods = {{
|
||||
// nativeForkAndSpecialize_l
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
@@ -8,13 +15,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[0].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[0].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_o
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
@@ -23,13 +31,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[1].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[1].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_p
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
|
||||
@@ -39,13 +48,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[2].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[2].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_q_alt
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z)I",
|
||||
@@ -56,13 +66,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.is_top_app = &is_top_app;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(g_hook->zygote_methods[3].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(get_defs()->fork_app_methods[3].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_r
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
|
||||
@@ -77,13 +88,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.mount_storage_dirs = &mount_storage_dirs;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(g_hook->zygote_methods[4].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(get_defs()->fork_app_methods[4].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_u
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)I",
|
||||
@@ -99,13 +111,37 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.mount_sysprop_overrides = &mount_sysprop_overrides;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(g_hook->zygote_methods[5].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->fork_app_methods[5].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_b
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;ZZ[Ljava/lang/String;[Ljava/lang/String;ZZZ)I",
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) static -> jint {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
args.pkg_data_info_list = &pkg_data_info_list;
|
||||
args.whitelisted_data_info_list = &whitelisted_data_info_list;
|
||||
args.mount_data_dirs = &mount_data_dirs;
|
||||
args.mount_storage_dirs = &mount_storage_dirs;
|
||||
args.mount_sysprop_overrides = &mount_sysprop_overrides;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->fork_app_methods[6].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, use_fifo_ui, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_samsung_m
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
@@ -113,13 +149,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _0, jint _1, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[6].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _0, jint _1, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[7].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _0, _1, nice_name, fds_to_close, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_samsung_n
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;I)I",
|
||||
@@ -127,13 +164,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _2, jint _3, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint _4)>(g_hook->zygote_methods[7].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _2, jint _3, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint _4)>(get_defs()->fork_app_methods[8].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _2, _3, nice_name, fds_to_close, instruction_set, app_data_dir, _4
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_samsung_o
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
@@ -142,13 +180,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _5, jint _6, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[8].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _5, jint _6, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[9].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _5, _6, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_samsung_p
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
|
||||
@@ -158,13 +197,40 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _7, jint _8, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[9].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _7, jint _8, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[10].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _7, _8, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkAndSpecialize_nubia_u
|
||||
{
|
||||
"nativeForkAndSpecialize",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;ZZ[Ljava/lang/String;[Ljava/lang/String;ZZZLjava/lang/String;)I",
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jstring _9) static -> jint {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
args.fds_to_ignore = &fds_to_ignore;
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
args.pkg_data_info_list = &pkg_data_info_list;
|
||||
args.whitelisted_data_info_list = &whitelisted_data_info_list;
|
||||
args.mount_data_dirs = &mount_data_dirs;
|
||||
args.mount_storage_dirs = &mount_storage_dirs;
|
||||
args.mount_sysprop_overrides = &mount_sysprop_overrides;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkAndSpecialize_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jstring _9)>(get_defs()->fork_app_methods[11].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, use_fifo_ui, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides, _9
|
||||
);
|
||||
ctx.nativeForkAndSpecialize_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
}};
|
||||
|
||||
std::array<JNINativeMethod, 7> specialize_app_methods = {{
|
||||
// nativeSpecializeAppProcess_q
|
||||
{
|
||||
"nativeSpecializeAppProcess",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
|
||||
@@ -173,12 +239,13 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[10].fnPtr)(
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->specialize_app_methods[0].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
},
|
||||
// nativeSpecializeAppProcess_q_alt
|
||||
{
|
||||
"nativeSpecializeAppProcess",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z)V",
|
||||
@@ -188,12 +255,13 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.is_top_app = &is_top_app;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(g_hook->zygote_methods[11].fnPtr)(
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(get_defs()->specialize_app_methods[1].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app
|
||||
);
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
},
|
||||
// nativeSpecializeAppProcess_r
|
||||
{
|
||||
"nativeSpecializeAppProcess",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
|
||||
@@ -207,12 +275,13 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.mount_storage_dirs = &mount_storage_dirs;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(g_hook->zygote_methods[12].fnPtr)(
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(get_defs()->specialize_app_methods[2].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs
|
||||
);
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
},
|
||||
// nativeSpecializeAppProcess_u
|
||||
{
|
||||
"nativeSpecializeAppProcess",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)V",
|
||||
@@ -227,26 +296,72 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
args.mount_sysprop_overrides = &mount_sysprop_overrides;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(g_hook->zygote_methods[13].fnPtr)(
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->specialize_app_methods[3].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
|
||||
);
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
},
|
||||
// nativeSpecializeAppProcess_xr_u
|
||||
{
|
||||
"nativeSpecializeAppProcess",
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _9, jint _10, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) static -> void {
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;ZZ[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean is_perception_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) static -> void {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
args.pkg_data_info_list = &pkg_data_info_list;
|
||||
args.whitelisted_data_info_list = &whitelisted_data_info_list;
|
||||
args.mount_data_dirs = &mount_data_dirs;
|
||||
args.mount_storage_dirs = &mount_storage_dirs;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _9, jint _10, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[14].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _9, _10, nice_name, is_child_zygote, instruction_set, app_data_dir
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean is_perception_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(get_defs()->specialize_app_methods[4].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, is_perception_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs
|
||||
);
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
},
|
||||
// nativeSpecializeAppProcess_samsung_q
|
||||
{
|
||||
"nativeSpecializeAppProcess",
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _10, jint _11, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) static -> void {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _10, jint _11, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->specialize_app_methods[5].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _10, _11, nice_name, is_child_zygote, instruction_set, app_data_dir
|
||||
);
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
},
|
||||
// nativeSpecializeAppProcess_nubia_u
|
||||
{
|
||||
"nativeSpecializeAppProcess",
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZLjava/lang/String;)V",
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jstring _12) static -> void {
|
||||
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
|
||||
args.is_child_zygote = &is_child_zygote;
|
||||
args.is_top_app = &is_top_app;
|
||||
args.pkg_data_info_list = &pkg_data_info_list;
|
||||
args.whitelisted_data_info_list = &whitelisted_data_info_list;
|
||||
args.mount_data_dirs = &mount_data_dirs;
|
||||
args.mount_storage_dirs = &mount_storage_dirs;
|
||||
args.mount_sysprop_overrides = &mount_sysprop_overrides;
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeSpecializeAppProcess_pre();
|
||||
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jstring _12)>(get_defs()->specialize_app_methods[6].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides, _12
|
||||
);
|
||||
ctx.nativeSpecializeAppProcess_post();
|
||||
}
|
||||
},
|
||||
}};
|
||||
|
||||
std::array<JNINativeMethod, 2> fork_server_methods = {{
|
||||
// nativeForkSystemServer_l
|
||||
{
|
||||
"nativeForkSystemServer",
|
||||
"(II[II[[IJJ)I",
|
||||
@@ -254,22 +369,23 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkSystemServer_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(g_hook->zygote_methods[15].fnPtr)(
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(get_defs()->fork_server_methods[0].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities
|
||||
);
|
||||
ctx.nativeForkSystemServer_post();
|
||||
return ctx.pid;
|
||||
}
|
||||
},
|
||||
// nativeForkSystemServer_samsung_q
|
||||
{
|
||||
"nativeForkSystemServer",
|
||||
"(II[IIII[[IJJ)I",
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _11, jint _12, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) static -> jint {
|
||||
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _13, jint _14, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) static -> jint {
|
||||
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
|
||||
ZygiskContext ctx(env, &args);
|
||||
ctx.nativeForkSystemServer_pre();
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _11, jint _12, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(g_hook->zygote_methods[16].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, _11, _12, rlimits, permitted_capabilities, effective_capabilities
|
||||
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _13, jint _14, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(get_defs()->fork_server_methods[1].fnPtr)(
|
||||
env, clazz, uid, gid, gids, runtime_flags, _13, _14, rlimits, permitted_capabilities, effective_capabilities
|
||||
);
|
||||
ctx.nativeForkSystemServer_post();
|
||||
return ctx.pid;
|
||||
@@ -277,3 +393,4 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
|
||||
},
|
||||
}};
|
||||
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#endif
|
||||
|
||||
// Extreme verbose logging
|
||||
//#define ZLOGV(...) ZLOGD(__VA_ARGS__)
|
||||
// #define ZLOGV(...) ZLOGD(__VA_ARGS__)
|
||||
#define ZLOGV(...) (void*)0
|
||||
|
||||
void hook_entry();
|
||||
|
||||
12
native/src/external/Android.mk
vendored
12
native/src/external/Android.mk
vendored
@@ -11,18 +11,6 @@ LOCAL_SRC_FILES := \
|
||||
xz-embedded/xz_dec_stream.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# liblz4.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := liblz4
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/lz4/lib
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_SRC_FILES := \
|
||||
lz4/lib/lz4.c \
|
||||
lz4/lib/lz4frame.c \
|
||||
lz4/lib/lz4hc.c \
|
||||
lz4/lib/xxhash.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
SE_PATH := $(LOCAL_PATH)/selinux
|
||||
|
||||
# libsepol.a
|
||||
|
||||
1
native/src/external/lz4
vendored
1
native/src/external/lz4
vendored
Submodule native/src/external/lz4 deleted from d44371841a
10
native/src/external/lz4-sys/Cargo.toml
vendored
10
native/src/external/lz4-sys/Cargo.toml
vendored
@@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "lz4-sys"
|
||||
license = "MIT"
|
||||
version = "1.11.1+lz4-1.10.0"
|
||||
authors = [ "Jens Heyens <jens.heyens@ewetel.net>", "Artem V. Navrotskiy <bozaro@buzzsoft.ru>", "Patrick Marks <pmarks@gmail.com>"]
|
||||
description = "Rust LZ4 sys package."
|
||||
repository = "https://github.com/10xGenomics/lz4-rs"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
480
native/src/external/lz4-sys/src/lib.rs
vendored
480
native/src/external/lz4-sys/src/lib.rs
vendored
@@ -1,480 +0,0 @@
|
||||
#![allow(unexpected_cfgs)]
|
||||
#![no_std]
|
||||
extern crate libc;
|
||||
|
||||
#[cfg(not(all(
|
||||
target_arch = "wasm32",
|
||||
not(any(target_env = "wasi", target_os = "wasi"))
|
||||
)))]
|
||||
pub use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t};
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(any(target_env = "wasi", target_os = "wasi"))
|
||||
))]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(any(target_env = "wasi", target_os = "wasi"))
|
||||
))]
|
||||
mod wasm_shim;
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(any(target_env = "wasi", target_os = "wasi"))
|
||||
))]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(any(target_env = "wasi", target_os = "wasi"))
|
||||
))]
|
||||
pub use std::os::raw::{c_char, c_int, c_uint, c_ulonglong, c_void};
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(any(target_env = "wasi", target_os = "wasi"))
|
||||
))]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type size_t = usize;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4FCompressionContext(pub *mut c_void);
|
||||
unsafe impl Send for LZ4FCompressionContext {}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4FDecompressionContext(pub *mut c_void);
|
||||
unsafe impl Send for LZ4FDecompressionContext {}
|
||||
|
||||
pub type LZ4FErrorCode = size_t;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum BlockSize {
|
||||
Default = 0, // Default - 64KB
|
||||
Max64KB = 4,
|
||||
Max256KB = 5,
|
||||
Max1MB = 6,
|
||||
Max4MB = 7,
|
||||
}
|
||||
|
||||
impl BlockSize {
|
||||
pub fn get_size(&self) -> usize {
|
||||
match self {
|
||||
&BlockSize::Default | &BlockSize::Max64KB => 64 * 1024,
|
||||
&BlockSize::Max256KB => 256 * 1024,
|
||||
&BlockSize::Max1MB => 1 * 1024 * 1024,
|
||||
&BlockSize::Max4MB => 4 * 1024 * 1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum BlockMode {
|
||||
Linked = 0,
|
||||
Independent,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum ContentChecksum {
|
||||
NoChecksum = 0,
|
||||
ChecksumEnabled,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum FrameType {
|
||||
Frame = 0,
|
||||
SkippableFrame,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum BlockChecksum {
|
||||
NoBlockChecksum = 0,
|
||||
BlockChecksumEnabled,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4FFrameInfo {
|
||||
pub block_size_id: BlockSize,
|
||||
pub block_mode: BlockMode,
|
||||
pub content_checksum_flag: ContentChecksum,
|
||||
pub frame_type: FrameType,
|
||||
pub content_size: c_ulonglong,
|
||||
pub dict_id: c_uint,
|
||||
pub block_checksum_flag: BlockChecksum,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4FPreferences {
|
||||
pub frame_info: LZ4FFrameInfo,
|
||||
pub compression_level: c_uint, // 0 == default (fast mode); values above 16 count as 16
|
||||
pub auto_flush: c_uint, // 1 == always flush : reduce need for tmp buffer
|
||||
pub favor_dec_speed: c_uint, // 1 == favor decompression speed over ratio, requires level 10+
|
||||
pub reserved: [c_uint; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4FCompressOptions {
|
||||
pub stable_src: c_uint, /* 1 == src content will remain available on future calls
|
||||
* to LZ4F_compress(); avoid saving src content within tmp
|
||||
* buffer as future dictionary */
|
||||
pub reserved: [c_uint; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4FDecompressOptions {
|
||||
pub stable_dst: c_uint, /* guarantee that decompressed data will still be there on next
|
||||
* function calls (avoid storage into tmp buffers) */
|
||||
pub reserved: [c_uint; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4StreamEncode(c_void);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LZ4StreamDecode(c_void);
|
||||
|
||||
pub const LZ4F_VERSION: c_uint = 100;
|
||||
|
||||
extern "C" {
|
||||
|
||||
// int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
|
||||
#[allow(non_snake_case)]
|
||||
pub fn LZ4_compress_default(
|
||||
source: *const c_char,
|
||||
dest: *mut c_char,
|
||||
sourceSize: c_int,
|
||||
maxDestSize: c_int,
|
||||
) -> c_int;
|
||||
|
||||
// int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
|
||||
#[allow(non_snake_case)]
|
||||
pub fn LZ4_compress_fast(
|
||||
source: *const c_char,
|
||||
dest: *mut c_char,
|
||||
sourceSize: c_int,
|
||||
maxDestSize: c_int,
|
||||
acceleration: c_int,
|
||||
) -> c_int;
|
||||
|
||||
// int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel);
|
||||
#[allow(non_snake_case)]
|
||||
pub fn LZ4_compress_HC(
|
||||
src: *const c_char,
|
||||
dst: *mut c_char,
|
||||
srcSize: c_int,
|
||||
dstCapacity: c_int,
|
||||
compressionLevel: c_int,
|
||||
) -> c_int;
|
||||
|
||||
// int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
|
||||
#[allow(non_snake_case)]
|
||||
pub fn LZ4_decompress_safe(
|
||||
source: *const c_char,
|
||||
dest: *mut c_char,
|
||||
compressedSize: c_int,
|
||||
maxDecompressedSize: c_int,
|
||||
) -> c_int;
|
||||
|
||||
// unsigned LZ4F_isError(LZ4F_errorCode_t code);
|
||||
pub fn LZ4F_isError(code: size_t) -> c_uint;
|
||||
|
||||
// const char* LZ4F_getErrorName(LZ4F_errorCode_t code);
|
||||
pub fn LZ4F_getErrorName(code: size_t) -> *const c_char;
|
||||
|
||||
// LZ4F_createCompressionContext() :
|
||||
// The first thing to do is to create a compressionContext object, which will be used in all
|
||||
// compression operations.
|
||||
// This is achieved using LZ4F_createCompressionContext(), which takes as argument a version
|
||||
// and an LZ4F_preferences_t structure.
|
||||
// The version provided MUST be LZ4F_VERSION. It is intended to track potential version
|
||||
// differences between different binaries.
|
||||
// The function will provide a pointer to a fully allocated LZ4F_compressionContext_t object.
|
||||
// If the result LZ4F_errorCode_t is not zero, there was an error during context creation.
|
||||
// Object can release its memory using LZ4F_freeCompressionContext();
|
||||
//
|
||||
// LZ4F_errorCode_t LZ4F_createCompressionContext(
|
||||
// LZ4F_compressionContext_t* LZ4F_compressionContextPtr,
|
||||
// unsigned version);
|
||||
pub fn LZ4F_createCompressionContext(
|
||||
ctx: &mut LZ4FCompressionContext,
|
||||
version: c_uint,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_errorCode_t LZ4F_freeCompressionContext(
|
||||
// LZ4F_compressionContext_t LZ4F_compressionContext);
|
||||
pub fn LZ4F_freeCompressionContext(ctx: LZ4FCompressionContext) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_compressBegin() :
|
||||
// will write the frame header into dstBuffer.
|
||||
// dstBuffer must be large enough to accommodate a header (dstMaxSize). Maximum header
|
||||
// size is 19 bytes.
|
||||
// The LZ4F_preferences_t structure is optional : you can provide NULL as argument, all
|
||||
// preferences will then be set to default.
|
||||
// The result of the function is the number of bytes written into dstBuffer for the header
|
||||
// or an error code (can be tested using LZ4F_isError())
|
||||
//
|
||||
// size_t LZ4F_compressBegin(LZ4F_compressionContext_t compressionContext,
|
||||
// void* dstBuffer,
|
||||
// size_t dstMaxSize,
|
||||
// const LZ4F_preferences_t* preferencesPtr);
|
||||
pub fn LZ4F_compressBegin(
|
||||
ctx: LZ4FCompressionContext,
|
||||
dstBuffer: *mut u8,
|
||||
dstMaxSize: size_t,
|
||||
preferencesPtr: *const LZ4FPreferences,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_compressBound() :
|
||||
// Provides the minimum size of Dst buffer given srcSize to handle worst case situations.
|
||||
// preferencesPtr is optional : you can provide NULL as argument, all preferences will then
|
||||
// be set to default.
|
||||
// Note that different preferences will produce in different results.
|
||||
//
|
||||
// size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr);
|
||||
pub fn LZ4F_compressBound(
|
||||
srcSize: size_t,
|
||||
preferencesPtr: *const LZ4FPreferences,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_compressUpdate()
|
||||
// LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
|
||||
// The most important rule is that dstBuffer MUST be large enough (dstMaxSize) to ensure
|
||||
// compression completion even in worst case.
|
||||
// If this condition is not respected, LZ4F_compress() will fail (result is an errorCode)
|
||||
// You can get the minimum value of dstMaxSize by using LZ4F_compressBound()
|
||||
// The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
|
||||
// The result of the function is the number of bytes written into dstBuffer : it can be zero,
|
||||
// meaning input data was just buffered.
|
||||
// The function outputs an error code if it fails (can be tested using LZ4F_isError())
|
||||
//
|
||||
// size_t LZ4F_compressUpdate(LZ4F_compressionContext_t compressionContext,
|
||||
// void* dstBuffer,
|
||||
// size_t dstMaxSize,
|
||||
// const void* srcBuffer,
|
||||
// size_t srcSize,
|
||||
// const LZ4F_compressOptions_t* compressOptionsPtr);
|
||||
pub fn LZ4F_compressUpdate(
|
||||
ctx: LZ4FCompressionContext,
|
||||
dstBuffer: *mut u8,
|
||||
dstMaxSize: size_t,
|
||||
srcBuffer: *const u8,
|
||||
srcSize: size_t,
|
||||
compressOptionsPtr: *const LZ4FCompressOptions,
|
||||
) -> size_t;
|
||||
|
||||
// LZ4F_flush()
|
||||
// Should you need to create compressed data immediately, without waiting for a block
|
||||
// to be be filled, you can call LZ4_flush(), which will immediately compress any remaining
|
||||
// data buffered within compressionContext.
|
||||
// The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
|
||||
// The result of the function is the number of bytes written into dstBuffer
|
||||
// (it can be zero, this means there was no data left within compressionContext)
|
||||
// The function outputs an error code if it fails (can be tested using LZ4F_isError())
|
||||
//
|
||||
// size_t LZ4F_flush(LZ4F_compressionContext_t compressionContext,
|
||||
// void* dstBuffer,
|
||||
// size_t dstMaxSize,
|
||||
// const LZ4F_compressOptions_t* compressOptionsPtr);
|
||||
pub fn LZ4F_flush(
|
||||
ctx: LZ4FCompressionContext,
|
||||
dstBuffer: *mut u8,
|
||||
dstMaxSize: size_t,
|
||||
compressOptionsPtr: *const LZ4FCompressOptions,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_compressEnd()
|
||||
// When you want to properly finish the compressed frame, just call LZ4F_compressEnd().
|
||||
// It will flush whatever data remained within compressionContext (like LZ4_flush())
|
||||
// but also properly finalize the frame, with an endMark and a checksum.
|
||||
// The result of the function is the number of bytes written into dstBuffer
|
||||
// (necessarily >= 4 (endMark size))
|
||||
// The function outputs an error code if it fails (can be tested using LZ4F_isError())
|
||||
// The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
|
||||
// compressionContext can then be used again, starting with LZ4F_compressBegin().
|
||||
//
|
||||
// size_t LZ4F_compressEnd(LZ4F_compressionContext_t compressionContext,
|
||||
// void* dstBuffer,
|
||||
// size_t dstMaxSize,
|
||||
// const LZ4F_compressOptions_t* compressOptionsPtr);
|
||||
pub fn LZ4F_compressEnd(
|
||||
ctx: LZ4FCompressionContext,
|
||||
dstBuffer: *mut u8,
|
||||
dstMaxSize: size_t,
|
||||
compressOptionsPtr: *const LZ4FCompressOptions,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_createDecompressionContext() :
|
||||
// The first thing to do is to create a decompressionContext object, which will be used
|
||||
// in all decompression operations.
|
||||
// This is achieved using LZ4F_createDecompressionContext().
|
||||
// The version provided MUST be LZ4F_VERSION. It is intended to track potential version
|
||||
// differences between different binaries.
|
||||
// The function will provide a pointer to a fully allocated and initialized
|
||||
// LZ4F_decompressionContext_t object.
|
||||
// If the result LZ4F_errorCode_t is not OK_NoError, there was an error during
|
||||
// context creation.
|
||||
// Object can release its memory using LZ4F_freeDecompressionContext();
|
||||
//
|
||||
// LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_decompressionContext_t* ctxPtr,
|
||||
// unsigned version);
|
||||
pub fn LZ4F_createDecompressionContext(
|
||||
ctx: &mut LZ4FDecompressionContext,
|
||||
version: c_uint,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_decompressionContext_t ctx);
|
||||
pub fn LZ4F_freeDecompressionContext(ctx: LZ4FDecompressionContext) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_getFrameInfo()
|
||||
// This function decodes frame header information, such as blockSize.
|
||||
// It is optional : you could start by calling directly LZ4F_decompress() instead.
|
||||
// The objective is to extract header information without starting decompression, typically
|
||||
// for allocation purposes.
|
||||
// LZ4F_getFrameInfo() can also be used *after* starting decompression, on a
|
||||
// valid LZ4F_decompressionContext_t.
|
||||
// The number of bytes read from srcBuffer will be provided within *srcSizePtr
|
||||
// (necessarily <= original value).
|
||||
// You are expected to resume decompression from where it stopped (srcBuffer + *srcSizePtr)
|
||||
// The function result is an hint of how many srcSize bytes LZ4F_decompress() expects for
|
||||
// next call, or an error code which can be tested using LZ4F_isError().
|
||||
//
|
||||
// size_t LZ4F_getFrameInfo(LZ4F_decompressionContext_t ctx,
|
||||
// LZ4F_frameInfo_t* frameInfoPtr,
|
||||
// const void* srcBuffer, size_t* srcSizePtr);
|
||||
pub fn LZ4F_getFrameInfo(
|
||||
ctx: LZ4FDecompressionContext,
|
||||
frameInfoPtr: &mut LZ4FFrameInfo,
|
||||
srcBuffer: *const u8,
|
||||
srcSizePtr: &mut size_t,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// LZ4F_decompress()
|
||||
// Call this function repetitively to regenerate data compressed within srcBuffer.
|
||||
// The function will attempt to decode *srcSizePtr bytes from srcBuffer, into dstBuffer of
|
||||
// maximum size *dstSizePtr.
|
||||
//
|
||||
// The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr
|
||||
// (necessarily <= original value).
|
||||
//
|
||||
// The number of bytes read from srcBuffer will be provided within *srcSizePtr
|
||||
// (necessarily <= original value).
|
||||
// If number of bytes read is < number of bytes provided, then decompression operation
|
||||
// is not completed. It typically happens when dstBuffer is not large enough to contain
|
||||
// all decoded data.
|
||||
// LZ4F_decompress() must be called again, starting from where it stopped
|
||||
// (srcBuffer + *srcSizePtr)
|
||||
// The function will check this condition, and refuse to continue if it is not respected.
|
||||
//
|
||||
// dstBuffer is supposed to be flushed between each call to the function, since its content
|
||||
// will be overwritten.
|
||||
// dst arguments can be changed at will with each consecutive call to the function.
|
||||
//
|
||||
// The function result is an hint of how many srcSize bytes LZ4F_decompress() expects for
|
||||
// next call.
|
||||
// Schematically, it's the size of the current (or remaining) compressed block + header of
|
||||
// next block.
|
||||
// Respecting the hint provides some boost to performance, since it does skip intermediate
|
||||
// buffers.
|
||||
// This is just a hint, you can always provide any srcSize you want.
|
||||
// When a frame is fully decoded, the function result will be 0. (no more data expected)
|
||||
// If decompression failed, function result is an error code, which can be tested
|
||||
// using LZ4F_isError().
|
||||
//
|
||||
// size_t LZ4F_decompress(LZ4F_decompressionContext_t ctx,
|
||||
// void* dstBuffer, size_t* dstSizePtr,
|
||||
// const void* srcBuffer, size_t* srcSizePtr,
|
||||
// const LZ4F_decompressOptions_t* optionsPtr);
|
||||
pub fn LZ4F_decompress(
|
||||
ctx: LZ4FDecompressionContext,
|
||||
dstBuffer: *mut u8,
|
||||
dstSizePtr: &mut size_t,
|
||||
srcBuffer: *const u8,
|
||||
srcSizePtr: &mut size_t,
|
||||
optionsPtr: *const LZ4FDecompressOptions,
|
||||
) -> LZ4FErrorCode;
|
||||
|
||||
// int LZ4_versionNumber(void)
|
||||
pub fn LZ4_versionNumber() -> c_int;
|
||||
|
||||
// int LZ4_compressBound(int isize)
|
||||
pub fn LZ4_compressBound(size: c_int) -> c_int;
|
||||
|
||||
// LZ4_stream_t* LZ4_createStream(void)
|
||||
pub fn LZ4_createStream() -> *mut LZ4StreamEncode;
|
||||
|
||||
// int LZ4_compress_continue(LZ4_stream_t* LZ4_streamPtr,
|
||||
// const char* source,
|
||||
// char* dest,
|
||||
// int inputSize)
|
||||
pub fn LZ4_compress_continue(
|
||||
LZ4_stream: *mut LZ4StreamEncode,
|
||||
source: *const u8,
|
||||
dest: *mut u8,
|
||||
input_size: c_int,
|
||||
) -> c_int;
|
||||
|
||||
// int LZ4_freeStream(LZ4_stream_t* LZ4_streamPtr)
|
||||
pub fn LZ4_freeStream(LZ4_stream: *mut LZ4StreamEncode) -> c_int;
|
||||
|
||||
// int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode,
|
||||
// const char* dictionary,
|
||||
// int dictSize)
|
||||
pub fn LZ4_setStreamDecode(
|
||||
LZ4_stream: *mut LZ4StreamDecode,
|
||||
dictionary: *const u8,
|
||||
dict_size: c_int,
|
||||
) -> c_int;
|
||||
|
||||
// LZ4_streamDecode_t* LZ4_createStreamDecode(void)
|
||||
pub fn LZ4_createStreamDecode() -> *mut LZ4StreamDecode;
|
||||
|
||||
// int LZ4_decompress_safe_continue(LZ4_streamDecode_t* LZ4_streamDecode,
|
||||
// const char* source,
|
||||
// char* dest,
|
||||
// int compressedSize,
|
||||
// int maxDecompressedSize)
|
||||
pub fn LZ4_decompress_safe_continue(
|
||||
LZ4_stream: *mut LZ4StreamDecode,
|
||||
source: *const u8,
|
||||
dest: *mut u8,
|
||||
compressed_size: c_int,
|
||||
max_decompressed_size: c_int,
|
||||
) -> c_int;
|
||||
|
||||
// int LZ4_freeStreamDecode(LZ4_streamDecode_t* LZ4_stream)
|
||||
pub fn LZ4_freeStreamDecode(LZ4_stream: *mut LZ4StreamDecode) -> c_int;
|
||||
|
||||
// LZ4F_resetDecompressionContext()
|
||||
// In case of an error, the context is left in "undefined" state.
|
||||
// In which case, it's necessary to reset it, before re-using it.
|
||||
// This method can also be used to abruptly stop any unfinished decompression,
|
||||
// and start a new one using same context resources.
|
||||
pub fn LZ4F_resetDecompressionContext(ctx: LZ4FDecompressionContext);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_number() {
|
||||
unsafe {
|
||||
LZ4_versionNumber();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frame_info_size() {
|
||||
assert_eq!(core::mem::size_of::<LZ4FFrameInfo>(), 32);
|
||||
}
|
||||
123
native/src/external/lz4-sys/src/wasm_shim.rs
vendored
123
native/src/external/lz4-sys/src/wasm_shim.rs
vendored
@@ -1,123 +0,0 @@
|
||||
//! A shim for the libc functions used in lz4-rs that are not available when building for wasm
|
||||
//! targets. Adapted from the shim present in the [zstd](https://github.com/gyscos/zstd-rs) crate.
|
||||
//! zstd-rs license here:
|
||||
//! The MIT License (MIT)
|
||||
//! Copyright (c) 2016 Alexandre Bury
|
||||
//!
|
||||
//! Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
//! and associated documentation files (the "Software"), to deal in the Software without
|
||||
//! restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
//! distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
//! Software is furnished to do so, subject to the following conditions:
|
||||
//!
|
||||
//! The above copyright notice and this permission notice shall be included in all copies or
|
||||
//! substantial portions of the Software.
|
||||
//!
|
||||
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
//! BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
//! NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
//! DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
use alloc::alloc::{alloc, alloc_zeroed, dealloc, Layout};
|
||||
use core::ffi::{c_int, c_void};
|
||||
|
||||
const USIZE_ALIGN: usize = core::mem::align_of::<usize>();
|
||||
const USIZE_SIZE: usize = core::mem::size_of::<usize>();
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_lz4_wasm_shim_malloc(size: usize) -> *mut c_void {
|
||||
wasm_shim_alloc::<false>(size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_lz4_wasm_shim_memcmp(
|
||||
str1: *const c_void,
|
||||
str2: *const c_void,
|
||||
n: usize,
|
||||
) -> i32 {
|
||||
// Safety: function contracts requires str1 and str2 at least `n`-long.
|
||||
unsafe {
|
||||
let str1: &[u8] = core::slice::from_raw_parts(str1 as *const u8, n);
|
||||
let str2: &[u8] = core::slice::from_raw_parts(str2 as *const u8, n);
|
||||
match str1.cmp(str2) {
|
||||
core::cmp::Ordering::Less => -1,
|
||||
core::cmp::Ordering::Equal => 0,
|
||||
core::cmp::Ordering::Greater => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_lz4_wasm_shim_calloc(nmemb: usize, size: usize) -> *mut c_void {
|
||||
// note: calloc expects the allocation to be zeroed
|
||||
wasm_shim_alloc::<true>(nmemb * size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wasm_shim_alloc<const ZEROED: bool>(size: usize) -> *mut c_void {
|
||||
// in order to recover the size upon free, we store the size below the allocation
|
||||
// special alignment is never requested via the malloc API,
|
||||
// so it's not stored, and usize-alignment is used
|
||||
// memory layout: [size] [allocation]
|
||||
|
||||
let full_alloc_size = size + USIZE_SIZE;
|
||||
|
||||
unsafe {
|
||||
let layout = Layout::from_size_align_unchecked(full_alloc_size, USIZE_ALIGN);
|
||||
|
||||
let ptr = if ZEROED {
|
||||
alloc_zeroed(layout)
|
||||
} else {
|
||||
alloc(layout)
|
||||
};
|
||||
|
||||
// SAFETY: ptr is usize-aligned and we've allocated sufficient memory
|
||||
ptr.cast::<usize>().write(full_alloc_size);
|
||||
|
||||
ptr.add(USIZE_SIZE).cast()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_lz4_wasm_shim_free(ptr: *mut c_void) {
|
||||
// the layout for the allocation needs to be recovered for dealloc
|
||||
// - the size must be recovered from directly below the allocation
|
||||
// - the alignment will always by USIZE_ALIGN
|
||||
|
||||
let alloc_ptr = ptr.sub(USIZE_SIZE);
|
||||
// SAFETY: the allocation routines must uphold having a valid usize below the provided pointer
|
||||
let full_alloc_size = alloc_ptr.cast::<usize>().read();
|
||||
|
||||
let layout = Layout::from_size_align_unchecked(full_alloc_size, USIZE_ALIGN);
|
||||
dealloc(alloc_ptr.cast(), layout);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_lz4_wasm_shim_memcpy(
|
||||
dest: *mut c_void,
|
||||
src: *const c_void,
|
||||
n: usize,
|
||||
) -> *mut c_void {
|
||||
core::ptr::copy_nonoverlapping(src as *const u8, dest as *mut u8, n);
|
||||
dest
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_lz4_wasm_shim_memmove(
|
||||
dest: *mut c_void,
|
||||
src: *const c_void,
|
||||
n: usize,
|
||||
) -> *mut c_void {
|
||||
core::ptr::copy(src as *const u8, dest as *mut u8, n);
|
||||
dest
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_lz4_wasm_shim_memset(
|
||||
dest: *mut c_void,
|
||||
c: c_int,
|
||||
n: usize,
|
||||
) -> *mut c_void {
|
||||
core::ptr::write_bytes(dest as *mut u8, c as u8, n);
|
||||
dest
|
||||
}
|
||||
2
native/src/external/selinux
vendored
2
native/src/external/selinux
vendored
Submodule native/src/external/selinux updated: 3007b80e91...be1b39a657
@@ -7,6 +7,9 @@ edition.workspace = true
|
||||
crate-type = ["staticlib"]
|
||||
path = "lib.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cxx-gen = { workspace = true }
|
||||
|
||||
|
||||
@@ -143,6 +143,8 @@ void BootConfig::set(const kv_pairs &kv) noexcept {
|
||||
strscpy(hardware_plat.data(), value.data(), hardware_plat.size());
|
||||
} else if (key == "androidboot.fstab_suffix") {
|
||||
strscpy(fstab_suffix.data(), value.data(), fstab_suffix.size());
|
||||
} else if (key == "androidboot.mode") {
|
||||
strscpy(boot_mode.data(), value.data(), boot_mode.size());
|
||||
} else if (key == "qemu") {
|
||||
emulator = true;
|
||||
} else if (key == "androidboot.partition_map") {
|
||||
|
||||
@@ -9,6 +9,10 @@ impl BootConfig {
|
||||
debug!("force_normal_boot=[{}]", self.force_normal_boot);
|
||||
debug!("rootwait=[{}]", self.rootwait);
|
||||
unsafe {
|
||||
debug!(
|
||||
"boot_mode=[{}]",
|
||||
Utf8CStr::from_ptr_unchecked(self.boot_mode.as_ptr())
|
||||
);
|
||||
debug!(
|
||||
"slot=[{}]",
|
||||
Utf8CStr::from_ptr_unchecked(self.slot.as_ptr())
|
||||
@@ -43,7 +47,7 @@ impl MagiskInit {
|
||||
// Use the apex folder to determine whether 2SI (Android 10+)
|
||||
cstr!("/apex").exists() ||
|
||||
// If we still have no indication, parse the original init and see what's up
|
||||
MappedFile::open(backup_init())
|
||||
MappedFile::open(Some(cstr!("/init.real")).take_if(|p| p.exists()).unwrap_or(backup_init()))
|
||||
.map(|data| data.contains(b"selinux_setup"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <base.hpp>
|
||||
#include <sepolicy.hpp>
|
||||
|
||||
using kv_pairs = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
#include "init-rs.hpp"
|
||||
|
||||
int magisk_proxy_main(int, char *argv[]);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::ffi::{BootConfig, MagiskInit, backup_init, magisk_proxy_main};
|
||||
use crate::logging::setup_klog;
|
||||
use crate::mount::{is_rootfs, occupy, unoccupy};
|
||||
use crate::mount::is_rootfs;
|
||||
use crate::twostage::hexpatch_init_for_second_stage;
|
||||
use base::libc::{basename, getpid, mount, umask};
|
||||
use base::nix::mount::MsFlags;
|
||||
use base::{LibcReturn, LoggedResult, ResultExt, Utf8CStr, cstr, info, nix, raw_cstr};
|
||||
use base::{LibcReturn, LoggedResult, ResultExt, cstr, info, raw_cstr};
|
||||
use std::ffi::{CStr, c_char};
|
||||
use std::ptr::null;
|
||||
|
||||
@@ -25,6 +24,7 @@ impl MagiskInit {
|
||||
fstab_suffix: [0; 32],
|
||||
hardware: [0; 32],
|
||||
hardware_plat: [0; 32],
|
||||
boot_mode: [0; 16],
|
||||
partition_map: Vec::new(),
|
||||
},
|
||||
}
|
||||
@@ -32,7 +32,7 @@ impl MagiskInit {
|
||||
|
||||
fn first_stage(&self) {
|
||||
info!("First Stage Init");
|
||||
let rootfs_magisktmp = self.prepare_data(true);
|
||||
self.prepare_data();
|
||||
|
||||
if !cstr!("/sdcard").exists() && !cstr!("/first_stage_ramdisk/sdcard").exists() {
|
||||
self.hijack_init_with_switch_root();
|
||||
@@ -42,28 +42,11 @@ impl MagiskInit {
|
||||
// Fallback to hexpatch if /sdcard exists
|
||||
hexpatch_init_for_second_stage(true);
|
||||
}
|
||||
|
||||
if rootfs_magisktmp {
|
||||
info!("Occupy /data");
|
||||
occupy(cstr!("/data"));
|
||||
}
|
||||
}
|
||||
|
||||
fn second_stage(&mut self) {
|
||||
info!("Second Stage Init");
|
||||
|
||||
if unoccupy(cstr!("/data")) {
|
||||
nix::mount::mount(
|
||||
None::<&Utf8CStr>,
|
||||
cstr!("/data"),
|
||||
None::<&Utf8CStr>,
|
||||
MsFlags::MS_REMOUNT,
|
||||
Some(cstr!("size=100%")),
|
||||
)
|
||||
.check_os_err("mount", Some("/data"), Some("tmpfs"))
|
||||
.log_ok();
|
||||
}
|
||||
|
||||
cstr!("/init").unmount().ok();
|
||||
cstr!("/system/bin/init").unmount().ok(); // just in case
|
||||
cstr!("/data/init").remove().ok();
|
||||
@@ -89,7 +72,7 @@ impl MagiskInit {
|
||||
|
||||
fn legacy_system_as_root(&mut self) {
|
||||
info!("Legacy SAR Init");
|
||||
self.prepare_data(false);
|
||||
self.prepare_data();
|
||||
let is_two_stage = self.mount_system_root();
|
||||
if is_two_stage {
|
||||
hexpatch_init_for_second_stage(false);
|
||||
@@ -100,13 +83,13 @@ impl MagiskInit {
|
||||
|
||||
fn rootfs(&mut self) {
|
||||
info!("RootFS Init");
|
||||
self.prepare_data(false);
|
||||
self.prepare_data();
|
||||
self.restore_ramdisk_init();
|
||||
self.patch_rw_root();
|
||||
}
|
||||
|
||||
fn recovery(&self) {
|
||||
info!("Ramdisk is recovery, abort");
|
||||
fn recovery_or_charger(&self) {
|
||||
info!("Charger mode or ramdisk is recovery, abort");
|
||||
self.restore_ramdisk_init();
|
||||
cstr!("/.backup").remove_all().ok();
|
||||
}
|
||||
@@ -169,8 +152,11 @@ impl MagiskInit {
|
||||
self.legacy_system_as_root();
|
||||
} else if self.config.force_normal_boot {
|
||||
self.first_stage();
|
||||
} else if cstr!("/sbin/recovery").exists() || cstr!("/system/bin/recovery").exists() {
|
||||
self.recovery();
|
||||
} else if cstr!("/sbin/recovery").exists()
|
||||
|| cstr!("/system/bin/recovery").exists()
|
||||
|| unsafe { CStr::from_ptr(self.config.boot_mode.as_ptr()) } == c"charger"
|
||||
{
|
||||
self.recovery_or_charger();
|
||||
} else if self.check_two_stage() {
|
||||
self.first_stage();
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![feature(try_blocks)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use logging::setup_klog;
|
||||
@@ -35,6 +34,7 @@ pub mod ffi {
|
||||
fstab_suffix: [c_char; 32],
|
||||
hardware: [c_char; 32],
|
||||
hardware_plat: [c_char; 32],
|
||||
boot_mode: [c_char; 16],
|
||||
partition_map: Vec<KeyValue>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::ffi::MagiskInit;
|
||||
use base::WalkResult::{Continue, Skip};
|
||||
use base::nix::mount::{MntFlags, mount, umount2};
|
||||
use base::{
|
||||
Directory, FsPathBuilder, LibcReturn, LoggedResult, ResultExt, Utf8CStr, cstr, debug, libc,
|
||||
nix, parse_mount_info, raw_cstr,
|
||||
@@ -19,7 +17,7 @@ unsafe extern "C" {
|
||||
}
|
||||
|
||||
pub(crate) fn switch_root(path: &Utf8CStr) {
|
||||
let res: LoggedResult<()> = try {
|
||||
|| -> LoggedResult<()> {
|
||||
debug!("Switch root to {}", path);
|
||||
let mut mounts = BTreeSet::new();
|
||||
let rootfs = Directory::open(cstr!("/"))?;
|
||||
@@ -50,8 +48,9 @@ pub(crate) fn switch_root(path: &Utf8CStr) {
|
||||
|
||||
debug!("Cleaning rootfs");
|
||||
rootfs.remove_all()?;
|
||||
};
|
||||
res.ok();
|
||||
Ok(())
|
||||
}()
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub(crate) fn is_device_mounted(dev: u64, target: Pin<&mut CxxString>) -> bool {
|
||||
@@ -64,54 +63,6 @@ pub(crate) fn is_device_mounted(dev: u64, target: Pin<&mut CxxString>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn occupy(path: &Utf8CStr) {
|
||||
Directory::open(path)
|
||||
.map(|mut dir| {
|
||||
dir.pre_order_walk(|entry| {
|
||||
let mut path = cstr::buf::default();
|
||||
entry.resolve_path(&mut path)?;
|
||||
let path = path.as_utf8_cstr();
|
||||
mount(
|
||||
Some(path),
|
||||
path,
|
||||
None::<&Utf8CStr>,
|
||||
MsFlags::MS_BIND | MsFlags::MS_RDONLY,
|
||||
None::<&Utf8CStr>,
|
||||
)
|
||||
.check_os_err("occupy", Some(path), None)?;
|
||||
Ok(Continue)
|
||||
})
|
||||
.log_ok();
|
||||
})
|
||||
.log_ok();
|
||||
}
|
||||
|
||||
pub(crate) fn unoccupy(path: &Utf8CStr) -> bool {
|
||||
let mut ok = false;
|
||||
Directory::open(path)
|
||||
.map(|mut dir| {
|
||||
ok = dir
|
||||
.pre_order_walk(|entry| {
|
||||
let mut path = cstr::buf::default();
|
||||
entry.resolve_path(&mut path)?;
|
||||
let path = path.as_utf8_cstr();
|
||||
umount2(path, MntFlags::MNT_DETACH).check_os_err(
|
||||
"unoccupy",
|
||||
Some(path),
|
||||
None,
|
||||
)?;
|
||||
if entry.is_dir() {
|
||||
Ok(Skip)
|
||||
} else {
|
||||
Ok(Continue)
|
||||
}
|
||||
})
|
||||
.is_ok();
|
||||
})
|
||||
.log_ok();
|
||||
ok
|
||||
}
|
||||
|
||||
const RAMFS_MAGIC: u32 = 0x858458f6;
|
||||
|
||||
pub(crate) fn is_rootfs() -> bool {
|
||||
@@ -123,44 +74,22 @@ pub(crate) fn is_rootfs() -> bool {
|
||||
}
|
||||
|
||||
impl MagiskInit {
|
||||
pub(crate) fn prepare_data(&self, use_rootfs: bool) -> bool {
|
||||
pub(crate) fn prepare_data(&self) {
|
||||
debug!("Setup data tmp");
|
||||
cstr!("/data").mkdir(0o755).log_ok();
|
||||
nix::mount::mount(
|
||||
Some(cstr!("magisk")),
|
||||
cstr!("/data"),
|
||||
Some(cstr!("tmpfs")),
|
||||
MsFlags::empty(),
|
||||
Some(cstr!("mode=755")),
|
||||
)
|
||||
.check_os_err("mount", Some("/data"), Some("tmpfs"))
|
||||
.log_ok();
|
||||
|
||||
let mut rootfs_magisktmp = false;
|
||||
|
||||
if use_rootfs {
|
||||
cstr!("/magisk").mkdir(0o755).log_ok();
|
||||
rootfs_magisktmp = cstr!("/magisk")
|
||||
.bind_mount_to(cstr!("/data"), false)
|
||||
.is_ok();
|
||||
}
|
||||
|
||||
if rootfs_magisktmp {
|
||||
cstr!("/init")
|
||||
.rename_to(cstr!("/magisk/magiskinit"))
|
||||
.log_ok();
|
||||
cstr!("/.backup").copy_to(cstr!("/magisk/.backup")).ok();
|
||||
cstr!("/overlay.d")
|
||||
.rename_to(cstr!("/magisk/overlay.d"))
|
||||
.ok();
|
||||
} else {
|
||||
nix::mount::mount(
|
||||
Some(cstr!("magisk")),
|
||||
cstr!("/data"),
|
||||
Some(cstr!("tmpfs")),
|
||||
MsFlags::empty(),
|
||||
Some(cstr!("mode=755")),
|
||||
)
|
||||
.check_os_err("mount", Some("/data"), Some("tmpfs"))
|
||||
.log_ok();
|
||||
|
||||
cstr!("/init").copy_to(cstr!("/data/magiskinit")).ok();
|
||||
cstr!("/.backup").copy_to(cstr!("/data/.backup")).ok();
|
||||
cstr!("/overlay.d").copy_to(cstr!("/data/overlay.d")).ok();
|
||||
}
|
||||
|
||||
rootfs_magisktmp
|
||||
cstr!("/init").copy_to(cstr!("/data/magiskinit")).ok();
|
||||
cstr!("/.backup").copy_to(cstr!("/data/.backup")).ok();
|
||||
cstr!("/overlay.d").copy_to(cstr!("/data/overlay.d")).ok();
|
||||
}
|
||||
|
||||
pub(crate) fn exec_init(&mut self) {
|
||||
|
||||
@@ -28,7 +28,7 @@ pub(crate) fn hexpatch_init_for_second_stage(writable: bool) {
|
||||
// If we cannot directly modify /init, we need to bind mount a replacement on top of it
|
||||
let src = cstr!("/init");
|
||||
let dest = cstr!("/data/init");
|
||||
let _: LoggedResult<()> = try {
|
||||
let _ = || -> LoggedResult<()> {
|
||||
{
|
||||
let mut fd = dest.create(OFlag::O_CREAT | OFlag::O_WRONLY, 0)?;
|
||||
fd.write_all(init.as_ref())?;
|
||||
@@ -36,7 +36,8 @@ pub(crate) fn hexpatch_init_for_second_stage(writable: bool) {
|
||||
let attr = src.follow_link().get_attr()?;
|
||||
dest.set_attr(&attr)?;
|
||||
dest.bind_mount_to(src, false)?;
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +85,20 @@ impl MagiskInit {
|
||||
.log_ok();
|
||||
debug!("Symlink /storage/self/primary -> /system/system/bin/init");
|
||||
}
|
||||
cstr!("/init").rename_to(cstr!("/sdcard")).log_ok();
|
||||
|
||||
// Binding mounting from rootfs is not supported before Linux 3.12
|
||||
cstr!("/sdcard")
|
||||
.create(OFlag::O_RDONLY | OFlag::O_CLOEXEC, 0)
|
||||
.log_ok();
|
||||
cstr!("/data/magiskinit")
|
||||
// First try to mount magiskinit from rootfs to workaround Samsung RKP
|
||||
if cstr!("/sdcard")
|
||||
.bind_mount_to(cstr!("/sdcard"), false)
|
||||
.log_ok();
|
||||
debug!("Bind mount /data/magiskinit -> /sdcard");
|
||||
.is_ok()
|
||||
{
|
||||
debug!("Bind mount /sdcard -> /sdcard");
|
||||
} else {
|
||||
// Binding mounting from rootfs is not supported before Linux 3.12
|
||||
cstr!("/data/magiskinit")
|
||||
.bind_mount_to(cstr!("/sdcard"), false)
|
||||
.log_ok();
|
||||
debug!("Bind mount /data/magiskinit -> /sdcard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,15 @@ edition.workspace = true
|
||||
crate-type = ["staticlib", "rlib"]
|
||||
path = "lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
cxx-gen = { workspace = true }
|
||||
|
||||
[features]
|
||||
no-main = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cxx-gen = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
base = { workspace = true }
|
||||
cxx = { workspace = true }
|
||||
|
||||
@@ -79,12 +79,12 @@ pub unsafe extern "C" fn main(
|
||||
umask(0);
|
||||
}
|
||||
|
||||
let res: LoggedResult<()> = try {
|
||||
let res = || -> LoggedResult<()> {
|
||||
let cmds = CmdArgs::new(argc, argv);
|
||||
let cmds = cmds.as_slice();
|
||||
if argc < 2 {
|
||||
print_usage(cmds.first().unwrap_or(&"magiskpolicy"));
|
||||
return 1;
|
||||
return log_err!();
|
||||
}
|
||||
let cli = Cli::from_args(&[cmds[0]], &cmds[1..]).on_early_exit(|| print_usage(cmds[0]));
|
||||
|
||||
@@ -109,7 +109,7 @@ pub unsafe extern "C" fn main(
|
||||
log_err!("Cannot print rules with other options")?;
|
||||
}
|
||||
sepol.print_rules();
|
||||
return 0;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if cli.magisk {
|
||||
@@ -133,6 +133,7 @@ pub unsafe extern "C" fn main(
|
||||
{
|
||||
log_err!("Cannot dump policy to {}", file)?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if res.is_ok() { 0 } else { 1 }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![feature(try_blocks)]
|
||||
|
||||
pub use base;
|
||||
use std::fmt::Write;
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ impl SePolicy {
|
||||
allow(["kernel"], ["rootfs", "tmpfs"], ["chr_file"], ["write"]);
|
||||
|
||||
// Allow magiskinit daemon to handle mock selinuxfs
|
||||
allow(["kernel"], ["rootfs", "tmpfs"], ["fifo_file"], ["open", "read", "write"]);
|
||||
allow(["kernel"], ["tmpfs"], ["fifo_file"], ["open", "read", "write"]);
|
||||
|
||||
// For relabelling files
|
||||
allow(["rootfs"], ["labeledfs", "tmpfs"], ["filesystem"], ["associate"]);
|
||||
@@ -125,9 +125,6 @@ impl SePolicy {
|
||||
// Let init run stuffs
|
||||
allow(["init"], [proc], ["process"], all);
|
||||
|
||||
// For mounting loop devices, mirrors, tmpfs
|
||||
allow(["kernel"], ["fs_type", "dev_type", "file_type"], ["file"], ["read", "write"]);
|
||||
|
||||
// Zygisk rules
|
||||
allow(["zygote"], ["zygote"], ["process"], ["execmem"]);
|
||||
allow(["zygote"], ["fs_type"], ["filesystem"], ["unmount"]);
|
||||
|
||||
@@ -279,11 +279,12 @@ impl SePolicy {
|
||||
}
|
||||
|
||||
pub fn load_rule_file(&mut self, filename: &Utf8CStr) {
|
||||
let result: LoggedResult<()> = try {
|
||||
let result = || -> LoggedResult<()> {
|
||||
let file = filename.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
self.load_rules_from_reader(&mut reader);
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
result.ok();
|
||||
}
|
||||
|
||||
@@ -339,7 +340,7 @@ impl SePolicy {
|
||||
};
|
||||
match action {
|
||||
Token::AL | Token::DN | Token::AA | Token::DA => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let s = parse_sterm(tokens)?;
|
||||
let t = parse_sterm(tokens)?;
|
||||
let c = parse_sterm(tokens)?;
|
||||
@@ -352,13 +353,14 @@ impl SePolicy {
|
||||
Token::DA => self.dontaudit(s, t, c, p),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::AvtabAv(action))?
|
||||
}
|
||||
}
|
||||
Token::AX | Token::AY | Token::DX => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let s = parse_sterm(tokens)?;
|
||||
let t = parse_sterm(tokens)?;
|
||||
let c = parse_sterm(tokens)?;
|
||||
@@ -371,13 +373,14 @@ impl SePolicy {
|
||||
Token::DX => self.dontauditxperm(s, t, c, p),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::AvtabXperms(action))?
|
||||
}
|
||||
}
|
||||
Token::PM | Token::EF => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let t = parse_sterm(tokens)?;
|
||||
check_additional_args(tokens)?;
|
||||
match action {
|
||||
@@ -385,24 +388,26 @@ impl SePolicy {
|
||||
Token::EF => self.enforce(t),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::TypeState(action))?
|
||||
}
|
||||
}
|
||||
Token::TA => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let t = parse_term(tokens)?;
|
||||
let a = parse_term(tokens)?;
|
||||
check_additional_args(tokens)?;
|
||||
self.typeattribute(t, a)
|
||||
};
|
||||
self.typeattribute(t, a);
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::TypeAttr)?
|
||||
}
|
||||
}
|
||||
Token::TY => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let t = parse_id(tokens)?;
|
||||
let a = if tokens.peek().is_none() {
|
||||
vec![]
|
||||
@@ -410,24 +415,26 @@ impl SePolicy {
|
||||
parse_term(tokens)?
|
||||
};
|
||||
check_additional_args(tokens)?;
|
||||
self.type_(t, a)
|
||||
};
|
||||
self.type_(t, a);
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::NewType)?
|
||||
}
|
||||
}
|
||||
Token::AT => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let t = parse_id(tokens)?;
|
||||
check_additional_args(tokens)?;
|
||||
self.attribute(t)
|
||||
};
|
||||
self.attribute(t);
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::NewAttr)?
|
||||
}
|
||||
}
|
||||
Token::TC | Token::TM => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let s = parse_id(tokens)?;
|
||||
let t = parse_id(tokens)?;
|
||||
let c = parse_id(tokens)?;
|
||||
@@ -438,13 +445,14 @@ impl SePolicy {
|
||||
Token::TM => self.type_member(s, t, c, d),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::AvtabType(action))?
|
||||
}
|
||||
}
|
||||
Token::TT => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let s = parse_id(tokens)?;
|
||||
let t = parse_id(tokens)?;
|
||||
let c = parse_id(tokens)?;
|
||||
@@ -456,19 +464,21 @@ impl SePolicy {
|
||||
};
|
||||
check_additional_args(tokens)?;
|
||||
self.type_transition(s, t, c, d, o);
|
||||
};
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::TypeTrans)?
|
||||
}
|
||||
}
|
||||
Token::GF => {
|
||||
let result: ParseResult<()> = try {
|
||||
let result = || -> ParseResult<()> {
|
||||
let s = parse_id(tokens)?;
|
||||
let t = parse_id(tokens)?;
|
||||
let c = parse_id(tokens)?;
|
||||
check_additional_args(tokens)?;
|
||||
self.genfscon(s, t, c)
|
||||
};
|
||||
self.genfscon(s, t, c);
|
||||
Ok(())
|
||||
}();
|
||||
if result.is_err() {
|
||||
Err(ParseError::GenfsCon)?
|
||||
}
|
||||
|
||||
@@ -137,7 +137,9 @@ case "$1" in
|
||||
# Back up PREINITDEVICE from existing partition before OTA on A-only devices
|
||||
if ! $backuptool_ab; then
|
||||
initialize
|
||||
RECOVERYMODE=false
|
||||
# Suppress ui_print for this stage
|
||||
ui_print() { return; }
|
||||
get_flags
|
||||
find_boot_image
|
||||
$MAGISKBIN/magiskboot unpack "$BOOTIMAGE"
|
||||
$MAGISKBIN/magiskboot cpio ramdisk.cpio "extract .backup/.magisk config.orig"
|
||||
|
||||
@@ -32,17 +32,17 @@ case $(uname -m) in
|
||||
esac
|
||||
|
||||
cleanup() {
|
||||
pkill -INT -P $$
|
||||
wait
|
||||
trap - EXIT
|
||||
rm -f magisk_*.img
|
||||
"$avd" delete avd -n test
|
||||
exit 1
|
||||
}
|
||||
|
||||
test_error() {
|
||||
trap - EXIT
|
||||
print_error "! An error occurred"
|
||||
pkill -INT -P $$
|
||||
wait
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_for_boot() {
|
||||
@@ -72,13 +72,14 @@ wait_emu() {
|
||||
|
||||
dump_vars() {
|
||||
local val
|
||||
for name in $@; do
|
||||
for name in $@ emu_args; do
|
||||
eval val=\$$name
|
||||
echo $name=\"$val\"\;
|
||||
done
|
||||
}
|
||||
|
||||
resolve_vars() {
|
||||
set +x
|
||||
local arg_list="$1"
|
||||
local ver=$2
|
||||
local type=$3
|
||||
@@ -91,6 +92,7 @@ resolve_vars() {
|
||||
UpsideDownCakePrivacySandbox) api=34 ;;
|
||||
VanillaIceCream) api=35 ;;
|
||||
Baklava) api=36 ;;
|
||||
CinnamonBun) api=37 ;;
|
||||
*CANARY) api=10000 ;;
|
||||
*)
|
||||
print_error "! Unknown system image version '$ver'"
|
||||
@@ -138,8 +140,14 @@ dl_emu() {
|
||||
|
||||
setup_emu() {
|
||||
local avd_pkg=$1
|
||||
local ver=$2
|
||||
dl_emu $avd_pkg
|
||||
echo no | "$avd" create avd -f -n test -k $avd_pkg
|
||||
|
||||
# avdmanager is outdated, it might not set the proper target
|
||||
local ini=$ANDROID_AVD_HOME/test.ini
|
||||
sed "s:^target\s*=.*:target=android-$ver:g" $ini > $ini.new
|
||||
mv $ini.new $ini
|
||||
}
|
||||
|
||||
test_emu() {
|
||||
@@ -169,16 +177,15 @@ test_emu() {
|
||||
}
|
||||
|
||||
test_main() {
|
||||
local avd_pkg ramdisk vars
|
||||
vars=$(resolve_vars "emu_args avd_pkg ramdisk" $1 $2)
|
||||
eval $vars
|
||||
local ver avd_pkg ramdisk
|
||||
eval $(resolve_vars "ver avd_pkg ramdisk" $1 $2)
|
||||
|
||||
# Specify an explicit port so that tests can run with other emulators running at the same time
|
||||
local emu_port=5682
|
||||
emu_args="$emu_args -port $emu_port"
|
||||
export ANDROID_SERIAL="emulator-$emu_port"
|
||||
|
||||
setup_emu "$avd_pkg"
|
||||
setup_emu "$avd_pkg" $ver
|
||||
|
||||
# Restart ADB daemon just in case
|
||||
adb kill-server
|
||||
@@ -211,24 +218,21 @@ test_main() {
|
||||
test_emu release
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -f magisk_*.img
|
||||
"$avd" delete avd -n test
|
||||
cleanup
|
||||
}
|
||||
|
||||
run_main() {
|
||||
local avd_pkg vars
|
||||
vars=$(resolve_vars "emu_args avd_pkg" $1 $2)
|
||||
eval $vars
|
||||
setup_emu "$avd_pkg"
|
||||
local ver avd_pkg
|
||||
eval $(resolve_vars "ver avd_pkg" $1 $2)
|
||||
setup_emu "$avd_pkg" $ver
|
||||
print_title "* Launching $avd_pkg"
|
||||
"$emu" @test $emu_args 2>/dev/null
|
||||
cleanup
|
||||
}
|
||||
|
||||
dl_main() {
|
||||
local avd_pkg vars
|
||||
vars=$(resolve_vars "avd_pkg" $1 $2)
|
||||
eval $vars
|
||||
local avd_pkg
|
||||
eval $(resolve_vars "avd_pkg" $1 $2)
|
||||
print_title "* Downloading $avd_pkg"
|
||||
dl_emu "$avd_pkg"
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ setup_env() {
|
||||
sudo apt-get update
|
||||
sudo dpkg -i ./cuttlefish-base_*_*64.deb || sudo apt-get install -f
|
||||
rm cuttlefish-base_*_*64.deb
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
sudo usermod -aG kvm,cvdnetwork,render $USER
|
||||
yes | "$sdk" --licenses > /dev/null
|
||||
"$sdk" --channel=3 platform-tools
|
||||
|
||||
@@ -36,25 +36,6 @@ disable_version_config() {
|
||||
sed -i "s:^version=:# version=:g" $CONFIG
|
||||
}
|
||||
|
||||
bump_canary_version() {
|
||||
# Update version code
|
||||
local code=$(grep_prop magisk.versionCode $GCONFIG)
|
||||
code=$((code + 1))
|
||||
local tag="canary-$code"
|
||||
sed -i "s:versionCode=.*:versionCode=${code}:g" $GCONFIG
|
||||
|
||||
# Commit version code changes
|
||||
git add -u .
|
||||
git status
|
||||
git commit -m "Release new canary build" -m "[skip ci]"
|
||||
git tag $tag
|
||||
|
||||
# Update version name
|
||||
local ver=$(git rev-parse --short=8 HEAD)
|
||||
sed -i "s:version=.*:version=${ver}:g" $CONFIG
|
||||
sed -i "1s:.*:## Magisk (${ver}) (${code}):" $NOTES
|
||||
}
|
||||
|
||||
# $1 = ver
|
||||
set_version() {
|
||||
local ver=$1
|
||||
@@ -69,89 +50,45 @@ set_version() {
|
||||
git add -u .
|
||||
git status
|
||||
git commit -m "Release Magisk v$ver" -m "[skip ci]"
|
||||
git tag $tag
|
||||
}
|
||||
|
||||
build_apk() {
|
||||
# $1 = ver
|
||||
build() {
|
||||
[ -z $1 ] && exit 1
|
||||
local ver=$1
|
||||
git pull
|
||||
set_version $ver
|
||||
$BUILDCMD clean
|
||||
$BUILDCMD all
|
||||
$BUILDCMD -r all
|
||||
}
|
||||
|
||||
build_canary() {
|
||||
bump_canary_version
|
||||
build_apk
|
||||
}
|
||||
|
||||
# $1 = ver
|
||||
build_public() {
|
||||
[ -z $1 ] && exit 1
|
||||
local ver=$1
|
||||
set_version $ver
|
||||
build_apk
|
||||
}
|
||||
|
||||
upload() {
|
||||
# Verify pattern
|
||||
[[ "$1" =~ canary|beta|stable ]]
|
||||
local type=$1
|
||||
|
||||
gh auth status
|
||||
|
||||
local latest_tag=$(git describe --abbrev=0 --tags)
|
||||
local ver=$(grep_prop version $CONFIG)
|
||||
local code=$(grep_prop magisk.versionCode $GCONFIG)
|
||||
local out=$(grep_prop outdir $CONFIG)
|
||||
local tag title
|
||||
local ver=$(echo - | awk "{ print $code / 1000 }")
|
||||
local tag="v$ver"
|
||||
local title="Magisk v$ver"
|
||||
|
||||
local out=$(grep_prop outdir $CONFIG)
|
||||
if [ -z $out ]; then
|
||||
out=out
|
||||
fi
|
||||
|
||||
git tag $tag
|
||||
git push origin master
|
||||
git push --tags
|
||||
|
||||
# Prepare release notes
|
||||
tail -n +3 $NOTES > release.md
|
||||
|
||||
case $type in
|
||||
canary )
|
||||
tag="canary-$code"
|
||||
title="Magisk ($ver) ($code)"
|
||||
# Publish release
|
||||
local release_apk="Magisk-v${ver}.apk"
|
||||
cp $out/app-release.apk $release_apk
|
||||
gh release create --verify-tag $tag -p -t "$title" -F release.md $release_apk $out/app-debug.apk $NOTES
|
||||
|
||||
# Assert tag format
|
||||
[ $latest_tag = $tag ]
|
||||
|
||||
# Publish release
|
||||
gh release create --verify-tag $tag -p -t "$title" -F release.md $out/app-release.apk $out/app-debug.apk $NOTES
|
||||
;;
|
||||
beta|stable )
|
||||
tag="v$ver"
|
||||
title="Magisk v$ver"
|
||||
|
||||
# Assert tag format
|
||||
[ $latest_tag = $tag ]
|
||||
|
||||
# Publish release
|
||||
local release_apk="Magisk-v${ver}.apk"
|
||||
cp $out/app-release.apk $release_apk
|
||||
gh release create --verify-tag $tag -p -t "$title" -F release.md $release_apk $out/app-debug.apk $NOTES
|
||||
rm -f $release_apk
|
||||
;;
|
||||
esac
|
||||
|
||||
# If publishing stable, make it not prerelease and explicitly latest
|
||||
if [ $type = "stable" ]; then
|
||||
gh release edit $tag --prerelease=false --latest
|
||||
fi
|
||||
|
||||
rm -f release.md
|
||||
}
|
||||
|
||||
revert() {
|
||||
local latest_tag=$(git describe --abbrev=0 --tags)
|
||||
git tag -d $latest_tag
|
||||
git reset --hard HEAD~
|
||||
rm -f $release_apk release.md
|
||||
}
|
||||
|
||||
# Use GNU sed on macOS
|
||||
@@ -160,14 +97,10 @@ if command -v gsed >/dev/null; then
|
||||
export -f sed
|
||||
fi
|
||||
|
||||
git pull
|
||||
|
||||
trap disable_version_config EXIT
|
||||
ensure_config
|
||||
case $1 in
|
||||
canary ) build_canary ;;
|
||||
public ) build_public $2 ;;
|
||||
upload ) upload $2 ;;
|
||||
revert ) revert ;;
|
||||
build ) build $2 ;;
|
||||
upload ) upload ;;
|
||||
* ) exit 1 ;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user