mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-30 13:35:27 +00:00
Merge branch 'master' into feature/redesign
This commit is contained in:
commit
40c64d50d5
@ -74,7 +74,7 @@ dependencies {
|
||||
implementation "${bindingAdapter}:${vBAdapt}"
|
||||
implementation "${bindingAdapter}-recyclerview:${vBAdapt}"
|
||||
|
||||
def vMarkwon = '4.1.2'
|
||||
def vMarkwon = '4.2.0'
|
||||
implementation "io.noties.markwon:core:${vMarkwon}"
|
||||
implementation "io.noties.markwon:html:${vMarkwon}"
|
||||
implementation "io.noties.markwon:image:${vMarkwon}"
|
||||
@ -99,7 +99,7 @@ dependencies {
|
||||
implementation "com.squareup.okhttp3:okhttp:${vOkHttp}"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}"
|
||||
|
||||
def vMoshi = '1.9.1'
|
||||
def vMoshi = '1.9.2'
|
||||
implementation "com.squareup.moshi:moshi:${vMoshi}"
|
||||
|
||||
def vKotshi = '2.0.2'
|
||||
@ -111,7 +111,7 @@ dependencies {
|
||||
replacedBy('com.github.topjohnwu:room-runtime')
|
||||
}
|
||||
}
|
||||
def vRoom = '2.2.1'
|
||||
def vRoom = '2.2.2'
|
||||
implementation "com.github.topjohnwu:room-runtime:${vRoom}"
|
||||
implementation "androidx.room:room-rxjava2:${vRoom}"
|
||||
kapt "androidx.room:room-compiler:${vRoom}"
|
||||
@ -125,13 +125,12 @@ dependencies {
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03'
|
||||
implementation 'androidx.browser:browser:1.0.0'
|
||||
implementation 'androidx.preference:preference:1.1.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc03'
|
||||
implementation 'androidx.work:work-runtime:2.2.0'
|
||||
implementation 'androidx.transition:transition:1.2.0'
|
||||
implementation 'androidx.transition:transition:1.3.0-rc02'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.core:core-ktx:1.1.0'
|
||||
implementation 'androidx.biometric:biometric:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.1.0-beta01'
|
||||
implementation 'com.google.android.material:material:1.2.0-alpha01'
|
||||
implementation 'com.karumi:dexter:6.0.0'
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ object Config : PreferenceModel, DBConfig {
|
||||
const val REDESIGN = "redesign"
|
||||
const val SAFETY = "safety_notice"
|
||||
const val THEME_ORDINAL = "theme_ordinal"
|
||||
const val BOOT_ID = "boot_id"
|
||||
|
||||
// system state
|
||||
const val MAGISKHIDE = "magiskhide"
|
||||
@ -111,6 +112,8 @@ object Config : PreferenceModel, DBConfig {
|
||||
}
|
||||
else Value.DEFAULT_CHANNEL
|
||||
|
||||
var bootId by preference(Key.BOOT_ID, "")
|
||||
|
||||
var downloadPath by preference(Key.DOWNLOAD_PATH, Environment.DIRECTORY_DOWNLOADS)
|
||||
var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE)
|
||||
|
||||
|
@ -24,9 +24,6 @@ import com.topjohnwu.magisk.ui.flash.FlashActivity
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||
import com.topjohnwu.magisk.utils.refreshLocale
|
||||
import com.topjohnwu.magisk.utils.updateConfig
|
||||
import com.topjohnwu.magisk.utils.currentLocale
|
||||
import com.topjohnwu.magisk.utils.defaultLocale
|
||||
import java.util.*
|
||||
import com.topjohnwu.magisk.redesign.MainActivity as RedesignActivity
|
||||
|
||||
fun AssetManager.addAssetPath(path: String) {
|
||||
|
@ -8,6 +8,8 @@ import com.topjohnwu.magisk.utils.CachedValue
|
||||
import com.topjohnwu.magisk.utils.KObservableField
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
|
||||
val isRunningAsStub get() = Info.stub != null
|
||||
|
||||
@ -36,6 +38,22 @@ object Info {
|
||||
}
|
||||
}
|
||||
|
||||
val isNewReboot by lazy {
|
||||
try {
|
||||
FileInputStream("/proc/sys/kernel/random/boot_id").bufferedReader().use {
|
||||
val id = it.readLine()
|
||||
if (id != Config.bootId) {
|
||||
Config.bootId = id
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadState() = runCatching {
|
||||
val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0]
|
||||
val code = ShellUtils.fastCmd("magisk -V").toInt()
|
||||
|
@ -3,8 +3,8 @@ package com.topjohnwu.magisk.data.database
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Delete
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Delete
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Replace
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Select
|
||||
import com.topjohnwu.magisk.extensions.now
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Delete
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Delete
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Replace
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Select
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.topjohnwu.magisk.data.database
|
||||
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Delete
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Delete
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Replace
|
||||
import com.topjohnwu.magisk.data.database.magiskdb.Select
|
||||
|
||||
|
@ -33,8 +33,8 @@ import com.topjohnwu.magisk.FileProvider
|
||||
import com.topjohnwu.magisk.utils.DynamicClassLoader
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.currentLocale
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.topjohnwu.magisk.extensions
|
||||
|
||||
import android.os.Build
|
||||
import androidx.core.net.toFile
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
@ -3,7 +3,6 @@ package com.topjohnwu.magisk.model.download
|
||||
import android.app.Activity
|
||||
import android.app.Notification
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.topjohnwu.magisk.R
|
||||
|
@ -13,17 +13,21 @@ class Module(path: String) : BaseModule() {
|
||||
override var versionCode: Int = -1
|
||||
override var description: String = ""
|
||||
|
||||
private val removeFile: SuFile = SuFile(path, "remove")
|
||||
private val disableFile: SuFile = SuFile(path, "disable")
|
||||
private val updateFile: SuFile = SuFile(path, "update")
|
||||
private val removeFile = SuFile(path, "remove")
|
||||
private val disableFile = SuFile(path, "disable")
|
||||
private val updateFile = SuFile(path, "update")
|
||||
private val ruleFile = SuFile(path, "sepolicy.rule")
|
||||
|
||||
val updated: Boolean = updateFile.exists()
|
||||
|
||||
var enable: Boolean = !disableFile.exists()
|
||||
set(enable) {
|
||||
val dir = "$PERSIST/$id"
|
||||
field = if (enable) {
|
||||
Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit()
|
||||
disableFile.delete()
|
||||
} else {
|
||||
Shell.su("rm -rf $dir").submit()
|
||||
!disableFile.createNewFile()
|
||||
}
|
||||
}
|
||||
@ -31,8 +35,10 @@ class Module(path: String) : BaseModule() {
|
||||
var remove: Boolean = removeFile.exists()
|
||||
set(remove) {
|
||||
field = if (remove) {
|
||||
Shell.su("rm -rf $PERSIST/$id").submit()
|
||||
removeFile.createNewFile()
|
||||
} else {
|
||||
Shell.su("cp -af $ruleFile $PERSIST/$id").submit()
|
||||
!removeFile.delete()
|
||||
}
|
||||
}
|
||||
@ -54,6 +60,8 @@ class Module(path: String) : BaseModule() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val PERSIST = "/sbin/.magisk/mirror/persist/magisk"
|
||||
|
||||
@WorkerThread
|
||||
fun loadModules(): List<Module> {
|
||||
val moduleList = mutableListOf<Module>()
|
||||
|
@ -287,8 +287,10 @@ abstract class MagiskInstaller {
|
||||
protected fun flashBoot(): Boolean {
|
||||
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
|
||||
return false
|
||||
if (!Info.keepVerity)
|
||||
"patch_dtbo_image".sh()
|
||||
arrayOf(
|
||||
"(KEEPVERITY=${Info.keepVerity} patch_dtb_partitions)",
|
||||
"run_migrations"
|
||||
).sh()
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -3,18 +3,16 @@ package com.topjohnwu.magisk.ui
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.topjohnwu.magisk.*
|
||||
import com.topjohnwu.magisk.model.navigation.Navigation
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.intent
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.model.navigation.Navigation
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.magisk.view.Shortcuts
|
||||
import com.topjohnwu.magisk.wrap
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
|
||||
open class SplashActivity : Activity() {
|
||||
|
||||
@ -41,6 +39,10 @@ open class SplashActivity : Activity() {
|
||||
}
|
||||
}
|
||||
|
||||
Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
|
||||
Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
|
||||
Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
|
||||
|
||||
// Set default configs
|
||||
Config.initialize()
|
||||
|
||||
@ -53,9 +55,13 @@ open class SplashActivity : Activity() {
|
||||
// Setup shortcuts
|
||||
Shortcuts.setup(this)
|
||||
|
||||
Shell.su("mm_patch_dtbo").submit {
|
||||
if (Info.isNewReboot) {
|
||||
val shell = Shell.newInstance()
|
||||
shell.newJob().add("mm_patch_dtb").submit {
|
||||
if (it.isSuccess)
|
||||
Notifications.dtboPatched(this)
|
||||
shell.close()
|
||||
}
|
||||
}
|
||||
|
||||
DONE = true
|
||||
|
@ -2,12 +2,10 @@ package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.content.Context
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.Info
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.extensions.rawResource
|
||||
import com.topjohnwu.magisk.wrap
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
|
||||
class RootInit : Shell.Initializer() {
|
||||
@ -17,9 +15,6 @@ class RootInit : Shell.Initializer() {
|
||||
}
|
||||
|
||||
fun init(context: Context, shell: Shell): Boolean {
|
||||
// Invalidate env state if shell is recreated
|
||||
Info.envRef.invalidate()
|
||||
|
||||
val job = shell.newJob()
|
||||
if (shell.isRoot) {
|
||||
job.add(context.rawResource(R.raw.util_functions))
|
||||
@ -29,15 +24,12 @@ class RootInit : Shell.Initializer() {
|
||||
job.add(context.rawResource(R.raw.nonroot_utils))
|
||||
}
|
||||
|
||||
job.add("mount_partitions",
|
||||
job.add(
|
||||
"mount_partitions",
|
||||
"get_flags",
|
||||
"run_migrations",
|
||||
"export BOOTMODE=true")
|
||||
.exec()
|
||||
|
||||
Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
|
||||
Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
|
||||
Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
|
||||
"export BOOTMODE=true"
|
||||
).exec()
|
||||
|
||||
return true
|
||||
}
|
||||
|
Binary file not shown.
@ -59,6 +59,8 @@
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_margin="@dimen/fab_padding"
|
||||
android:onClick="@{() -> viewModel.fabPressed()}"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
app:fabSize="normal"
|
||||
app:layout_behavior="com.google.android.material.floatingactionbutton.FloatingActionButton$Behavior"
|
||||
app:srcCompat="@drawable/ic_add" />
|
||||
|
@ -16,6 +16,7 @@
|
||||
</data>
|
||||
|
||||
<TextView
|
||||
android:focusable="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
|
@ -94,6 +94,8 @@
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/hide_app_checkbox"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
style="@style/Widget.Icon"
|
||||
isChecked="@{item.isHiddenState}"
|
||||
android:onClick="@{() -> item.toggle()}"
|
||||
|
@ -38,6 +38,8 @@
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/hide_process_icon"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
style="@style/Widget.Icon"
|
||||
isChecked="@{item.isHidden}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
@ -106,6 +106,8 @@
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/checkbox"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
style="@style/Widget.Icon"
|
||||
isChecked="@{item.isChecked}"
|
||||
android:layout_marginEnd="@dimen/margin_generic"
|
||||
@ -127,6 +129,8 @@
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/delete"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
style="@style/Widget.Icon"
|
||||
srcCompat="@{item.isDeletable ? R.drawable.ic_undelete : R.drawable.ic_delete}"
|
||||
android:onClick="@{() -> item.toggleDelete()}"
|
||||
|
@ -29,6 +29,7 @@
|
||||
items="@{item.items}"
|
||||
scrollPosition="@={viewModel.scrollPosition}"
|
||||
scrollPositionSmooth="@{true}"
|
||||
android:focusable="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@ -39,6 +40,8 @@
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
hide="@{viewModel.scrollPosition == item.items.size - 1 || item.items.size == 0}"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
|
@ -105,6 +105,8 @@
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/download"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
style="@style/Widget.Icon"
|
||||
isEnabled="@{viewModel.isConnected}"
|
||||
android:alpha="@{viewModel.isConnected ? 1f : .2f}"
|
||||
|
@ -1,5 +1,4 @@
|
||||
# v7.4.0
|
||||
- Hide Magisk Manager with stub APKs on Android 9.0+. Not all devices will be supported, please refer to Magisk v20.1 release notes.
|
||||
- Allow customizing app name when hiding Magisk Manager
|
||||
- Generate random keys to sign the hidden Magisk Manager to prevent signature detections
|
||||
- Fix fingerprint UI infinite loop
|
||||
# v7.5.0
|
||||
- Support new communication method (ContentProvider)
|
||||
- Fix several issues with hidden stub APK
|
||||
- Support using BiometricPrompt (face unlock)
|
||||
|
@ -1,6 +1,7 @@
|
||||
mount_partitions() {
|
||||
[ "`getprop ro.build.ab_update`" = "true" ] && SLOT=`getprop ro.boot.slot_suffix`
|
||||
[ "`getprop ro.build.system_root_image`" = "true" ] && SYSTEM_ROOT=true || SYSTEM_ROOT=false
|
||||
# Check whether non rootfs root dir exists
|
||||
grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true || SYSTEM_ROOT=false
|
||||
}
|
||||
|
||||
get_flags() {
|
||||
|
@ -17,16 +17,6 @@ fix_env() {
|
||||
cd /
|
||||
}
|
||||
|
||||
run_migrations() {
|
||||
# Move the stock backups
|
||||
if [ -f /data/magisk/stock_boot* ]; then
|
||||
mv /data/magisk/stock_boot* /data 2>/dev/null
|
||||
fi
|
||||
if [ -f /data/adb/magisk/stock_boot* ]; then
|
||||
mv /data/adb/magisk/stock_boot* /data 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
direct_install() {
|
||||
rm -rf $MAGISKBIN/* 2>/dev/null
|
||||
mkdir -p $MAGISKBIN 2>/dev/null
|
||||
@ -43,30 +33,43 @@ direct_install() {
|
||||
return 0
|
||||
}
|
||||
|
||||
mm_patch_dtbo() {
|
||||
$KEEPVERITY && return 1 || patch_dtbo_image
|
||||
mm_patch_dtb() {
|
||||
local result=1
|
||||
local PATCHED=$TMPDIR/dt.patched
|
||||
for name in dtb dtbo; do
|
||||
local IMAGE=`find_block $name$SLOT`
|
||||
if [ ! -z $IMAGE ]; then
|
||||
if $MAGISKBIN/magiskboot dtb $IMAGE patch $PATCHED; then
|
||||
result=0
|
||||
if [ ! -z $SHA1 ]; then
|
||||
# Backup stuffs
|
||||
mkdir /data/magisk_backup_${SHA1} 2>/dev/null
|
||||
cat $IMAGE | gzip -9 > /data/magisk_backup_${SHA1}/${name}.img.gz
|
||||
fi
|
||||
cat $PATCHED /dev/zero > $IMAGE
|
||||
rm -f $PATCHED
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return $result
|
||||
}
|
||||
|
||||
restore_imgs() {
|
||||
local SHA1=`grep_prop SHA1 /sbin/.magisk/config`
|
||||
[ -z $SHA1 ] && local SHA1=`cat /.backup/.sha1`
|
||||
[ -z $SHA1 ] && return 1
|
||||
local STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
|
||||
local STOCKDTBO=/data/stock_dtbo.img.gz
|
||||
[ -f $STOCKBOOT ] || return 1
|
||||
local BACKUPDIR=/data/magisk_backup_$SHA1
|
||||
[ -d $BACKUPDIR ] || return 1
|
||||
|
||||
get_flags
|
||||
find_boot_image
|
||||
find_dtbo_image
|
||||
|
||||
if [ -f $STOCKDTBO -a -b "$DTBOIMAGE" ]; then
|
||||
flash_image $STOCKDTBO $DTBOIMAGE
|
||||
fi
|
||||
if [ -f $STOCKBOOT -a -b "$BOOTIMAGE" ]; then
|
||||
flash_image $STOCKBOOT $BOOTIMAGE
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
for name in dtb dtbo; do
|
||||
[ -f $BACKUPDIR/${name}.img.gz ] || continue
|
||||
local IMAGE=`find_block $name$SLOT`
|
||||
[ -z $IMAGE ] && continue
|
||||
flash_image $BACKUPDIR/${name}.img.gz $IMAGE
|
||||
done
|
||||
[ -f $BACKUPDIR/boot.img.gz ] || return 1
|
||||
flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE
|
||||
}
|
||||
|
||||
post_ota() {
|
||||
@ -119,3 +122,5 @@ force_pm_install() {
|
||||
[ "$VERIFY" -eq 1 ] && settings put global package_verifier_enable 1
|
||||
return $res
|
||||
}
|
||||
|
||||
SHA1=`grep_prop SHA1 /sbin/.magisk/config`
|
||||
|
@ -80,10 +80,10 @@
|
||||
<string name="direct_install">Instal·lació directa (Recomanat)</string>
|
||||
<string name="install_inactive_slot">Instal·la a la ranura inactiva (Després d\'una OTA)</string>
|
||||
<string name="install_inactive_slot_msg">El teu dispositiu serà FORÇAT a arrancar en l\'actual ranura inactiva després del reinici!\nUtilitza aquesta opció NOMÉS quan l\'OTA s\'hagi fet.\nContinuar?</string>
|
||||
<string name="select_method">Sel·lecciona un mètode</string>
|
||||
<string name="select_method">Selecciona un mètode</string>
|
||||
<string name="setup_title">Instal·lació addicional</string>
|
||||
<string name="select_patch_file">Sel·lecciona i arranja un arxiu</string>
|
||||
<string name="patch_file_msg">Sel·lecciona una imatge crua (*.img) o un ODIN tarfile (*.tar)</string>
|
||||
<string name="select_patch_file">Selecciona i arranja un arxiu</string>
|
||||
<string name="patch_file_msg">Selecciona una imatge crua (*.img) o un ODIN tarfile (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Reinici en 5 segons…</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
@ -94,7 +94,6 @@
|
||||
<string name="settings_reboot_toast">Reinicia per aplicar els canvis</string>
|
||||
<string name="release_notes">Notes de llançament</string>
|
||||
<string name="repo_cache_cleared">Memòria cau del repositori netejada</string>
|
||||
|
||||
<string name="dtbo_patched_title">S\'ha arranjat DTBO</string>
|
||||
<string name="dtbo_patched_reboot">Magisk Manager ha arranjat dtbo.img. Si us plau, reinicia el telèfon.</string>
|
||||
<string name="flashing">Arranjament…</string>
|
||||
@ -110,18 +109,19 @@
|
||||
<string name="restore_done">Restauració feta!</string>
|
||||
<string name="restore_fail">La còpia de seguretat de Stock no existeix!</string>
|
||||
<string name="proprietary_title">Baixar codi propietari</string>
|
||||
<string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nPot permetre que Magisk Manager baixi una extensió que conté el GoogleApiClient per poder fer la comprobació de SafetyNet?</string>
|
||||
<string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nVot permetre que Magisk Manager baixi una extensió que conté el GoogleApiClient per poder fer la comprovació de SafetyNet?</string>
|
||||
<string name="setup_fail">Instal·lació fallida.</string>
|
||||
<string name="env_fix_title">Es requereix instal·lació addicional</string>
|
||||
<string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instalació ara?</string>
|
||||
<string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instal·lació ara?</string>
|
||||
<string name="setup_msg">S\'està executant la configuració de l\'entorn…</string>
|
||||
<string name="authenticate">Autenticar</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">General</string>
|
||||
<string name="settings_dark_theme_title">Tema fosc</string>
|
||||
<string name="settings_dark_theme_summary">Habilitar el tema fosc</string>
|
||||
<string name="settings_download_path_title">Directori de baixades</string>
|
||||
<string name="settings_download_path_message">Els arxius es desaràn a %1$s</string>
|
||||
<string name="settings_download_path_message">Els arxius es desaran a %1$s</string>
|
||||
<string name="settings_clear_cache_title">Netejar memòria cau del repositori</string>
|
||||
<string name="settings_clear_cache_summary">Neteja l\'informació en memòria cau per als repositoris en línia, força a l\'aplicació a actualitzar-se en línia.</string>
|
||||
<string name="settings_hide_manager_title">Amagar Magisk Manager</string>
|
||||
@ -139,7 +139,7 @@
|
||||
<string name="settings_update_custom">Personalitzat</string>
|
||||
<string name="settings_update_custom_msg">Inserta una URL personalitzada</string>
|
||||
<string name="settings_core_only_title">Mode nucli de Magisk</string>
|
||||
<string name="settings_core_only_summary">Habilitar només les funcions principals, no es carregaran tots els mòduls. MagiskSU y MagiskHide seguirán habilitats</string>
|
||||
<string name="settings_core_only_summary">Habilitar només les funcions principals, no es carregaran tots els mòduls. MagiskSU y MagiskHide seguiran habilitats</string>
|
||||
<string name="settings_magiskhide_summary">Amagar Magisk de varies deteccions</string>
|
||||
<string name="settings_hosts_title">Systemless Hosts</string>
|
||||
<string name="settings_hosts_summary">Suport per aplicacions tipus Adblock fora de la partició del sistema</string>
|
||||
@ -148,7 +148,7 @@
|
||||
<string name="settings_app_name">Escriu el nom desitjat per l\'App</string>
|
||||
<string name="settings_app_name_hint">Nou nom</string>
|
||||
<string name="settings_app_name_helper">Es refarà l\'App amb aquest nom</string>
|
||||
<string name="settings_app_name_error">Format invàl·lid</string>
|
||||
<string name="settings_app_name_error">Format invàlid</string>
|
||||
<string name="settings_su_app_adb">Aplicacions y ADB</string>
|
||||
<string name="settings_su_app">Només aplicacions</string>
|
||||
<string name="settings_su_adb">Només ADB</string>
|
||||
@ -166,6 +166,9 @@
|
||||
<string name="request_timeout_summary">%1$d segons</string>
|
||||
<string name="settings_su_reauth_title">Demanar després d\'una actualització</string>
|
||||
<string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada</string>
|
||||
<string name="settings_su_biometric_title">Activar autenticació biomètrica</string>
|
||||
<string name="settings_su_biometric_summary">Utilitza l\'autenticació biomètrica per permetre solicituds de superusuari</string>
|
||||
<string name="no_biometric">El dispositiu no suporta o no té establerta configuració biomètrica</string>
|
||||
|
||||
<string name="multiuser_mode">Mode Multiusuari</string>
|
||||
<string name="settings_owner_only">Només Administrador del Dispositiu</string>
|
||||
@ -182,7 +185,7 @@
|
||||
<string name="global_summary">Totes les sessions d\'arrel utilitzen el suport Namespace Global</string>
|
||||
<string name="requester_summary">Les sessions d\'arrel heretaran les peticiones Namespace</string>
|
||||
<string name="isolate_summary">Totes les sessions d\'arrel tindran la seva pròpia Namespace</string>
|
||||
<string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu.</string>
|
||||
<string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accessible des de el directori arrel i no pot ser un arxiu.</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Petició de superusuari</string>
|
||||
@ -216,6 +219,6 @@
|
||||
<string name="command">Ordre: %1$s</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Mostra apps del sistema</string>
|
||||
<string name="show_system_app">Mostra Apps del sistema</string>
|
||||
|
||||
</resources>
|
||||
|
@ -8,13 +8,14 @@
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="install">Installieren</string>
|
||||
<string name="unsupport_magisk_title">Nicht unterstützte Magisk Version</string>
|
||||
<string name="unsupport_magisk_msg">Diese Version von Magisk Manager unterstützt keine Magisk-Versionen unter %1$s.\n\nDie App verhält sich so, als ob kein Magisk installiert ist. Bitte Magisk so schnell wie möglich aktualisieren.</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version_error">Magisk ist nicht installiert</string>
|
||||
<string name="checking_for_updates">Suche nach Aktualisierungen…</string>
|
||||
<string name="invalid_update_channel">Ungültiger Aktualisierungskanal</string>
|
||||
<string name="safetyNet_check_text">SafetyNet-Status abfragen</string>
|
||||
<string name="checking_safetyNet_status">Prüfe SafetyNet-Status…</string>
|
||||
<string name="checking_safetyNet_status">SafetyNet-Status prüfen…</string>
|
||||
<string name="safetyNet_check_success">SafetyNet-Test erfolgreich</string>
|
||||
<string name="safetyNet_api_error">SafetyNet API Fehler</string>
|
||||
<string name="safetyNet_res_invalid">Die Antwort ist ungültig</string>
|
||||
@ -23,11 +24,12 @@
|
||||
<string name="advanced_settings_title">Erweiterte Optionen</string>
|
||||
<string name="keep_force_encryption">\"force encryption\" beibehalten</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity beibehalten</string>
|
||||
<string name="recovery_mode">Wiederherstellungsmodus</string>
|
||||
<string name="current_installed">Installiert: %1$s</string>
|
||||
<string name="latest_version">Neueste: %1$s</string>
|
||||
<string name="uninstall">Deinstallieren</string>
|
||||
<string name="uninstall_magisk_title">Magisk deinstallieren</string>
|
||||
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt. Root wird entfernt, und Ihre Daten werden möglicherweise verschlüsselt, wenn nicht bereits der Fall.</string>
|
||||
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt. Root wird entfernt, und deine Daten werden möglicherweise verschlüsselt, wenn es nicht bereits der Fall ist.</string>
|
||||
<string name="update">aktualisieren</string>
|
||||
<string name="core_only_enabled">(Core only Modus aktiviert)</string>
|
||||
|
||||
@ -43,6 +45,7 @@
|
||||
<string name="reboot_recovery">Neustart in das Recovery</string>
|
||||
<string name="reboot_bootloader">Neustart in den Bootloader</string>
|
||||
<string name="reboot_download">Neustart in den Download-Modus</string>
|
||||
<string name="reboot_edl">Neustart in den EDL-Modus</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Aktualisierung verfügbar</string>
|
||||
@ -67,6 +70,8 @@
|
||||
<string name="progress_channel">Fortschrittsbenachrichtigungen</string>
|
||||
<string name="download_complete">Download abgeschlossen</string>
|
||||
<string name="download_file_error">Fehler beim Herunterladen der Datei</string>
|
||||
<string name="download_open_parent">Im übergeordneten Ordner anzeigen</string>
|
||||
<string name="download_open_self">Datei anzeigen</string>
|
||||
<string name="magisk_update_title">Neues Magisk Update verfügbar!</string>
|
||||
<string name="manager_update_title">Aktualisierung für Magisk Manager verfügbar!</string>
|
||||
|
||||
@ -74,71 +79,77 @@
|
||||
<string name="manager_download_install">Herunterladen und installieren</string>
|
||||
<string name="download_zip_only">Nur Zip-Datei herunterladen</string>
|
||||
<string name="direct_install">Direkt installieren (empfohlen)</string>
|
||||
<string name="install_inactive_slot">Installiere in inaktiven Slot (Nach OTA)</string>
|
||||
<string name="install_inactive_slot">In inaktiven Slot installieren (Nach OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Dein Gerät wird GEZWUNGEN in den aktuell inaktiven Slot zu starten, nachdem ein Neustart durchgeführt wurde!\nBenutze diese Option nur, nachdem das OTA beendet wurde.\nFortsetzen?</string>
|
||||
<string name="select_method">Methode auswählen</string>
|
||||
<string name="setup_title">Zusätzliche Einrichtung</string>
|
||||
<string name="select_patch_file">Auswählen und Patchen einer Datei</string>
|
||||
<string name="patch_file_msg">Wählen Sie ein Rohabbild (*.img) oder eine ODIN-Tar-Datei (*.tar) aus.</string>
|
||||
<string name="patch_file_msg">Ein Rohabbild (*.img) oder eine ODIN-Tar-Datei (*.tar) auswählen.</string>
|
||||
<string name="reboot_delay_toast">Neustart in 5 Sekunden…</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Installiere %1$s</string>
|
||||
<string name="repo_install_title">%1$s installieren</string>
|
||||
<string name="repo_install_msg">Möchtest du %1$s installieren?</string>
|
||||
<string name="download">Herunterladen</string>
|
||||
<string name="reboot">Neustart</string>
|
||||
<string name="settings_reboot_toast">Neustarten, um die Änderungen zu übernehmen</string>
|
||||
<string name="release_notes">Versionshinweise</string>
|
||||
<string name="repo_cache_cleared">Repo-Cache geleert</string>
|
||||
|
||||
<string name="dtbo_patched_title">DTBO wurde gepatched!</string>
|
||||
<string name="dtbo_patched_reboot">Magisk Manager hat dtbo.img gepatched, bitte neustarten</string>
|
||||
<string name="flashing">Flashing</string>
|
||||
<string name="done">Erledigt!</string>
|
||||
<string name="failure">Fehler</string>
|
||||
<string name="hide_manager_title">Verberge Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Verbergen von Magisk Manager fehlgeschlagen…</string>
|
||||
<string name="open_link_failed_toast">Es wurde keine Anwendung gefunden, um diesen Link zu öffnen...</string>
|
||||
<string name="hide_manager_title">Magisk Manager verbergen…</string>
|
||||
<string name="hide_manager_fail_toast">Magisk Manager verbergen ist fehlgeschlagen…</string>
|
||||
<string name="open_link_failed_toast">Es wurde keine App gefunden, um diesen Link zu öffnen…</string>
|
||||
<string name="warning">Warnung</string>
|
||||
<string name="complete_uninstall">Komplette Deinstallation</string>
|
||||
<string name="restore_img">Images wiederherstellen</string>
|
||||
<string name="restore_img_msg">Wiederherstellen...</string>
|
||||
<string name="restore_img_msg">Wiederherstellen…</string>
|
||||
<string name="restore_done">Wiederherstellung erfolgreich!</string>
|
||||
<string name="restore_fail">Kein Original Backup vorhanden!</string>
|
||||
<string name="proprietary_title">Lade proprietären Code herunter</string>
|
||||
<string name="proprietary_notice">Magisk Manager ist FOSS und enthält keinen proprietären SafetyNet API Code von Google. Magisk Manager erlauben eine Erweiterung (enthält GoogleApiClient) für SafetyNet-Checks herunterzuladen?</string>
|
||||
<string name="setup_fail">Einrichtung fehlgeschlagen</string>
|
||||
<string name="env_fix_title">Zusätzliche Einrichtung erforderlich</string>
|
||||
<string name="env_fix_msg">Ihr Gerät benötigt zusätzliche Einrichtung, damit Magisk ordnungsgemäß funktioniert. Es wird eine Magisk-Installations-Zip wird heruntergeladen, fortfahren?</string>
|
||||
<string name="env_fix_msg">Dein Gerät benötigt eine zusätzliche Einrichtung, damit Magisk ordnungsgemäß funktioniert. Es wird eine Magisk-Installations-Zip heruntergeladen. Fortfahren?</string>
|
||||
<string name="setup_msg">Umgebungseinrichtung läuft…</string>
|
||||
<string name="authenticate">Authentifizieren</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">Allgemein</string>
|
||||
<string name="settings_dark_theme_title">Dunkles Theme</string>
|
||||
<string name="settings_dark_theme_summary">Dunkles Theme aktivieren</string>
|
||||
<string name="settings_download_path_title">Download-Verzeichnis</string>
|
||||
<string name="settings_download_path_message">Dateien werden in %1$s gespeichert</string>
|
||||
<string name="settings_clear_cache_title">Repo-Cache leeren</string>
|
||||
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informationen des Online-Repos. Erzwingt eine Aktualisierung</string>
|
||||
<string name="settings_hide_manager_title">Magisk Manager verbergen</string>
|
||||
<string name="settings_hide_manager_summary">Magisk Manager mit zufälligem Paketnamen neu packen</string>
|
||||
<string name="settings_restore_manager_title">Magisk Manager wiederherstellen</string>
|
||||
<string name="settings_restore_manager_summary">Stellt Magisk Manager mit ursprünglichem Paketnamen wieder her</string>
|
||||
<string name="settings_restore_manager_summary">Magisk Manager mit ursprünglichem Paketnamen wiederherstellen</string>
|
||||
<string name="language">Sprache</string>
|
||||
<string name="system_default">(Systemstandard)</string>
|
||||
<string name="settings_update">Aktualisierungs-Einstellungen</string>
|
||||
<string name="settings_check_update_title">Prüfe nach Aktualisierungen</string>
|
||||
<string name="settings_check_update_summary">Prüfe regelmäßig im Hintergrund nach Aktualisierungen</string>
|
||||
<string name="settings_check_update_title">Auf Aktualisierungen prüfen</string>
|
||||
<string name="settings_check_update_summary">Regelmäßig im Hintergrund auf Aktualisierungen prüfen</string>
|
||||
<string name="settings_update_channel_title">Aktualisierungs-Kanal</string>
|
||||
<string name="settings_update_stable">Stabil</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Benutzerdefiniert</string>
|
||||
<string name="settings_update_custom_msg">Gib eine benutzerdefinierte URL ein</string>
|
||||
<string name="settings_update_custom_msg">Eine benutzerdefinierte URL eingeben</string>
|
||||
<string name="settings_core_only_title">Nur Kernfunktionen</string>
|
||||
<string name="settings_core_only_summary">Aktiviert lediglich die Kernfunktionen, Module werden nicht geladen. MagiskSU und Magisk Hide bleiben weiterhin aktiv</string>
|
||||
<string name="settings_magiskhide_summary">Versteckt Magisk vor diversen Entdeckungsmethoden</string>
|
||||
<string name="settings_hosts_title">Systemlose Hosts-Datei</string>
|
||||
<string name="settings_hosts_summary">Systemlose Unterstützung für Werbeblocker</string>
|
||||
<string name="settings_hosts_toast">systemless Hosts-Modul hinzugefügt</string>
|
||||
<string name="settings_hosts_toast">Systemless Hosts-Modul hinzugefügt</string>
|
||||
|
||||
<string name="settings_app_name">Den gewünschten App-Namen eingeben</string>
|
||||
<string name="settings_app_name_hint">Neuer Name</string>
|
||||
<string name="settings_app_name_helper">Die App wird unter diesem Namen neu gepackt</string>
|
||||
<string name="settings_app_name_error">Ungültiges Format</string>
|
||||
<string name="settings_su_app_adb">Apps und ADB</string>
|
||||
<string name="settings_su_app">Nur Apps</string>
|
||||
<string name="settings_su_adb">Nur ADB</string>
|
||||
@ -156,6 +167,9 @@
|
||||
<string name="request_timeout_summary">%1$d Sekunden</string>
|
||||
<string name="settings_su_reauth_title">Nach Aktualisierung erneut authentifizieren</string>
|
||||
<string name="settings_su_reauth_summary">Superuser-Zugriff nach App-Aktualisierung erneut abfragen</string>
|
||||
<string name="settings_su_biometric_title">Biometrische Authentifizierung aktivieren</string>
|
||||
<string name="settings_su_biometric_summary">Biometrische Authentifizierung verwenden, um Superuser-Anfragen zu ermöglichen</string>
|
||||
<string name="no_biometric">Nicht unterstütztes Gerät oder keine biometrischen Einstellungen aktiviert</string>
|
||||
|
||||
<string name="multiuser_mode">Mehrbenutzermodus</string>
|
||||
<string name="settings_owner_only">Nur der Gerätebesitzer</string>
|
||||
@ -172,6 +186,7 @@
|
||||
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namespace</string>
|
||||
<string name="requester_summary">Root-Sitzungen erben den Namespace des Abfragenden</string>
|
||||
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namespace</string>
|
||||
<string name="settings_download_path_error">Fehler beim Erstellen eines Ordners. Er muss im Stammverzeichnis des Speichers zugänglich und darf keine Datei sein.</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Superuser-Anfrage</string>
|
||||
@ -187,7 +202,7 @@
|
||||
<string name="sixtymin">60 Min.</string>
|
||||
<string name="su_allow_toast">Superuser-Rechte für %1$s gewährt</string>
|
||||
<string name="su_deny_toast">Superuser-Rechte für %1$s verweigert</string>
|
||||
<string name="no_apps_found">Keine Applikationen gefunden</string>
|
||||
<string name="no_apps_found">Keine Apps gefunden</string>
|
||||
<string name="su_snack_grant">Superuser-Rechte werden für %1$s gewährt</string>
|
||||
<string name="su_snack_deny">Superuser-Rechte werden für %1$s verweigert</string>
|
||||
<string name="su_snack_notif_on">Benachrichtigungen für %1$s sind aktiviert</string>
|
||||
@ -205,6 +220,6 @@
|
||||
<string name="command">Befehl: %1$s</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">System Applikationen anzeigen</string>
|
||||
<string name="show_system_app">System-Apps anzeigen</string>
|
||||
|
||||
</resources>
|
||||
|
@ -3,216 +3,221 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="modules">Modules</string>
|
||||
<string name="downloads">Téléchargements</string>
|
||||
<string name="superuser">Super‐utilisateur</string>
|
||||
<string name="superuser">Super‑utilisateur</string>
|
||||
<string name="log">Journal</string>
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="install">Installer</string>
|
||||
<string name="unsupport_magisk_title">Version de Magisk non prise en charge</string>
|
||||
<string name="unsupport_magisk_title">Version de Magisk non prise en charge</string>
|
||||
<string name="unsupport_magisk_msg">Cette version de Magisk Manager ne prend pas en charge les versions de Magisk inférieures à %1$s.\n\nL’application se comportera comme si Magisk n’était pas installé, veuillez mettre à jour Magisk dès que possible.</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version_error">Magisk n’est pas installé.</string>
|
||||
<string name="checking_for_updates">Vérification des mises à jour…</string>
|
||||
<string name="invalid_update_channel">Canal de mise à jour invalide</string>
|
||||
<string name="safetyNet_check_text">Appuyez pour lancer le contrôle SafetyNet.</string>
|
||||
<string name="magisk_version_error">Magisk n’est pas installé</string>
|
||||
<string name="checking_for_updates">Vérification des mises à jour…</string>
|
||||
<string name="invalid_update_channel">Canal de mise à jour invalide</string>
|
||||
<string name="safetyNet_check_text">Appuyez pour lancer le contrôle SafetyNet</string>
|
||||
<string name="checking_safetyNet_status">Vérification de l’état de SafetyNet…</string>
|
||||
<string name="safetyNet_check_success">Contrôle SafetyNet passé avec succès</string>
|
||||
<string name="safetyNet_check_success">Contrôle SafetyNet réussi</string>
|
||||
<string name="safetyNet_api_error">Erreur de l’API SafetyNet</string>
|
||||
<string name="safetyNet_res_invalid">La réponse est incorrecte.</string>
|
||||
<string name="magisk_up_to_date">Magisk est à jour</string>
|
||||
<string name="manager_up_to_date">Magisk Manager est à jour</string>
|
||||
<string name="safetyNet_res_invalid">La réponse est incorrecte</string>
|
||||
<string name="magisk_up_to_date">Magisk est à jour</string>
|
||||
<string name="manager_up_to_date">Magisk Manager est à jour</string>
|
||||
<string name="advanced_settings_title">Paramètres avancés</string>
|
||||
<string name="keep_force_encryption">Conserver le chiffrement forcé</string>
|
||||
<string name="keep_dm_verity">Conserver AVB 2.0/dm-verity</string>
|
||||
<string name="recovery_mode">Mode Récupération</string>
|
||||
<string name="current_installed">Version actuellement installée : %1$s</string>
|
||||
<string name="latest_version">Dernière version disponible : %1$s</string>
|
||||
<string name="recovery_mode">Mode récupération</string>
|
||||
<string name="current_installed">Version actuellement installée : %1$s</string>
|
||||
<string name="latest_version">Dernière version disponible : %1$s</string>
|
||||
<string name="uninstall">Désinstaller</string>
|
||||
<string name="uninstall_magisk_title">Désinstaller Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Tous les modules seront désactivés ou supprimés. Les permissions de super‐utilisateur seront perdues et vos données seront potentiellement chiffrées si elles ne le sont pas déjà.</string>
|
||||
<string name="update">Mise à jour</string>
|
||||
<string name="core_only_enabled">(Mode « sans modules » activé)</string>
|
||||
<string name="uninstall_magisk_msg">Tous les modules seront désactivés ou supprimés !\nL’accès super‑utiliateur sera perdu !\nVos données seront potentiellement chiffrées si elles ne le sont pas déjà.</string>
|
||||
<string name="update">Mise à jour</string>
|
||||
<string name="core_only_enabled">(mode « sans modules » activé)</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(aucune information transmise)</string>
|
||||
<string name="no_modules_found">Aucun module trouvé.</string>
|
||||
<string name="update_file_created">Le module sera mis à jour au prochain redémarrage.</string>
|
||||
<string name="remove_file_created">Le module sera supprimé au prochain redémarrage.</string>
|
||||
<string name="remove_file_deleted">Le module ne sera pas supprimé au prochain redémarrage.</string>
|
||||
<string name="disable_file_created">Le module sera désactivé au prochain redémarrage.</string>
|
||||
<string name="disable_file_removed">Le module sera activé au prochain redémarrage.</string>
|
||||
<string name="no_modules_found">Aucun module trouvé</string>
|
||||
<string name="update_file_created">Le module sera mis à jour au prochain redémarrage !</string>
|
||||
<string name="remove_file_created">Le module sera supprimé au prochain redémarrage !</string>
|
||||
<string name="remove_file_deleted">Le module ne sera pas supprimé au prochain redémarrage !</string>
|
||||
<string name="disable_file_created">Le module sera désactivé au prochain redémarrage !</string>
|
||||
<string name="disable_file_removed">Le module sera activé au prochain redémarrage !</string>
|
||||
<string name="author">Créé par %1$s</string>
|
||||
<string name="reboot_recovery">Redémarrer en mode récupération</string>
|
||||
<string name="reboot_bootloader">Redémarrer en mode amorçage</string>
|
||||
<string name="reboot_download">Redémarrer en mode téléchargement</string>
|
||||
<string name="reboot_edl">Redémarrer en mode EDL</string>
|
||||
<string name="reboot_edl">Redémarrer en mode secours (EDL)</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Mise à jour disponible</string>
|
||||
<string name="installed">Installé</string>
|
||||
<string name="not_installed">Non installé</string>
|
||||
<string name="updated_on">Mis à jour le : %1$s</string>
|
||||
<string name="sorting_order">Mode de tri :</string>
|
||||
<string name="sort_by_name">par nom</string>
|
||||
<string name="sort_by_update">par date décroissante</string>
|
||||
<string name="updated_on">Mis à jour le : %1$s</string>
|
||||
<string name="sorting_order">Mode de tri</string>
|
||||
<string name="sort_by_name">alphabétique</string>
|
||||
<string name="sort_by_update">antichronologique</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveLog">Enregistrer le journal</string>
|
||||
<string name="menuReload">Actualiser</string>
|
||||
<string name="menuClearLog">Effacer le journal maintenant</string>
|
||||
<string name="logs_cleared">Journal effacé avec succès.</string>
|
||||
<string name="logs_cleared">Le journal a bien été effacé.</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="app_changelog">Journal</string>
|
||||
<string name="app_changelog">Journal des modifications</string>
|
||||
|
||||
<!-- System Components, Notifications -->
|
||||
<string name="update_channel">Mises à jour de Magisk</string>
|
||||
<string name="progress_channel">Progression des notifications</string>
|
||||
<string name="update_channel">Mises à jour de Magisk</string>
|
||||
<string name="progress_channel">Notifications de progression</string>
|
||||
<string name="download_complete">Téléchargement terminé</string>
|
||||
<string name="download_file_error">Erreur de téléchargement du fichier</string>
|
||||
<string name="download_open_parent">Afficher dans le dossier parent</string>
|
||||
<string name="download_open_self">Afficher fichier</string>
|
||||
<string name="magisk_update_title">Une mise à jour de Magisk est disponible !</string>
|
||||
<string name="manager_update_title">Une mise à jour de Magisk Manager est disponible !</string>
|
||||
<string name="download_file_error">Erreur lors du téléchargement du fichier</string>
|
||||
<string name="download_open_parent">Afficher le dossier parent</string>
|
||||
<string name="download_open_self">Afficher le fichier</string>
|
||||
<string name="magisk_update_title">Une mise à jour de Magisk est disponible !</string>
|
||||
<string name="manager_update_title">Une mise à jour de Magisk Manager est disponible !</string>
|
||||
|
||||
<!-- Installation -->
|
||||
<string name="manager_download_install">Appuyez pour le télécharger et l’installer.</string>
|
||||
<string name="download_zip_only">Télécharger le ZIP uniquement</string>
|
||||
<string name="download_zip_only">Télécharger le ZIP sans l’installer</string>
|
||||
<string name="direct_install">Installation directe (recommandée)</string>
|
||||
<string name="install_inactive_slot">Installer dans l’espace inactif (après mise à jour OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Votre appareil sera réamorcé à partir de l’espace actuellement inactif après un redémarrage!\nN’utilisez cette option qu’uniquement après que la mise à jour OTA ait été effectuée.\nVoulez-vous continuer?</string>
|
||||
<string name="install_inactive_slot">Installer dans l’espace inactif (après mise à jour OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Votre appareil sera obligatoirement réamorcé à partir de l’espace (slot) actuellement inactif après son redémarrage !\nN’utilisez cette option uniquement après que la mise à jour OTA a été effectuée.\nVoulez‑vous continuer ?</string>
|
||||
<string name="select_method">Sélectionnez la méthode</string>
|
||||
<string name="setup_title">Installation additionnelle</string>
|
||||
<string name="select_patch_file">Sélectionner et Patcher un Fichier</string>
|
||||
<string name="patch_file_msg">Sélectionnez une image brute (*.img) ou un fichier tar ODIN (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Redémarrage dans 5 secondes…</string>
|
||||
<string name="setup_title">Configuration supplémentaire</string>
|
||||
<string name="select_patch_file">Sélectionnez le fichier cible du correctif</string>
|
||||
<string name="patch_file_msg">Sélectionnez une image brute (*.img) ou une archive TAR ODIN (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Redémarrage dans 5 secondes…</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Installer %1$s</string>
|
||||
<string name="repo_install_msg">Voulez‐vous installer %1$s maintenant ?</string>
|
||||
<string name="repo_install_msg">Voulez‑vous installer %1$s maintenant ?</string>
|
||||
<string name="download">Télécharger</string>
|
||||
<string name="reboot">Redémarrer</string>
|
||||
<string name="settings_reboot_toast">Redémarrer afin d’appliquer les réglages.</string>
|
||||
<string name="settings_reboot_toast">Redémarrer afin d’appliquer les paramètres</string>
|
||||
<string name="release_notes">Notes de version</string>
|
||||
<string name="repo_cache_cleared">Cache du dépôt effacé</string>
|
||||
|
||||
<string name="dtbo_patched_title">DTBO a été modifié !</string>
|
||||
<string name="dtbo_patched_reboot">Magisk Manager vient de modifier le fichier dtbo.img, veuillez redémarrer.</string>
|
||||
<string name="flashing">Installation</string>
|
||||
<string name="done">Terminé!</string>
|
||||
<string name="failure">Échec</string>
|
||||
<string name="hide_manager_title">Masquer Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Le masquage de Magisk Manager a échoué.</string>
|
||||
<string name="open_link_failed_toast">Aucune application permettant d’ouvrir le lien n’a été trouvée.</string>
|
||||
<string name="dtbo_patched_title">L’arborescence matérielle (DTBO) a été modifiée !</string>
|
||||
<string name="dtbo_patched_reboot">Magisk Manager a modifié le fichier dtbo.img. Veuillez redémarrer.</string>
|
||||
<string name="flashing">Écriture dans la mémoire Flash…</string>
|
||||
<string name="done">Terminé !</string>
|
||||
<string name="failure">Erreur</string>
|
||||
<string name="hide_manager_title">Masquer Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Le masquage de Magisk Manager a échoué.</string>
|
||||
<string name="open_link_failed_toast">Aucune application permettant d’ouvrir le lien n’a été trouvée</string>
|
||||
<string name="warning">Avertissement</string>
|
||||
<string name="complete_uninstall">Désinstallation terminée</string>
|
||||
<string name="complete_uninstall">Désinstallation complète</string>
|
||||
<string name="restore_img">Restauration des images</string>
|
||||
<string name="restore_img_msg">Restauration…</string>
|
||||
<string name="restore_done">Restauration terminée !</string>
|
||||
<string name="restore_fail">La sauvegarde par défaut n’existe pas !</string>
|
||||
<string name="restore_fail">La sauvegarde par défaut n’existe pas !</string>
|
||||
<string name="proprietary_title">Téléchargement de code propriétaire</string>
|
||||
<string name="proprietary_notice">Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de l’API SafetyNet de Google.Voulez‐vous autoriser Magisk Manager à télécharger une extension (contenant GoogleApiClient) pour les contrôles SafetyNet ?</string>
|
||||
<string name="proprietary_notice">Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de l’API SafetyNet de Google. Autorisez‑vous Magisk Manager à télécharger une extension (contenant GoogleApiClient) pour les contrôles SafetyNet ?</string>
|
||||
<string name="setup_fail">Échec de l’installation</string>
|
||||
<string name="env_fix_title">Installation additionnelle requise</string>
|
||||
<string name="env_fix_msg">Votre appareil a besoin d’une configuration supplémentaire pour que Magisk fonctionne correctement. Pour cela, il est nécessaire de télécharger un fichier ZIP d’installation de Magisk. Voulez‐vous le faire maintenant ?</string>
|
||||
<string name="setup_msg">Démarrer l’installation de l’environnement…</string>
|
||||
<string name="env_fix_title">Installation supplémentaire requise</string>
|
||||
<string name="env_fix_msg">Votre appareil a besoin d’une installation supplémentaire afin que Magisk fonctionne correctement. Un fichier ZIP d’installation pour Magisk devra être téléchargé. Voulez‑vous faire cette installation maintenant ?</string>
|
||||
<string name="setup_msg">Installation de l’environnement en cours…</string>
|
||||
<string name="authenticate">Authentification</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">Général</string>
|
||||
<string name="settings_dark_theme_title">Thème sombre</string>
|
||||
<string name="settings_dark_theme_summary">Activer le thème sombre.</string>
|
||||
<string name="settings_download_path_title">Emplacement téléchargement</string>
|
||||
<string name="settings_dark_theme_summary">Activer le thème sombre</string>
|
||||
<string name="settings_download_path_title">Répertoire de téléchargement</string>
|
||||
<string name="settings_download_path_message">Les fichiers seront sauvegardés dans %1$s</string>
|
||||
<string name="settings_clear_cache_title">Effacer le cache du dépôt</string>
|
||||
<string name="settings_clear_cache_summary">Effacer les informations de cache pour les dépôts en ligne. Cela force l’application à récupérer ses informations en ligne.</string>
|
||||
<string name="settings_hide_manager_title">Masquer Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Réempaqueter Magisk Manager avec un nom de paquet aléatoire.</string>
|
||||
<string name="settings_restore_manager_title">Restaurer Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Restaurer Magisk Manager avec son nom de paquet d’origine</string>
|
||||
<string name="settings_clear_cache_summary">Effacer les informations en cache concerant les dépôts en ligne. Ceci force l’application à télécharger des informations à jour.</string>
|
||||
<string name="settings_hide_manager_title">Masquer Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Réempaqueter Magisk Manager avec des noms de paquet et d’application aléatoires.</string>
|
||||
<string name="settings_restore_manager_title">Restaurer Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Restaurer Magisk Manager avec ses noms de paquet et d’application d’origine</string>
|
||||
<string name="language">Langue</string>
|
||||
<string name="system_default">(système par défaut)</string>
|
||||
<string name="settings_update">Mise à jour des réglages</string>
|
||||
<string name="settings_check_update_title">Vérification des mises à jour</string>
|
||||
<string name="settings_check_update_summary">Vérifier périodiquement en tâche de fond l’existence d’une mise à jour.</string>
|
||||
<string name="settings_update_channel_title">Canal de mise à jour</string>
|
||||
<string name="system_default">(langue par défaut du système)</string>
|
||||
<string name="settings_update">Paramètres de mise à jour</string>
|
||||
<string name="settings_check_update_title">Vérifier les mises à jour</string>
|
||||
<string name="settings_check_update_summary">Vérifier périodiquement en tâche de fond l’existence d’une mise à jour</string>
|
||||
<string name="settings_update_channel_title">Canal de mise à jour</string>
|
||||
<string name="settings_update_stable">Stable</string>
|
||||
<string name="settings_update_beta">Bêta</string>
|
||||
<string name="settings_update_custom">Personnalisé</string>
|
||||
<string name="settings_update_custom_msg">Insérez une URL personnalisée</string>
|
||||
<string name="settings_core_only_title">Mode Magisk Core uniquement</string>
|
||||
<string name="settings_core_only_summary">Active uniquement les fonctionnalités de base. MagiskSU et MagiskHide resteront activés, mais aucun module ne sera chargé. </string>
|
||||
<string name="settings_update_custom_msg">Saisissez une URL personnalisée</string>
|
||||
<string name="settings_core_only_title">Mode « sans modules » uniquement</string>
|
||||
<string name="settings_core_only_summary">Activer uniquement les fonctionnalités de base. MagiskSU et MagiskHide resteront activés, mais aucun module ne sera chargé.</string>
|
||||
<string name="settings_magiskhide_summary">Rendre Magisk invisible à diverses formes de détection.</string>
|
||||
<string name="settings_hosts_title">Fichier hosts sans système</string>
|
||||
<string name="settings_hosts_summary">Prise en charge du fichier hosts sans système pour les applications de type AdBlock.</string>
|
||||
<string name="settings_hosts_toast">Ajout d’un module hosts sans système</string>
|
||||
<string name="settings_app_name">Taper le nom de l\'application désirée</string>
|
||||
<string name="settings_hosts_title">Fichier hosts hors partition système</string>
|
||||
<string name="settings_hosts_summary">Utilisation d’un fichier hosts hors de la partition système pour les applications de blocage de publicité.</string>
|
||||
<string name="settings_hosts_toast">Ajout d’un module pour fichier hosts hors système</string>
|
||||
|
||||
<string name="settings_app_name">Saisissez le nom de l’application désiré</string>
|
||||
<string name="settings_app_name_hint">Nouveau nom</string>
|
||||
<string name="settings_app_name_helper">L\'application sera réempacter sous ce nom</string>
|
||||
<string name="settings_app_name_error">Format invalide</string>
|
||||
<string name="settings_app_name_helper">L’application sera réempaquetée sous ce nom</string>
|
||||
<string name="settings_app_name_error">Format incorrect</string>
|
||||
<string name="settings_su_app_adb">Applications et ADB</string>
|
||||
<string name="settings_su_app">Applications uniquement</string>
|
||||
<string name="settings_su_adb">ADB uniquement</string>
|
||||
<string name="settings_su_disable">Désactivé</string>
|
||||
<string name="settings_su_request_10">10 secondes</string>
|
||||
<string name="settings_su_request_15">15 secondes</string>
|
||||
<string name="settings_su_request_20">20 secondes</string>
|
||||
<string name="settings_su_request_30">30 secondes</string>
|
||||
<string name="settings_su_request_45">45 secondes</string>
|
||||
<string name="settings_su_request_60">60 secondes</string>
|
||||
<string name="superuser_access">Accès super‐utilisateur</string>
|
||||
<string name="settings_su_request_10">10 secondes</string>
|
||||
<string name="settings_su_request_15">15 secondes</string>
|
||||
<string name="settings_su_request_20">20 secondes</string>
|
||||
<string name="settings_su_request_30">30 secondes</string>
|
||||
<string name="settings_su_request_45">45 secondes</string>
|
||||
<string name="settings_su_request_60">60 secondes</string>
|
||||
<string name="superuser_access">Accès super‑utilisateur</string>
|
||||
<string name="auto_response">Réponse automatique</string>
|
||||
<string name="request_timeout">Délai d’expiration de la demande</string>
|
||||
<string name="superuser_notification">Notification super‐utilisateur</string>
|
||||
<string name="request_timeout_summary">%1$d secondes</string>
|
||||
<string name="settings_su_reauth_title">Authentifier à nouveau après la mise à niveau</string>
|
||||
<string name="settings_su_reauth_summary">Authentifier à nouveau les autorisations super‐utilisateur après une mise à jour de l’application</string>
|
||||
<string name="superuser_notification">Notification super‑utilisateur</string>
|
||||
<string name="request_timeout_summary">%1$d secondes</string>
|
||||
<string name="settings_su_reauth_title">S’authentifier à nouveau après la mise à niveau</string>
|
||||
<string name="settings_su_reauth_summary">Redemander une authentification pour autoriser l’accès en super‑utilisateur après une mise à jour de l’application</string>
|
||||
<string name="settings_su_biometric_title">Activer l’authentification biométrique</string>
|
||||
<string name="settings_su_biometric_summary">Utiliser l’authentification biométrique pour autoriser les accès en super‑utilisateur</string>
|
||||
<string name="no_biometric">L’appareil n’est pas pris en charge ou alors aucun paramètre biométrique n’est activé</string>
|
||||
|
||||
<string name="multiuser_mode">Mode multi‐utilisateur</string>
|
||||
<string name="multiuser_mode">Mode multi‑utilisateur</string>
|
||||
<string name="settings_owner_only">Propriétaire de l’appareil uniquement</string>
|
||||
<string name="settings_owner_manage">Géré par le propriétaire de l’appareil</string>
|
||||
<string name="settings_user_independent">Indépendant de l’utilisateur</string>
|
||||
<string name="owner_only_summary">Seul le propriétaire a un accès super‐utilisateur.</string>
|
||||
<string name="owner_manage_summary">Seul le propriétaire peut gérer l’accès super‐utilisateur et recevoir les demandes de requêtes.</string>
|
||||
<string name="user_indepenent_summary">Chaque utilisateur a ses propres règles super‐utilisateur séparées.</string>
|
||||
<string name="owner_only_summary">Seul le propriétaire possède un accès super‑utilisateur.</string>
|
||||
<string name="owner_manage_summary">Seul le propriétaire peut gérer l’accès super‑utilisateur et recevoir les demandes d’accès.</string>
|
||||
<string name="user_indepenent_summary">Chaque utilisateur a ses propres règles d’accès super‑utilisateur séparées.</string>
|
||||
|
||||
<string name="mount_namespace_mode">Mode d’espace de noms du montage</string>
|
||||
<string name="mount_namespace_mode">Mode d’espace de noms du montage</string>
|
||||
<string name="settings_ns_global">Espace de noms global</string>
|
||||
<string name="settings_ns_requester">Hériter de l’espace de noms</string>
|
||||
<string name="settings_ns_requester">Hériter de l’espace de noms</string>
|
||||
<string name="settings_ns_isolate">Espace de noms isolé</string>
|
||||
<string name="global_summary">Toutes les sessions super‐utilisateur utilisent l’espace de noms global du montage.</string>
|
||||
<string name="requester_summary">Les sessions super‐utilisateur hériteront de l’espace de noms de leur demandeur.</string>
|
||||
<string name="isolate_summary">Chaque session super‐utilisateur aura son propre espace de noms isolé.</string>
|
||||
<string name="settings_download_path_error">Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string>
|
||||
<string name="global_summary">Toutes les sessions super‑utilisateur utilisent l’espace de noms global du montage.</string>
|
||||
<string name="requester_summary">Les sessions super‑utilisateur hériteront de l’espace de noms de leur demandeur.</string>
|
||||
<string name="isolate_summary">Chaque session super‑utilisateur aura son propre espace de noms isolé.</string>
|
||||
<string name="settings_download_path_error">Erreur lors de la création du dossier. Ce dernier doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Requête super‐utilisateur</string>
|
||||
<string name="su_request_title">Demande d’accès super‑utilisateur</string>
|
||||
<string name="deny">Refuser</string>
|
||||
<string name="prompt">Demander</string>
|
||||
<string name="grant">Accepter</string>
|
||||
<string name="su_warning">Autoriser l’accès complet à votre appareil.\nRefusez si vous n’êtes pas sûr !</string>
|
||||
<string name="su_warning">Autoriser l’accès complet à votre appareil.\nRefusez si vous n’êtes pas sûr(e) !</string>
|
||||
<string name="forever">Toujours</string>
|
||||
<string name="once">Une fois</string>
|
||||
<string name="once">Une fois</string>
|
||||
<string name="tenmin">10 min</string>
|
||||
<string name="twentymin">20 min</string>
|
||||
<string name="thirtymin">30 min</string>
|
||||
<string name="sixtymin">60 min</string>
|
||||
<string name="su_allow_toast">%1$s a obtenu les droits super‐utilisateur</string>
|
||||
<string name="su_deny_toast">%1$s n’a pas obtenu les droits super‐utilisateur</string>
|
||||
<string name="su_allow_toast">%1$s a obtenu les droits de super‑utilisateur</string>
|
||||
<string name="su_deny_toast">%1$s s’est vu refuser les droits de super‑utilisateur</string>
|
||||
<string name="no_apps_found">Aucune application trouvée</string>
|
||||
<string name="su_snack_grant">Les droits super‐utilisateur de %1$s sont accordés</string>
|
||||
<string name="su_snack_deny">Les droits super‐utilisateur de %1$s sont refusés</string>
|
||||
<string name="su_snack_notif_on">Les notifications pour %1$s sont activées</string>
|
||||
<string name="su_snack_notif_off">Les notifications pour %1$s sont désactivées</string>
|
||||
<string name="su_snack_log_on">La journalisation pour %1$s est activée</string>
|
||||
<string name="su_snack_log_off">La journalisation pour %1$s est désactivée</string>
|
||||
<string name="su_revoke_title">Annuler ?</string>
|
||||
<string name="su_revoke_msg">Confirmez‐vous l’annulation des droits pour %1$s ?</string>
|
||||
<string name="su_snack_grant">Les droits de super‑utilisateur sont accordés à %1$s</string>
|
||||
<string name="su_snack_deny">Les droits de super‑utilisateur sont refusés à %1$s</string>
|
||||
<string name="su_snack_notif_on">Les notifications sont activées pour %1$s</string>
|
||||
<string name="su_snack_notif_off">Les notifications sont désactivées pour %1$s</string>
|
||||
<string name="su_snack_log_on">La journalisation est activée pour %1$s</string>
|
||||
<string name="su_snack_log_off">La journalisation est désactivée pour %1$s</string>
|
||||
<string name="su_revoke_title">Révoquer ?</string>
|
||||
<string name="su_revoke_msg">Confirmez‑vous la révocation des droits accordés à %1$s ?</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="none">Aucun</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID : %1$d</string>
|
||||
<string name="target_uid">UID cible : %1$d</string>
|
||||
<string name="command">Commande : %1$s</string>
|
||||
<string name="pid">PID : %1$d</string>
|
||||
<string name="target_uid">UID cible : %1$d</string>
|
||||
<string name="command">Commande : %1$s</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Afficher les applications système</string>
|
||||
|
@ -8,6 +8,7 @@
|
||||
<string name="settings">Ayarlar</string>
|
||||
<string name="install">Yükle</string>
|
||||
<string name="unsupport_magisk_title">Desteklenmeyen Magisk Sürümü</string>
|
||||
<string name="unsupport_magisk_msg">Magisk Manager\'ın bu sürümü, %1$s daha düşük Magisk versiyonlarını desteklememektedir.\n\nUygulama hiçbir Magisk kurulu değil gibi davranacak, lütfen en kısa zamanda Magisk\'i yükseltin.</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version_error">Magisk yüklü değil</string>
|
||||
@ -94,7 +95,6 @@
|
||||
<string name="settings_reboot_toast">Ayarları uygulamak için yeniden başlatın</string>
|
||||
<string name="release_notes">Sürüm notları</string>
|
||||
<string name="repo_cache_cleared">Repo önbelleği temizlendi</string>
|
||||
|
||||
<string name="dtbo_patched_title">DTBO yamalandı!</string>
|
||||
<string name="dtbo_patched_reboot">Magisk Manager dtbo.img\'yi yamaladı, lütfen yeniden başlatın</string>
|
||||
<string name="flashing">Yükleniyor</string>
|
||||
@ -115,6 +115,7 @@
|
||||
<string name="env_fix_title">Ek Kurulum Gerekli</string>
|
||||
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Bu Magisk kurulum zip dosyasını indirecektir, şimdi devam etmek istiyor musunuz?</string>
|
||||
<string name="setup_msg">Ortam kurulumu çalışıyor…</string>
|
||||
<string name="authenticate">Kimlik doğrulaması</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">Genel</string>
|
||||
@ -166,6 +167,9 @@
|
||||
<string name="request_timeout_summary">%1$d saniye</string>
|
||||
<string name="settings_su_reauth_title">Yükseltmeden sonra yeniden kimlik doğrula</string>
|
||||
<string name="settings_su_reauth_summary">Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula</string>
|
||||
<string name="settings_su_biometric_title">Biyometrik Kimlik Doğrulamayı Etkinleştir</string>
|
||||
<string name="settings_su_biometric_summary">Superuser isteklerine izin vermek için biyometrik kimlik doğrulamayı kullanın</string>
|
||||
<string name="no_biometric">Desteklenmeyen cihaz veya biyometrik ayar etkinleştirilmemiş</string>
|
||||
|
||||
<string name="multiuser_mode">Çok Kullanıcılı Mod</string>
|
||||
<string name="settings_owner_only">Yalnızca Cihaz Sahibi</string>
|
||||
|
@ -167,7 +167,7 @@
|
||||
<string name="request_timeout_summary">%1$d 秒</string>
|
||||
<string name="settings_su_reauth_title">更新後重新驗證</string>
|
||||
<string name="settings_su_reauth_summary">應用程式更新後,重新驗證超級使用者的請求</string>
|
||||
|
||||
<string name="settings_su_biometric_title">啟用生物特徵驗證</string>
|
||||
<string name="multiuser_mode">多重使用者模式</string>
|
||||
<string name="settings_owner_only">僅限裝置擁有者</string>
|
||||
<string name="settings_owner_manage">由裝置擁有者管理</string>
|
||||
|
@ -7,7 +7,7 @@ if (configPath.exists())
|
||||
configPath.withInputStream { is -> props.load(is) }
|
||||
|
||||
buildscript {
|
||||
ext.vKotlin = '1.3.60'
|
||||
ext.vKotlin = '1.3.61'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
@ -16,7 +16,7 @@ buildscript {
|
||||
maven { url 'https://kotlin.bintray.com/kotlinx' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}"
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Magisk Documentation
|
||||
(Updated on 2019.9.19)
|
||||
(Updated on 2020.1.2)
|
||||
|
||||
- [Installation](install.md)
|
||||
- [Tutorials](tutorials.md)
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
The following sections are for developers
|
||||
|
||||
- [Magisk Details](details.md)
|
||||
- [Magisk Tools](tools.md)
|
||||
- [Developer Guides](guides.md)
|
||||
- [Magisk Tools](tools.md)
|
||||
- [Internal Details](details.md)
|
||||
- [Deployment](deploy.md)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Magisk Details
|
||||
# Internal Details
|
||||
## File Structure
|
||||
### Paths in "sbin tmpfs overlay"
|
||||
One of Magisk's breakthrough designs is sbin tmpfs overlay. It is required to support system-as-root devices, and also is the key to hiding Magisk from detection. All Magisk binaries, applets, mirrors, and other trivial stuffs are all located in the `tmpfs` mounted on `/sbin`. MagiskHide can just simply unmount `/sbin` and the bind mounts to hide all modifications easily.
|
||||
|
186
docs/guides.md
186
docs/guides.md
@ -1,9 +1,7 @@
|
||||
# Developer Guides
|
||||
|
||||
Please read through [Magisk Details](details.md) before reading the following guides. If you are developing a module, pay extra attention to the [Magic Mount](details.md#magic-mount) section.
|
||||
|
||||
## Magisk Modules
|
||||
A Magisk module is a folder placed in `/data/adb/modules` with a structure below:
|
||||
A Magisk module is a folder placed in `/data/adb/modules` with the structure below:
|
||||
|
||||
```
|
||||
/data/adb/modules
|
||||
@ -14,40 +12,38 @@ A Magisk module is a folder placed in `/data/adb/modules` with a structure below
|
||||
│ │
|
||||
│ │ *** Module Identity ***
|
||||
│ │
|
||||
│ ├── module.prop <--- This files stores the metadata of the module
|
||||
│ ├── module.prop <--- This file stores the metadata of the module
|
||||
│ │
|
||||
│ │ *** Status files ***
|
||||
│ │ *** Main Contents ***
|
||||
│ │
|
||||
│ ├── skip_mount <--- If exists, Magisk will NOT mount your files
|
||||
│ ├── system <--- This folder will be mounted if skip_mount does not exists
|
||||
│ │ ├── ...
|
||||
│ │ ├── ...
|
||||
│ │ └── ...
|
||||
│ │
|
||||
│ │ *** Status Flags ***
|
||||
│ │
|
||||
│ ├── skip_mount <--- If exists, Magisk will NOT mount your system folder
|
||||
│ ├── disable <--- If exists, the module will be disabled
|
||||
│ ├── remove <--- If exists, the module will be removed next reboot
|
||||
│ │
|
||||
│ │ *** Scripts ***
|
||||
│ │ *** Optional Files ***
|
||||
│ │
|
||||
│ ├── post-fs-data.sh <--- This script will be executed in post-fs-data
|
||||
│ ├── service.sh <--- This script will be executed in late_start service
|
||||
| ├── uninstall.sh <--- This script will be executed when Magisk removes your module
|
||||
│ │
|
||||
│ │ *** Resetprop Files ***
|
||||
│ │
|
||||
│ ├── system.prop <--- This file will be loaded as system properties by resetprop
|
||||
│ │
|
||||
│ │ *** Module contents ***
|
||||
│ │
|
||||
│ ├── system <--- This folder will be Magic Mounted if skip_mount does not exists
|
||||
│ │ ├── .
|
||||
│ │ ├── .
|
||||
│ │ └── .
|
||||
│ ├── system.prop <--- Properties in this file will be loaded as system properties by resetprop
|
||||
│ ├── sepolicy.rule <--- Additional custom sepolicy rules to be patched
|
||||
│ │
|
||||
│ │ *** Auto Generated by Magisk ***
|
||||
│ │ *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
|
||||
│ │
|
||||
│ ├── vendor <--- A symlink to $MODID/system/vendor
|
||||
│ ├── product <--- A symlink to $MODID/system/product
|
||||
│ │
|
||||
│ │ *** Others ***
|
||||
│ │ *** Any additional files / folders are allowed ***
|
||||
│ │
|
||||
│ ├── . <--- Any additional files / folders are allowed
|
||||
│ └── .
|
||||
│ ├── ...
|
||||
│ └── ...
|
||||
|
|
||||
├── another_module
|
||||
│ ├── .
|
||||
@ -55,9 +51,11 @@ A Magisk module is a folder placed in `/data/adb/modules` with a structure below
|
||||
├── .
|
||||
├── .
|
||||
```
|
||||
As long as a folder follows the structure above, it will be recognized as a module.
|
||||
|
||||
Here is the **strict** format of `module.prop`:
|
||||
|
||||
#### module.prop
|
||||
|
||||
This is the **strict** format of `module.prop`
|
||||
|
||||
```
|
||||
id=<string>
|
||||
@ -71,27 +69,134 @@ description=<string>
|
||||
ex: ✓ `a_module`, ✓ `a.module`, ✓ `module-101`, ✗ `a module`, ✗ `1_module`, ✗ `-a-module`<br>
|
||||
This is the **unique identifier** of your module. You should not change it once published.
|
||||
- `versionCode` has to be an **integer**. This is used to compare versions
|
||||
- Others that isn't mentioned above can be any **single line** string.
|
||||
- Others that weren't mentioned above can be any **single line** string.
|
||||
|
||||
#### Shell scripts (`*.sh`)
|
||||
Please read the [Boot Scripts](#boot-scripts) section to understand the difference between `post-fs-data.sh` and `service.sh`. For most module developers, `service.sh` should be good enough if you just need to run a boot script.
|
||||
|
||||
In all scripts of your module, please use `MODDIR=${0%/*}` to get your module's base directory path; do **NOT** hardcode your module path in scripts.
|
||||
|
||||
#### system.prop
|
||||
This file follows the same format as `build.prop`. Each line comprises of `[key]=[value]`.
|
||||
|
||||
#### sepolicy.rule
|
||||
If your module requires some additional sepolicy patches, please add those rules into this file. The module installer script and Magisk's daemon will make sure this file is copied to somewhere `magiskinit` can read pre-init to ensure these rules are injected properly.
|
||||
|
||||
Each line in this file will be treated as a policy statement. For more details how a policy statement is formated, please check [magiskpolicy](tools.md#magiskpolicy)'s documentation.
|
||||
|
||||
#### The `system` folder
|
||||
All files you want Magisk to replace/inject for you should be placed in this folder. Please read through the [Magic Mount](details.md#magic-mount) section to understand how Magisk mount your files.
|
||||
|
||||
## Magisk Module Installer
|
||||
The official Magisk Module installer is hosted **[here](https://github.com/topjohnwu/magisk-module-installer)**.
|
||||
|
||||
That repo is a starting point for creating a Magisk module installer zip. Here are some details:
|
||||
A Magisk Module Installer is a Magisk Module packaged in a zip file that can be flashed in Magisk Manager or custom recoveries such as TWRP. An installer have the same file structure as a Magisk module (please check the previous section for more info). The simplest Magisk Module Installer is just a Magisk Module packed in a zip file, with addition to the following files:
|
||||
|
||||
- You will found out that `META-INF/com/google/android/update-binary` is a dummy file. If you are creating a module locally for personal usage or testing, download the latest installer [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and replace `update-binary` with the script
|
||||
- The next thing to do is to modify `module.prop` to provide information about your module
|
||||
- Finally, modify `install.sh` to fit your need. The script is heavily documented so you should know what to do.
|
||||
- (Optional) If you want to run custom uninstallation logic when your module is removed, add a new file `uninstall.sh` to the root of the installer
|
||||
- **Windows users aware!!** The line endings of all text files should be in the **Unix format**. Please use advanced text editors like Sublime, Atom, Notepad++ etc. to edit **ALL** text files, **NEVER** use Windows Notepad.
|
||||
- `update-binary`: Download the latest [module_installer.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and rename/copy that script as `update-binary`
|
||||
- `updater-script`: This file should only contain the string `#MAGISK`
|
||||
|
||||
By default, `update-binary` will check/setup the environment, load utility functions, extract the module installer zip to where your module will be installed, and finally do some trivial tasks and cleanups, which should cover most simple modules' needs.
|
||||
|
||||
```
|
||||
module.zip
|
||||
│
|
||||
├── META-INF
|
||||
│ └── com
|
||||
│ └── google
|
||||
│ └── android
|
||||
│ ├── update-binary <--- The module_installer.sh you downloaded
|
||||
│ └── updater-script <--- Should only contain the string "#MAGISK"
|
||||
│
|
||||
├── customize.sh <--- (Optional, more details later)
|
||||
│ This script will be sourced by update-binary
|
||||
├── ...
|
||||
├── ... /* The rest of module's files */
|
||||
|
|
||||
```
|
||||
|
||||
#### Customization
|
||||
|
||||
If you need to customize the module installation process, optionally you can create a new script in the installer called `customize.sh`. This script will be *sourced* (not executed!) by `update-binary` after all files are extracted and default permissions and secontext are applied. This is very useful if your module includes different files based on ABI, or you need to set special permissions/secontext for some of your files (e.g. files in `/system/bin`).
|
||||
|
||||
If you need even more customization and prefer to do everything on your own, declare `SKIPUNZIP=1` in `customize.sh` to skip the extraction and applying default permissions/secontext steps. Be aware that by doing so, your `customize.sh` will then be responsible to install everything by itself.
|
||||
|
||||
#### `customize.sh` Environment
|
||||
|
||||
Magisk's internal busybox's path `$BBPATH` is added in the front of `PATH`. The following variables and shell functions are available for convenience:
|
||||
|
||||
##### Variables
|
||||
- `MAGISK_VER` (string): the version string of current installed Magisk (e.g. `v20.0`)
|
||||
- `MAGISK_VER_CODE` (int): the version code of current installed Magisk (e.g. `20000`)
|
||||
- `BOOTMODE` (bool): `true` if the module is being installed in Magisk Manager
|
||||
- `MODPATH` (path): the path where your module files should be installed
|
||||
- `TMPDIR` (path): a place where you can temporarily store files
|
||||
- `ZIPFILE` (path): your module's installation zip
|
||||
- `ARCH` (string): the CPU architecture of the device. Value is either `arm`, `arm64`, `x86`, or `x64`
|
||||
- `IS64BIT` (bool): `true` if `$ARCH` is either `arm64` or `x64`
|
||||
- `API` (int): the API level (Android version) of the device (e.g. `21` for Android 5.0)
|
||||
|
||||
##### Functions
|
||||
|
||||
```
|
||||
ui_print <msg>
|
||||
print <msg> to console
|
||||
Avoid using 'echo' as it will not display in custom recovery's console
|
||||
|
||||
abort <msg>
|
||||
print error message <msg> to console and terminate installation
|
||||
Avoid using 'exit' as it will skip the termination cleanup steps
|
||||
|
||||
set_perm <target> <owner> <group> <permission> [context]
|
||||
if [context] is not set, the default is "u:object_r:system_file:s0"
|
||||
this function is a shorthand for the following commands:
|
||||
chown owner.group target
|
||||
chmod permission target
|
||||
chcon context target
|
||||
|
||||
set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
|
||||
if [context] is not set, the default is "u:object_r:system_file:s0"
|
||||
for all files in <directory>, it will call:
|
||||
set_perm file owner group filepermission context
|
||||
for all directories in <directory> (including itself), it will call:
|
||||
set_perm dir owner group dirpermission context
|
||||
```
|
||||
|
||||
##### Easy Replace
|
||||
You can declare a list of folders you want to directly replace in the variable name `REPLACE`. The module installer script will pickup this variable and create `.replace` files for you. An example declaration:
|
||||
|
||||
```
|
||||
REPLACE="
|
||||
/system/app/YouTube
|
||||
/system/app/Bloatware
|
||||
"
|
||||
```
|
||||
The list above will result in the following files being created: `$MODPATH/system/app/YouTube/.replace` and `$MODPATH/system/app/Bloatware/.replace`
|
||||
|
||||
#### Notes
|
||||
|
||||
- When your module is downloaded with Magisk Manager, `update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) to ensure all installer uses up-to-date scripts. **DO NOT** try to add any custom logic in `update-binary` as it is pointless.
|
||||
- Due to historical reasons, **DO NOT** add a file named `install.sh` in your module installer. That specific file was previously used and will be treated differently.
|
||||
- **DO NOT** call `exit` at the end of `customize.sh`. The module installer would want to do finalizations.
|
||||
|
||||
## Submit Modules
|
||||
You can submit a module to **Magisk-Module-Repo** so users can download your module directly in Magisk Manager. Before submitting, follow the instructions in the previous section to create a valid installer for your module. You can then create a request for submission via this link: [submission](https://github.com/Magisk-Modules-Repo/submission).
|
||||
|
||||
- When your module is downloaded with Magisk Manager, `META-INF/com/google/android/update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) to make sure all installation uses the latest scripts.
|
||||
- Since `update-binary` will be replaced, this means that all modules in the repo are expected to follow how the installation framework works: the installation framework will load your `install.sh` script and run the corresponding callbacks.
|
||||
- This also means that you should NOT add custom logic in `update-binary` as they will simply be ignored.
|
||||
You can submit a module to **Magisk-Module-Repo** so users can download your module directly in Magisk Manager.
|
||||
|
||||
- Follow the instructions in the previous section to create a valid installer for your module.
|
||||
- Create `README.md` (filename should be exactly the same) containing all info for your module. If you are not familiar with the Markdown syntax, the [Markdown Cheat Sheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) will be handy.
|
||||
- Create a repository with your personal GitHub account, and upload your module installer to the repo
|
||||
- Create a request for submission via this link: [submission](https://github.com/Magisk-Modules-Repo/submission)
|
||||
|
||||
## Module Tricks
|
||||
|
||||
#### Remove Files
|
||||
How to remove a file systemless-ly? To actually make the file *disappear* is complicated (possible, not worth the effort). Replacing it with a dummy file should be good enough! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
|
||||
|
||||
#### Remove Folders
|
||||
Same as mentioned above, actually making the folder *disappear* is not worth the effort. Replacing it with an empty folder should be good enough! A handy trick for module developers is to add the folder you want to remove into the `REPLACE` list within `customize.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.
|
||||
|
||||
|
||||
## Boot Scripts
|
||||
|
||||
In Magisk, you can run boot scripts in 2 different modes: **post-fs-data** and **late_start service** mode.
|
||||
|
||||
- post-fs-data mode
|
||||
@ -118,9 +223,10 @@ In Magisk, there are also 2 kinds of scripts: **general scripts** and **module s
|
||||
- Will NOT be executed when **Core-Only** mode is enabled (all modules are disabled)
|
||||
- Modules require boot scripts should **ONLY** use module scripts instead of general scripts
|
||||
|
||||
Magisk's internal busybox's path `$BBPATH` is always prepended in `PATH`. This means all commands you call in scripts are always using busybox unless the applet is not included. This makes sure that your script always run in a predictable environment and always have the full suite of commands regardless of which Android version it is running on.
|
||||
Magisk's internal busybox's path `$BBPATH` is added in the front of `PATH`. This means all commands you call in scripts are always using busybox unless the applet is not included. This makes sure that your script always run in a predictable environment and always have the full suite of commands regardless of which Android version it is running on.
|
||||
|
||||
## Root Directory Overlay System
|
||||
|
||||
Since `/` is read-only in system-as-root devices, Magisk provides an overlay system, allowing developers to patch files / add new rc scripts. Additional files shall be placed in the `overlay.d` folder in the ramdisk, and they will have the following restrictions:
|
||||
|
||||
- All `*.rc` files in `overlay.d` will be read and concatenated *AFTER* `init.rc`
|
||||
@ -130,9 +236,3 @@ e.g. you can replace `/res/random.png` by adding the file `overlay.d/res/random.
|
||||
e.g. `overlay.d/new_file` will be ignored if `/new_file` does not exist
|
||||
- Additional files in `overlay.d/sbin` is allowed as they will be copied into Magisk's sbin overlay.<br>
|
||||
e.g. `overlay.d/sbin/libfoo.ko` will be copied to `/sbin/libfoo.ko`.
|
||||
|
||||
## Remove Files
|
||||
How to remove a file systemless-ly? To actually make the file *disappear* is complicated (possible, not worth the effort). Replacing it with a dummy file should be good enough! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
|
||||
|
||||
## Remove Folders
|
||||
Same as mentioned above, actually making the folder *disappear* is not worth the effort. Replacing it with an empty folder should be good enough! A handy trick for module developers is to add the folder you want to remove into the `REPLACE` list within `install.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.
|
||||
|
@ -25,9 +25,11 @@ The concept of `magiskboot` is to make boot image modification simpler. For unpa
|
||||
Usage: magiskboot <action> [args...]
|
||||
|
||||
Supported actions:
|
||||
unpack [-h] <bootimg>
|
||||
unpack [-n] [-h] <bootimg>
|
||||
Unpack <bootimg> to, if available, kernel, kernel_dtb, ramdisk.cpio,
|
||||
second, dtb, extra, and recovery_dtbo into current directory.
|
||||
If '-n' is provided, it will not attempt to decompress kernel or
|
||||
ramdisk.cpio from their original formats.
|
||||
If '-h' is provided, it will dump header info to 'header',
|
||||
which will be parsed when repacking.
|
||||
Return values:
|
||||
@ -45,7 +47,7 @@ Supported actions:
|
||||
|
||||
cpio <incpio> [commands...]
|
||||
Do cpio commands to <incpio> (modifications are done directly)
|
||||
Each command is a single argument, use quotes if necessary
|
||||
Each command is a single argument, add quotes for each command
|
||||
Supported commands:
|
||||
exists ENTRY
|
||||
Return 0 if ENTRY exists, else return 1
|
||||
@ -65,8 +67,9 @@ Supported actions:
|
||||
Test the current cpio's patch status
|
||||
Return values:
|
||||
0:stock 1:Magisk 2:unsupported (phh, SuperSU, Xposed)
|
||||
patch KEEPVERITY KEEPFORCEENCRYPT
|
||||
Ramdisk patches. KEEP**** are boolean values
|
||||
patch
|
||||
Apply ramdisk patches. Configure settings with env variables:
|
||||
KEEPVERITY KEEPFORCEENCRYPT
|
||||
backup ORIG
|
||||
Create ramdisk backups from ORIG
|
||||
restore
|
||||
@ -74,17 +77,25 @@ Supported actions:
|
||||
sha1
|
||||
Print stock boot SHA1 if previously backed up in ramdisk
|
||||
|
||||
dtb-<cmd> <dtb>
|
||||
Do dtb related cmds to <dtb> (modifications are done directly)
|
||||
Supported commands:
|
||||
dump
|
||||
Dump all contents from dtb for debugging
|
||||
test
|
||||
Check if fstab has verity/avb flags
|
||||
Return values:
|
||||
0:flag exists 1:no flags
|
||||
patch
|
||||
dtb <input> <action> [args...]
|
||||
Do dtb related actions to <input>
|
||||
Supported actions:
|
||||
print [-f]
|
||||
Print all contents of dtb for debugging
|
||||
Specify [-f] to only print fstab nodes
|
||||
patch [OUT]
|
||||
Search for fstab and remove verity/avb
|
||||
If [OUT] is not specified, it will directly output to <input>
|
||||
Configure with env variables: KEEPVERITY TWOSTAGEINIT
|
||||
|
||||
split <input>
|
||||
Split image.*-dtb into kernel + kernel_dtb
|
||||
|
||||
sha1 <file>
|
||||
Print the SHA1 checksum for <file>
|
||||
|
||||
cleanup
|
||||
Cleanup the current working directory
|
||||
|
||||
compress[=method] <infile> [outfile]
|
||||
Compress <infile> with [method] (default: gzip), optionally to [outfile]
|
||||
@ -95,12 +106,6 @@ Supported actions:
|
||||
Detect method and decompress <infile>, optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
Supported methods: bzip2 gzip lz4 lz4_legacy lzma xz
|
||||
|
||||
sha1 <file>
|
||||
Print the SHA1 checksum for <file>
|
||||
|
||||
cleanup
|
||||
Cleanup the current working directory
|
||||
```
|
||||
|
||||
### magiskinit
|
||||
@ -119,20 +124,22 @@ Usage: magiskpolicy [--options...] [policy statements...]
|
||||
Options:
|
||||
--help show help message for policy statements
|
||||
--load FILE load policies from FILE
|
||||
--load-split load from preloaded sepolicy or compile
|
||||
--load-split load from precompiled sepolicy or compile
|
||||
split policies
|
||||
--compile-split compile split cil policies
|
||||
--save FILE save policies to FILE
|
||||
--live directly apply sepolicy live
|
||||
--magisk inject built-in rules for a minimal
|
||||
Magisk selinux environment
|
||||
--apply FILE apply rules from FILE, read and parsed
|
||||
line by line as policy statements
|
||||
|
||||
If neither --load or --compile-split is specified, it will load
|
||||
from current live policies (/sys/fs/selinux/policy)
|
||||
|
||||
One policy statement should be treated as one parameter;
|
||||
this means a full policy statement should be enclosed in quotes;
|
||||
multiple policy statements can be provided in a single command
|
||||
this means a full policy statement should be enclosed in quotes.
|
||||
Multiple policy statements can be provided in a single command.
|
||||
|
||||
The statements has a format of "<rule_name> [args...]"
|
||||
Multiple types and permissions can be grouped into collections
|
||||
@ -173,10 +180,10 @@ Notes:
|
||||
Example: allow { s1 s2 } { t1 t2 } class *
|
||||
Will be expanded to:
|
||||
|
||||
allow s1 t1 class { all permissions }
|
||||
allow s1 t2 class { all permissions }
|
||||
allow s2 t1 class { all permissions }
|
||||
allow s2 t2 class { all permissions }
|
||||
allow s1 t1 class { all-permissions }
|
||||
allow s1 t2 class { all-permissions }
|
||||
allow s2 t1 class { all-permissions }
|
||||
allow s2 t2 class { all-permissions }
|
||||
```
|
||||
|
||||
|
||||
@ -202,7 +209,6 @@ Advanced Options (Internal APIs):
|
||||
--clone-attr SRC DEST clone permission, owner, and selinux context
|
||||
--clone SRC DEST clone SRC to DEST
|
||||
--sqlite SQL exec SQL commands to Magisk database
|
||||
--use-broadcast use broadcast for su logging and notify
|
||||
|
||||
Supported init triggers:
|
||||
post-fs-data, service, boot-complete
|
||||
@ -269,5 +275,4 @@ Actions:
|
||||
ls Print the current hide list
|
||||
exec CMDs... Execute commands in isolated mount
|
||||
namespace and do all hide unmounts
|
||||
test Run process monitor test
|
||||
```
|
||||
|
@ -73,6 +73,7 @@ LOCAL_SRC_FILES := \
|
||||
magiskpolicy/magiskpolicy.cpp \
|
||||
magiskpolicy/rules.cpp \
|
||||
magiskpolicy/policydb.cpp \
|
||||
magiskpolicy/statement.cpp \
|
||||
magiskpolicy/sepolicy.c
|
||||
|
||||
LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=magiskpolicy_main
|
||||
@ -97,7 +98,6 @@ ifdef BB_INIT
|
||||
LOCAL_STATIC_LIBRARIES := libsepol libxz libutils
|
||||
LOCAL_C_INCLUDES := \
|
||||
jni/include \
|
||||
jni/magiskpolicy \
|
||||
$(EXT_PATH)/include \
|
||||
out \
|
||||
out/$(TARGET_ARCH_ABI) \
|
||||
@ -106,13 +106,14 @@ LOCAL_C_INCLUDES := \
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
init/init.cpp \
|
||||
init/early_mount.cpp \
|
||||
init/mount.cpp \
|
||||
init/rootdir.cpp \
|
||||
init/getinfo.cpp \
|
||||
magiskpolicy/api.cpp \
|
||||
magiskpolicy/magiskpolicy.cpp \
|
||||
magiskpolicy/rules.cpp \
|
||||
magiskpolicy/policydb.cpp \
|
||||
magiskpolicy/statement.cpp \
|
||||
magiskpolicy/sepolicy.c
|
||||
|
||||
LOCAL_LDFLAGS := -static
|
||||
|
@ -131,7 +131,7 @@ void node_entry::create_module_tree(const char *module) {
|
||||
auto full_path = get_path();
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MODULEROOT, module, full_path.c_str());
|
||||
|
||||
unique_ptr<DIR, decltype(&closedir)> dir(xopendir(buf), closedir);
|
||||
auto dir = xopen_dir(buf);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
@ -148,7 +148,7 @@ void node_entry::create_module_tree(const char *module) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (struct dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
// Create new node
|
||||
@ -207,22 +207,18 @@ void node_entry::create_module_tree(const char *module) {
|
||||
}
|
||||
|
||||
void node_entry::clone_skeleton() {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
// Clone the structure
|
||||
auto full_path = get_path();
|
||||
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path.c_str());
|
||||
if (!(dir = xopendir(buf)))
|
||||
return;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path.data());
|
||||
if (auto dir = xopen_dir(buf); dir) {
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
// Create dummy node
|
||||
auto dummy = new node_entry(entry->d_name, entry->d_type, IS_DUMMY);
|
||||
insert(dummy);
|
||||
}
|
||||
closedir(dir);
|
||||
} else { return; }
|
||||
|
||||
if (status & IS_SKEL) {
|
||||
file_attr attr;
|
||||
@ -321,13 +317,15 @@ static int bind_mount(const char *from, const char *to, bool log) {
|
||||
static bool magisk_env() {
|
||||
LOGI("* Initializing Magisk environment\n");
|
||||
|
||||
string pkg;
|
||||
check_manager(&pkg);
|
||||
|
||||
char install_dir[128];
|
||||
sprintf(install_dir, "%s/0/%s/install", APP_DATA_DIR, pkg.data());
|
||||
|
||||
// Alternative binaries paths
|
||||
constexpr const char *alt_bin[] = {
|
||||
"/cache/data_adb/magisk", "/data/magisk",
|
||||
"/data/data/com.topjohnwu.magisk/install",
|
||||
"/data/user_de/0/com.topjohnwu.magisk/install"
|
||||
};
|
||||
for (auto &alt : alt_bin) {
|
||||
const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", install_dir };
|
||||
for (auto alt : alt_bin) {
|
||||
struct stat st;
|
||||
if (lstat(alt, &st) != -1) {
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
@ -415,15 +413,9 @@ static bool magisk_env() {
|
||||
}
|
||||
|
||||
static void prepare_modules() {
|
||||
const char *legacy_imgs[] = {SECURE_DIR "/magisk.img", SECURE_DIR "/magisk_merge.img"};
|
||||
for (auto img : legacy_imgs) {
|
||||
if (access(img, F_OK) == 0)
|
||||
migrate_img(img);
|
||||
}
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
if ((dir = opendir(MODULEUPGRADE))) {
|
||||
while ((entry = xreaddir(dir))) {
|
||||
// Upgrade modules
|
||||
if (auto dir = open_dir(MODULEUPGRADE); dir) {
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
@ -436,7 +428,6 @@ static void prepare_modules() {
|
||||
rename(buf2, buf);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
rm_rf(MODULEUPGRADE);
|
||||
}
|
||||
bind_mount(MIRRDIR MODULEROOT, MODULEMNT, false);
|
||||
@ -458,51 +449,48 @@ static void reboot() {
|
||||
|
||||
void remove_modules() {
|
||||
LOGI("* Remove all modules and reboot");
|
||||
chdir(MODULEROOT);
|
||||
rm_rf("lost+found");
|
||||
DIR *dir = xopendir(".");
|
||||
struct dirent *entry;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
auto dir = xopen_dir(MODULEROOT);
|
||||
int dfd = dirfd(dir.get());
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv)
|
||||
continue;
|
||||
chdir(entry->d_name);
|
||||
close(creat("remove", 0644));
|
||||
chdir("..");
|
||||
|
||||
int modfd = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
close(xopenat(modfd, "remove", O_RDONLY | O_CREAT | O_CLOEXEC));
|
||||
close(modfd);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
chdir("/");
|
||||
reboot();
|
||||
}
|
||||
|
||||
static void collect_modules() {
|
||||
chdir(MODULEROOT);
|
||||
rm_rf("lost+found");
|
||||
DIR *dir = xopendir(".");
|
||||
struct dirent *entry;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
auto dir = xopen_dir(MODULEROOT);
|
||||
int dfd = dirfd(dir.get());
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv)
|
||||
continue;
|
||||
chdir(entry->d_name);
|
||||
if (access("remove", F_OK) == 0) {
|
||||
chdir("..");
|
||||
|
||||
int modfd = xopenat(dfd, entry->d_name, O_RDONLY);
|
||||
run_finally f([=]{ close(modfd); });
|
||||
|
||||
if (faccessat(modfd, "remove", F_OK, 0) == 0) {
|
||||
LOGI("%s: remove\n", entry->d_name);
|
||||
sprintf(buf, "%s/uninstall.sh", entry->d_name);
|
||||
fd_pathat(modfd, "uninstall.sh", buf, sizeof(buf));
|
||||
if (access(buf, F_OK) == 0)
|
||||
exec_script(buf);
|
||||
rm_rf(entry->d_name);
|
||||
frm_rf(modfd);
|
||||
unlinkat(dfd, entry->d_name, AT_REMOVEDIR);
|
||||
continue;
|
||||
}
|
||||
unlink("update");
|
||||
if (access("disable", F_OK))
|
||||
|
||||
unlinkat(modfd, "update", 0);
|
||||
|
||||
if (faccessat(modfd, "disable", F_OK, 0) != 0)
|
||||
module_list.emplace_back(entry->d_name);
|
||||
chdir("..");
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
chdir("/");
|
||||
}
|
||||
|
||||
static bool load_modules(node_entry *root) {
|
||||
@ -510,36 +498,47 @@ static bool load_modules(node_entry *root) {
|
||||
|
||||
bool has_modules = false;
|
||||
for (const auto &m : module_list) {
|
||||
const auto module = m.c_str();
|
||||
const auto module = m.data();
|
||||
char *name = buf + snprintf(buf, sizeof(buf), MODULEROOT "/%s/", module);
|
||||
|
||||
// Read props
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system.prop", MODULEROOT, module);
|
||||
strcpy(name, "system.prop");
|
||||
if (access(buf, F_OK) == 0) {
|
||||
LOGI("%s: loading [system.prop]\n", module);
|
||||
load_prop_file(buf, false);
|
||||
}
|
||||
// Copy sepolicy rules
|
||||
strcpy(name, "sepolicy.rule");
|
||||
if (access(MIRRDIR "/persist", F_OK) == 0 && access(buf, F_OK) == 0) {
|
||||
char *p = buf2 + snprintf(buf2, sizeof(buf2), MIRRDIR "/persist/magisk/%s", module);
|
||||
xmkdirs(buf2, 0755);
|
||||
strcpy(p, "/sepolicy.rule");
|
||||
cp_afc(buf, buf2);
|
||||
}
|
||||
|
||||
// Check whether skip mounting
|
||||
snprintf(buf, PATH_MAX, "%s/%s/skip_mount", MODULEROOT, module);
|
||||
strcpy(name, "skip_mount");
|
||||
if (access(buf, F_OK) == 0)
|
||||
continue;
|
||||
// Double check whether the system folder exists
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system", MODULEROOT, module);
|
||||
if (access(buf, F_OK) == -1)
|
||||
strcpy(name, "system");
|
||||
if (access(buf, F_OK) != 0)
|
||||
continue;
|
||||
|
||||
// Construct structure
|
||||
has_modules = true;
|
||||
LOGI("%s: constructing magic mount structure\n", module);
|
||||
// If /system/vendor exists in module, create a link outside
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system/vendor", MODULEROOT, module);
|
||||
strcpy(name, "system/vendor");
|
||||
if (node_entry::vendor_root && access(buf, F_OK) == 0) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s/vendor", MODULEROOT, module);
|
||||
snprintf(buf2, sizeof(buf2), "%s/%s/vendor", MODULEROOT, module);
|
||||
unlink(buf2);
|
||||
xsymlink("./system/vendor", buf2);
|
||||
}
|
||||
// If /system/product exists in module, create a link outside
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system/product", MODULEROOT, module);
|
||||
strcpy(name, "system/product");
|
||||
if (node_entry::product_root && access(buf, F_OK) == 0) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s/product", MODULEROOT, module);
|
||||
snprintf(buf2, sizeof(buf2), "%s/%s/product", MODULEROOT, module);
|
||||
unlink(buf2);
|
||||
xsymlink("./system/product", buf2);
|
||||
}
|
||||
@ -575,15 +574,14 @@ static bool check_data() {
|
||||
}
|
||||
|
||||
void unlock_blocks() {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
int fd, dev, OFF = 0;
|
||||
|
||||
if (!(dir = xopendir("/dev/block")))
|
||||
auto dir = xopen_dir("/dev/block");
|
||||
if (!dir)
|
||||
return;
|
||||
dev = dirfd(dir);
|
||||
dev = dirfd(dir.get());
|
||||
|
||||
while((entry = readdir(dir))) {
|
||||
for (dirent *entry; (entry = readdir(dir.get()));) {
|
||||
if (entry->d_type == DT_BLK) {
|
||||
if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0)
|
||||
continue;
|
||||
@ -592,7 +590,6 @@ void unlock_blocks() {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static bool log_dump = false;
|
||||
@ -664,22 +661,6 @@ void post_fs_data(int client) {
|
||||
unblock_boot_process();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Increment boot count
|
||||
int boot_count = 0;
|
||||
FILE *cf = fopen(BOOTCOUNT, "r");
|
||||
if (cf) {
|
||||
fscanf(cf, "%d", &boot_count);
|
||||
fclose(cf);
|
||||
}
|
||||
boot_count++;
|
||||
if (boot_count > 2)
|
||||
creat(DISABLEFILE, 0644);
|
||||
cf = xfopen(BOOTCOUNT, "w");
|
||||
fprintf(cf, "%d", boot_count);
|
||||
fclose(cf);
|
||||
#endif
|
||||
|
||||
if (!magisk_env()) {
|
||||
LOGE("* Magisk environment setup incomplete, abort\n");
|
||||
unblock_boot_process();
|
||||
@ -760,7 +741,6 @@ void late_start(int client) {
|
||||
|
||||
auto_start_magiskhide();
|
||||
|
||||
// Run scripts after full patch, most reliable way to run scripts
|
||||
LOGI("* Running service.d scripts\n");
|
||||
exec_common_script("service");
|
||||
|
||||
@ -791,11 +771,9 @@ void boot_complete(int client) {
|
||||
rename(MANAGERAPK, "/data/magisk.apk");
|
||||
install_apk("/data/magisk.apk");
|
||||
} else {
|
||||
// Check whether we have a valid manager installed
|
||||
db_strings str;
|
||||
get_db_strings(str, SU_MANAGER);
|
||||
if (validate_manager(str[SU_MANAGER], 0, nullptr)) {
|
||||
// There is no manager installed, install the stub
|
||||
// Check whether we have manager installed
|
||||
if (!check_manager()) {
|
||||
// Install stub
|
||||
exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk");
|
||||
install_apk("/data/magisk.apk");
|
||||
}
|
||||
|
@ -118,10 +118,10 @@ static void main_daemon() {
|
||||
|
||||
// Unmount pre-init patches
|
||||
if (access(ROOTMNT, F_OK) == 0) {
|
||||
file_readline(ROOTMNT, [](auto line) -> bool {
|
||||
file_readline(true, ROOTMNT, [](auto line) -> bool {
|
||||
umount2(line.data(), MNT_DETACH);
|
||||
return true;
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
LOGI(SHOW_VER(Magisk) " daemon started\n");
|
||||
|
@ -246,28 +246,41 @@ int get_uid_policy(su_access &su, int uid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int validate_manager(string &alt_pkg, int userid, struct stat *st) {
|
||||
bool check_manager(string *pkg) {
|
||||
db_strings str;
|
||||
get_db_strings(str, SU_MANAGER);
|
||||
bool ret = validate_manager(str[SU_MANAGER], 0, nullptr);
|
||||
if (pkg) {
|
||||
if (ret)
|
||||
pkg->swap(str[SU_MANAGER]);
|
||||
else
|
||||
*pkg = "xxx"; /* Make sure the return pkg can never exist */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool validate_manager(string &pkg, int userid, struct stat *st) {
|
||||
struct stat tmp_st;
|
||||
if (st == nullptr)
|
||||
st = &tmp_st;
|
||||
|
||||
// Prefer DE storage
|
||||
char app_path[128];
|
||||
sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, alt_pkg.empty() ? "xxx" : alt_pkg.data());
|
||||
if (stat(app_path, st)) {
|
||||
sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, pkg.data());
|
||||
if (pkg.empty() || stat(app_path, st)) {
|
||||
// Check the official package name
|
||||
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, userid);
|
||||
if (stat(app_path, st)) {
|
||||
LOGE("su: cannot find manager");
|
||||
memset(st, 0, sizeof(*st));
|
||||
alt_pkg.clear();
|
||||
return 1;
|
||||
pkg.clear();
|
||||
return false;
|
||||
} else {
|
||||
// Switch to official package if exists
|
||||
alt_pkg = JAVA_PACKAGE_NAME;
|
||||
pkg = JAVA_PACKAGE_NAME;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void exec_sql(int client) {
|
||||
|
@ -26,37 +26,36 @@ void exec_script(const char *script) {
|
||||
|
||||
void exec_common_script(const char *stage) {
|
||||
char path[4096];
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
sprintf(path, SECURE_DIR "/%s.d", stage);
|
||||
if (!(dir = xopendir(path)))
|
||||
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage);
|
||||
auto dir = xopen_dir(path);
|
||||
if (!dir)
|
||||
return;
|
||||
chdir(path);
|
||||
|
||||
bool pfs = strcmp(stage, "post-fs-data") == 0;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
int dfd = dirfd(dir.get());
|
||||
bool pfs = stage == "post-fs-data"sv;
|
||||
*(name++) = '/';
|
||||
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_type == DT_REG) {
|
||||
if (access(entry->d_name, X_OK) == -1)
|
||||
if (faccessat(dfd, entry->d_name, X_OK, 0) != 0)
|
||||
continue;
|
||||
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
|
||||
strcpy(name, entry->d_name);
|
||||
exec_t exec {
|
||||
.pre_exec = set_path,
|
||||
.fork = pfs ? fork_no_zombie : fork_dont_care
|
||||
};
|
||||
if (pfs)
|
||||
exec_command_sync(exec, "/system/bin/sh", entry->d_name);
|
||||
exec_command_sync(exec, "/system/bin/sh", path);
|
||||
else
|
||||
exec_command(exec, "/system/bin/sh", entry->d_name);
|
||||
exec_command(exec, "/system/bin/sh", path);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
chdir("/");
|
||||
}
|
||||
|
||||
void exec_module_script(const char *stage, const vector<string> &module_list) {
|
||||
char path[4096];
|
||||
bool pfs = strcmp(stage, "post-fs-data") == 0;
|
||||
bool pfs = stage == "post-fs-data"sv;
|
||||
for (auto &m : module_list) {
|
||||
const char* module = m.c_str();
|
||||
sprintf(path, MODULEROOT "/%s/%s.sh", module, stage);
|
||||
@ -74,33 +73,6 @@ void exec_module_script(const char *stage, const vector<string> &module_list) {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr char migrate_script[] =
|
||||
"MODULEROOT=" MODULEROOT R"EOF(
|
||||
IMG=%s
|
||||
MNT=/dev/img_mnt
|
||||
e2fsck -yf $IMG
|
||||
mkdir -p $MNT
|
||||
for num in 0 1 2 3 4 5 6 7; do
|
||||
losetup /dev/block/loop${num} $IMG || continue
|
||||
mount -t ext4 /dev/block/loop${num} $MNT
|
||||
rm -rf $MNT/lost+found $MNT/.core
|
||||
magisk --clone $MNT $MODULEROOT
|
||||
umount $MNT
|
||||
rm -rf $MNT
|
||||
losetup -d /dev/block/loop${num}
|
||||
break
|
||||
done
|
||||
rm -rf $IMG
|
||||
)EOF";
|
||||
|
||||
void migrate_img(const char *img) {
|
||||
LOGI("* Migrating %s\n", img);
|
||||
exec_t exec { .pre_exec = set_path };
|
||||
char cmds[sizeof(migrate_script) + 128];
|
||||
sprintf(cmds, migrate_script, img);
|
||||
exec_command_sync(exec, "/system/bin/sh", "-c", cmds);
|
||||
}
|
||||
|
||||
constexpr char install_script[] = R"EOF(
|
||||
APK=%s
|
||||
log -t Magisk "apk_install: $APK"
|
||||
|
@ -68,7 +68,6 @@ void remove_modules();
|
||||
void exec_script(const char *script);
|
||||
void exec_common_script(const char *stage);
|
||||
void exec_module_script(const char *stage, const std::vector<std::string> &module_list);
|
||||
void migrate_img(const char *img);
|
||||
void install_apk(const char *apk);
|
||||
|
||||
/**************
|
||||
|
@ -155,7 +155,8 @@ typedef std::function<bool(db_row&)> db_row_cb;
|
||||
int get_db_settings(db_settings &cfg, int key = -1);
|
||||
int get_db_strings(db_strings &str, int key = -1);
|
||||
int get_uid_policy(su_access &su, int uid);
|
||||
int validate_manager(std::string &alt_pkg, int userid, struct stat *st);
|
||||
bool check_manager(std::string *pkg = nullptr);
|
||||
bool validate_manager(std::string &pkg, int userid, struct stat *st);
|
||||
void exec_sql(int client);
|
||||
char *db_exec(const char *sql);
|
||||
char *db_exec(const char *sql, const db_row_cb &fn);
|
||||
|
@ -1,6 +1,3 @@
|
||||
/* magiskpolicy.h - Public API for policy patching
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -35,3 +32,8 @@ int sepol_exists(const char *source);
|
||||
|
||||
// Built in rules
|
||||
void sepol_magisk_rules();
|
||||
|
||||
// Statement parsing
|
||||
void parse_statement(const char *statement);
|
||||
void load_rule_file(const char *file);
|
||||
void statement_help();
|
@ -112,7 +112,7 @@ void load_kernel_info(cmdline *cmd) {
|
||||
cmd->slot[0] = '_';
|
||||
strcpy(cmd->slot + 1, value);
|
||||
} else if (key == "skip_initramfs") {
|
||||
cmd->system_as_root = true;
|
||||
cmd->skip_initramfs = true;
|
||||
} else if (key == "androidboot.force_normal_boot") {
|
||||
cmd->force_normal_boot = value[0] == '1';
|
||||
} else if (key == "androidboot.android_dt_dir") {
|
||||
@ -143,13 +143,13 @@ void load_kernel_info(cmdline *cmd) {
|
||||
|
||||
if (recovery_mode) {
|
||||
LOGD("Running in recovery mode, waiting for key...\n");
|
||||
cmd->system_as_root = !check_key_combo();
|
||||
cmd->skip_initramfs = !check_key_combo();
|
||||
}
|
||||
|
||||
if (cmd->dt_dir[0] == '\0')
|
||||
strcpy(cmd->dt_dir, DEFAULT_DT_DIR);
|
||||
|
||||
LOGD("system_as_root=[%d]\n", cmd->system_as_root);
|
||||
LOGD("skip_initramfs=[%d]\n", cmd->skip_initramfs);
|
||||
LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot);
|
||||
LOGD("slot=[%s]\n", cmd->slot);
|
||||
LOGD("dt_dir=[%s]\n", cmd->dt_dir);
|
||||
|
@ -147,11 +147,11 @@ class TestInit : public BaseInit {
|
||||
public:
|
||||
TestInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
|
||||
void start() override {
|
||||
// Write init tests here
|
||||
// Place init tests here
|
||||
}
|
||||
};
|
||||
|
||||
static void setup_test(const char *dir) {
|
||||
[[maybe_unused]] static int test_main(int argc, char *argv[]) {
|
||||
// Log to console
|
||||
cmdline_logging();
|
||||
log_cb.ex = nop_ex;
|
||||
@ -167,13 +167,21 @@ static void setup_test(const char *dir) {
|
||||
mounts.emplace_back(me->mnt_dir);
|
||||
return true;
|
||||
});
|
||||
for (auto m = mounts.rbegin(); m != mounts.rend(); ++m)
|
||||
xumount(m->data());
|
||||
for (auto &m : reversed(mounts))
|
||||
xumount(m.data());
|
||||
|
||||
// chroot jail
|
||||
chdir(dir);
|
||||
chdir(dirname(argv[0]));
|
||||
chroot(".");
|
||||
chdir("/");
|
||||
|
||||
cmdline cmd{};
|
||||
load_kernel_info(&cmd);
|
||||
|
||||
auto init = make_unique<TestInit>(argv, &cmd);
|
||||
init->start();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@ -184,44 +192,34 @@ int main(int argc, char *argv[]) {
|
||||
return (*init_applet_main[i])(argc, argv);
|
||||
}
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "-x") == 0) {
|
||||
if (strcmp(argv[2], "magisk") == 0)
|
||||
#ifdef MAGISK_DEBUG
|
||||
if (getenv("INIT_TEST") != nullptr)
|
||||
return test_main(argc, argv);
|
||||
#endif
|
||||
|
||||
if (argc > 1 && argv[1] == "-x"sv) {
|
||||
if (argv[2] == "magisk"sv)
|
||||
return dump_magisk(argv[3], 0755);
|
||||
else if (strcmp(argv[2], "manager") == 0)
|
||||
else if (argv[2] == "manager"sv)
|
||||
return dump_manager(argv[3], 0644);
|
||||
}
|
||||
|
||||
if (argc > 1 && argv[1] == "selinux_setup"sv) {
|
||||
auto init = make_unique<SecondStageInit>(argv);
|
||||
init->start();
|
||||
}
|
||||
|
||||
#ifdef MAGISK_DEBUG
|
||||
bool run_test = getenv("INIT_TEST") != nullptr;
|
||||
#else
|
||||
constexpr bool run_test = false;
|
||||
#endif
|
||||
|
||||
if (run_test) {
|
||||
setup_test(dirname(argv[0]));
|
||||
} else {
|
||||
if (getpid() != 1)
|
||||
return 1;
|
||||
setup_klog();
|
||||
}
|
||||
|
||||
cmdline cmd{};
|
||||
load_kernel_info(&cmd);
|
||||
|
||||
unique_ptr<BaseInit> init;
|
||||
if (run_test) {
|
||||
init = make_unique<TestInit>(argv, &cmd);
|
||||
} else if (cmd.force_normal_boot) {
|
||||
cmdline cmd{};
|
||||
|
||||
if (argc > 1 && argv[1] == "selinux_setup"sv) {
|
||||
init = make_unique<SecondStageInit>(argv);
|
||||
} else {
|
||||
// This will also mount /sys and /proc
|
||||
load_kernel_info(&cmd);
|
||||
|
||||
if (cmd.force_normal_boot) {
|
||||
init = make_unique<ABFirstStageInit>(argv, &cmd);
|
||||
} else if (cmd.system_as_root) {
|
||||
if (access("/overlay", F_OK) == 0) /* Compatible mode */
|
||||
init = make_unique<SARCompatInit>(argv, &cmd);
|
||||
else
|
||||
} else if (cmd.skip_initramfs) {
|
||||
init = make_unique<SARInit>(argv, &cmd);
|
||||
} else {
|
||||
decompress_ramdisk();
|
||||
@ -232,6 +230,7 @@ int main(int argc, char *argv[]) {
|
||||
else
|
||||
init = make_unique<RootFSInit>(argv, &cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the main routine
|
||||
init->start();
|
||||
|
@ -3,8 +3,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
#include <magisk.h>
|
||||
|
||||
struct cmdline {
|
||||
bool system_as_root;
|
||||
bool skip_initramfs;
|
||||
bool force_normal_boot;
|
||||
char slot[3];
|
||||
char dt_dir[128];
|
||||
@ -45,7 +47,7 @@ protected:
|
||||
virtual void cleanup();
|
||||
public:
|
||||
BaseInit(char *argv[], cmdline *cmd) :
|
||||
cmd(cmd), argv(argv), mount_list{"/sys", "/proc", "/dev"} {}
|
||||
cmd(cmd), argv(argv), mount_list{"/sys", "/proc"} {}
|
||||
virtual ~BaseInit() = default;
|
||||
virtual void start() = 0;
|
||||
};
|
||||
@ -53,28 +55,14 @@ public:
|
||||
class MagiskInit : public BaseInit {
|
||||
protected:
|
||||
raw_data self;
|
||||
const char *persist_dir;
|
||||
|
||||
virtual void early_mount() = 0;
|
||||
bool read_dt_fstab(const char *name, char *partname, char *fstype);
|
||||
bool patch_sepolicy(const char *file = "/sepolicy");
|
||||
public:
|
||||
MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
|
||||
};
|
||||
|
||||
class RootFSBase : public MagiskInit {
|
||||
protected:
|
||||
int root = -1;
|
||||
|
||||
virtual void setup_rootfs();
|
||||
public:
|
||||
RootFSBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {};
|
||||
void start() override {
|
||||
early_mount();
|
||||
setup_rootfs();
|
||||
exec_init();
|
||||
}
|
||||
};
|
||||
|
||||
class SARBase : public MagiskInit {
|
||||
protected:
|
||||
raw_data config;
|
||||
@ -83,7 +71,9 @@ protected:
|
||||
void backup_files();
|
||||
void patch_rootdir();
|
||||
public:
|
||||
SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {};
|
||||
SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {
|
||||
persist_dir = MIRRDIR "/persist/magisk";
|
||||
}
|
||||
void start() override {
|
||||
early_mount();
|
||||
patch_rootdir();
|
||||
@ -96,7 +86,7 @@ public:
|
||||
* *************/
|
||||
|
||||
class ABFirstStageInit : public BaseInit {
|
||||
protected:
|
||||
private:
|
||||
void prepare();
|
||||
public:
|
||||
ABFirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
|
||||
@ -107,7 +97,7 @@ public:
|
||||
};
|
||||
|
||||
class AFirstStageInit : public BaseInit {
|
||||
protected:
|
||||
private:
|
||||
void prepare();
|
||||
public:
|
||||
AFirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
|
||||
@ -140,26 +130,26 @@ public:
|
||||
* Initramfs
|
||||
* **********/
|
||||
|
||||
class RootFSInit : public RootFSBase {
|
||||
class RootFSInit : public MagiskInit {
|
||||
private:
|
||||
int root = -1;
|
||||
void setup_rootfs();
|
||||
protected:
|
||||
void early_mount() override;
|
||||
public:
|
||||
RootFSInit(char *argv[], cmdline *cmd) : RootFSBase(argv, cmd) {};
|
||||
};
|
||||
RootFSInit(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {
|
||||
persist_dir = "/dev/.magisk/mirror/persist/magisk";
|
||||
}
|
||||
|
||||
/* ****************
|
||||
* Compat-mode SAR
|
||||
* ****************/
|
||||
|
||||
class SARCompatInit : public RootFSBase {
|
||||
protected:
|
||||
void early_mount() override;
|
||||
void setup_rootfs() override;
|
||||
public:
|
||||
SARCompatInit(char *argv[], cmdline *cmd) : RootFSBase(argv, cmd) {};
|
||||
void start() override {
|
||||
early_mount();
|
||||
setup_rootfs();
|
||||
exec_init();
|
||||
}
|
||||
};
|
||||
|
||||
void load_kernel_info(cmdline *cmd);
|
||||
int dump_magisk(const char *path, mode_t mode);
|
||||
int magisk_proxy_main(int argc, char *argv[]);
|
||||
void setup_klog();
|
||||
void mount_sbin();
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <utils.h>
|
||||
#include <logging.h>
|
||||
#include <selinux.h>
|
||||
#include <magisk.h>
|
||||
|
||||
#include "init.h"
|
||||
|
||||
@ -20,13 +21,17 @@ struct devinfo {
|
||||
|
||||
static vector<devinfo> dev_list;
|
||||
|
||||
static char partname[32];
|
||||
static char fstype[32];
|
||||
static char block_dev[64];
|
||||
|
||||
static void parse_device(devinfo *dev, const char *uevent) {
|
||||
dev->partname[0] = '\0';
|
||||
parse_prop_file(uevent, [=](string_view key, string_view value) -> bool {
|
||||
if (key == "MAJOR")
|
||||
dev->major = atoi(value.data());
|
||||
dev->major = parse_int(value.data());
|
||||
else if (key == "MINOR")
|
||||
dev->minor = atoi(value.data());
|
||||
dev->minor = parse_int(value.data());
|
||||
else if (key == "DEVNAME")
|
||||
strcpy(dev->devname, value.data());
|
||||
else if (key == "PARTNAME")
|
||||
@ -38,35 +43,33 @@ static void parse_device(devinfo *dev, const char *uevent) {
|
||||
|
||||
static void collect_devices() {
|
||||
char path[128];
|
||||
struct dirent *entry;
|
||||
devinfo dev;
|
||||
DIR *dir = xopendir("/sys/dev/block");
|
||||
if (dir == nullptr)
|
||||
return;
|
||||
while ((entry = readdir(dir))) {
|
||||
devinfo dev{};
|
||||
if (auto dir = xopen_dir("/sys/dev/block"); dir) {
|
||||
for (dirent *entry; (entry = readdir(dir.get()));) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
|
||||
parse_device(&dev, path);
|
||||
dev_list.push_back(dev);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
static dev_t setup_block(const char *partname, char *block_dev = nullptr) {
|
||||
static int64_t setup_block(bool write_block = true) {
|
||||
if (dev_list.empty())
|
||||
collect_devices();
|
||||
for (;;) {
|
||||
xmkdir("/dev", 0755);
|
||||
xmkdir("/dev/block", 0755);
|
||||
|
||||
for (int tries = 0; tries < 3; ++tries) {
|
||||
for (auto &dev : dev_list) {
|
||||
if (strcasecmp(dev.partname, partname) == 0) {
|
||||
xmkdir("/dev", 0755);
|
||||
if (block_dev) {
|
||||
if (write_block) {
|
||||
sprintf(block_dev, "/dev/block/%s", dev.devname);
|
||||
xmkdir("/dev/block", 0755);
|
||||
}
|
||||
LOGD("Found %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor);
|
||||
dev_t rdev = makedev(dev.major, dev.minor);
|
||||
mknod(block_dev ? block_dev : "/dev/root", S_IFBLK | 0600, rdev);
|
||||
mknod(block_dev, S_IFBLK | 0600, rdev);
|
||||
return rdev;
|
||||
}
|
||||
}
|
||||
@ -75,6 +78,9 @@ static dev_t setup_block(const char *partname, char *block_dev = nullptr) {
|
||||
dev_list.clear();
|
||||
collect_devices();
|
||||
}
|
||||
|
||||
// The requested partname does not exist
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool is_lnk(const char *name) {
|
||||
@ -84,15 +90,7 @@ static bool is_lnk(const char *name) {
|
||||
return S_ISLNK(st.st_mode);
|
||||
}
|
||||
|
||||
void BaseInit::cleanup() {
|
||||
// Unmount in reverse order
|
||||
for (auto &p : reversed(mount_list)) {
|
||||
LOGD("Unmount [%s]\n", p.data());
|
||||
umount(p.data());
|
||||
}
|
||||
}
|
||||
|
||||
bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) {
|
||||
static bool read_dt_fstab(cmdline *cmd, const char *name) {
|
||||
char path[128];
|
||||
int fd;
|
||||
sprintf(path, "%s/fstab/%s/dev", cmd->dt_dir, name);
|
||||
@ -112,18 +110,10 @@ bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static char partname[32];
|
||||
static char fstype[32];
|
||||
static char block_dev[64];
|
||||
|
||||
#define link_root(name) \
|
||||
if (is_lnk("/system_root" name)) \
|
||||
cp_afc("/system_root" name, name)
|
||||
|
||||
#define mount_root(name) \
|
||||
if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \
|
||||
if (!is_lnk("/" #name) && read_dt_fstab(cmd, #name)) { \
|
||||
LOGD("Early mount " #name "\n"); \
|
||||
setup_block(partname, block_dev); \
|
||||
setup_block(); \
|
||||
xmkdir("/" #name, 0755); \
|
||||
xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \
|
||||
mount_list.emplace_back("/" #name); \
|
||||
@ -136,36 +126,19 @@ void RootFSInit::early_mount() {
|
||||
root = xopen("/", O_RDONLY | O_CLOEXEC);
|
||||
rename("/.backup/init", "/init");
|
||||
|
||||
// Mount sbin overlay for persist, but move it and add to cleanup list
|
||||
mount_sbin();
|
||||
xmount("/sbin", "/dev", nullptr, MS_MOVE, nullptr);
|
||||
mount_list.emplace_back("/dev");
|
||||
mount_list.emplace_back("/dev/.magisk/mirror/persist");
|
||||
mount_list.emplace_back("/dev/.magisk/mirror/cache");
|
||||
|
||||
mount_root(system);
|
||||
mount_root(vendor);
|
||||
mount_root(product);
|
||||
mount_root(odm);
|
||||
}
|
||||
|
||||
void SARCompatInit::early_mount() {
|
||||
full_read("/init", self.buf, self.sz);
|
||||
|
||||
LOGD("Cleaning rootfs\n");
|
||||
root = xopen("/", O_RDONLY | O_CLOEXEC);
|
||||
frm_rf(root, { ".backup", "overlay", "overlay.d", "proc", "sys" });
|
||||
|
||||
LOGD("Early mount system_root\n");
|
||||
sprintf(partname, "system%s", cmd->slot);
|
||||
setup_block(partname, block_dev);
|
||||
xmkdir("/system_root", 0755);
|
||||
if (xmount(block_dev, "/system_root", "ext4", MS_RDONLY, nullptr))
|
||||
xmount(block_dev, "/system_root", "erofs", MS_RDONLY, nullptr);
|
||||
xmkdir("/system", 0755);
|
||||
xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr);
|
||||
|
||||
link_root("/vendor");
|
||||
link_root("/product");
|
||||
link_root("/odm");
|
||||
mount_root(vendor);
|
||||
mount_root(product);
|
||||
mount_root(odm);
|
||||
}
|
||||
|
||||
static void switch_root(const string &path) {
|
||||
LOGD("Switch root to %s\n", path.data());
|
||||
vector<string> mounts;
|
||||
@ -203,6 +176,7 @@ void SARInit::early_mount() {
|
||||
// Make dev writable
|
||||
xmkdir("/dev", 0755);
|
||||
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
|
||||
mount_list.emplace_back("/dev");
|
||||
|
||||
backup_files();
|
||||
|
||||
@ -213,7 +187,19 @@ void SARInit::early_mount() {
|
||||
|
||||
LOGD("Early mount system_root\n");
|
||||
sprintf(partname, "system%s", cmd->slot);
|
||||
system_dev = setup_block(partname);
|
||||
strcpy(block_dev, "/dev/root");
|
||||
auto dev = setup_block(false);
|
||||
if (dev < 0) {
|
||||
// Try NVIDIA naming scheme
|
||||
strcpy(partname, "APP");
|
||||
dev = setup_block();
|
||||
if (dev < 0) {
|
||||
// We don't really know what to do at this point...
|
||||
LOGE("Cannot find root partition, abort\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
system_dev = dev;
|
||||
xmkdir("/system_root", 0755);
|
||||
if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr))
|
||||
xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr);
|
||||
@ -245,3 +231,42 @@ void SecondStageInit::early_mount() {
|
||||
|
||||
switch_root("/system_root");
|
||||
}
|
||||
|
||||
void BaseInit::cleanup() {
|
||||
// Unmount in reverse order
|
||||
for (auto &p : reversed(mount_list)) {
|
||||
if (xumount(p.data()) == 0)
|
||||
LOGD("Unmount [%s]\n", p.data());
|
||||
}
|
||||
mount_list.clear();
|
||||
mount_list.shrink_to_fit();
|
||||
}
|
||||
|
||||
void mount_sbin() {
|
||||
LOGD("Mount /sbin tmpfs overlay\n");
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755");
|
||||
|
||||
xmkdir(MAGISKTMP, 0755);
|
||||
xmkdir(MIRRDIR, 0);
|
||||
xmkdir(BLOCKDIR, 0);
|
||||
|
||||
// Mount persist partition
|
||||
strcpy(partname, "persist");
|
||||
strcpy(block_dev, BLOCKDIR "/persist");
|
||||
const char *mnt_point = MIRRDIR "/persist";
|
||||
if (setup_block(false) < 0) {
|
||||
// Fallback to cache
|
||||
strcpy(partname, "cache");
|
||||
strcpy(block_dev, BLOCKDIR "/cache");
|
||||
if (setup_block(false) < 0) {
|
||||
// Try NVIDIA's BS
|
||||
strcpy(partname, "CAC");
|
||||
if (setup_block(false) < 0)
|
||||
return;
|
||||
}
|
||||
mnt_point = MIRRDIR "/cache";
|
||||
xsymlink("./cache", MIRRDIR "/persist");
|
||||
}
|
||||
xmkdir(mnt_point, 0755);
|
||||
xmount(block_dev, mnt_point, "ext4", 0, nullptr);
|
||||
}
|
@ -24,7 +24,7 @@ static void patch_socket_name(const char *path) {
|
||||
mmap_rw(path, buf, size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
|
||||
gen_rand_str(buf + i, sizeof(MAIN_SOCKET));
|
||||
gen_rand_str(buf + i, 16);
|
||||
i += sizeof(MAIN_SOCKET);
|
||||
}
|
||||
}
|
||||
@ -85,7 +85,7 @@ static void load_overlay_rc(int dirfd) {
|
||||
rewinddir(dir);
|
||||
}
|
||||
|
||||
void RootFSBase::setup_rootfs() {
|
||||
void RootFSInit::setup_rootfs() {
|
||||
if (patch_sepolicy()) {
|
||||
char *addr;
|
||||
size_t size;
|
||||
@ -101,17 +101,8 @@ void RootFSBase::setup_rootfs() {
|
||||
munmap(addr, size);
|
||||
}
|
||||
|
||||
// Handle legacy overlays
|
||||
int fd = open("/overlay", O_RDONLY | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
LOGD("Merge overlay folder\n");
|
||||
mv_dir(fd, root);
|
||||
close(fd);
|
||||
rmdir("/overlay");
|
||||
}
|
||||
|
||||
// Handle overlays
|
||||
fd = open("/overlay.d", O_RDONLY | O_CLOEXEC);
|
||||
int fd = open("/overlay.d", O_RDONLY | O_CLOEXEC);
|
||||
if (fd >= 0) {
|
||||
LOGD("Merge overlay.d\n");
|
||||
load_overlay_rc(fd);
|
||||
@ -141,16 +132,6 @@ void RootFSBase::setup_rootfs() {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void SARCompatInit::setup_rootfs() {
|
||||
// Clone rootfs
|
||||
LOGD("Clone root dir from system to rootfs\n");
|
||||
int system_root = xopen("/system_root", O_RDONLY | O_CLOEXEC);
|
||||
clone_dir(system_root, root, false);
|
||||
close(system_root);
|
||||
|
||||
RootFSBase::setup_rootfs();
|
||||
}
|
||||
|
||||
bool MagiskInit::patch_sepolicy(const char *file) {
|
||||
bool patch_init = false;
|
||||
|
||||
@ -174,7 +155,23 @@ bool MagiskInit::patch_sepolicy(const char *file) {
|
||||
|
||||
sepol_magisk_rules();
|
||||
sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL);
|
||||
|
||||
// Custom rules
|
||||
if (auto dir = xopen_dir(persist_dir); dir) {
|
||||
char path[4096];
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
snprintf(path, sizeof(path), "%s/%s/sepolicy.rule", persist_dir, entry->d_name);
|
||||
if (access(path, R_OK) == 0) {
|
||||
LOGD("Loading custom sepolicy patch: %s\n", path);
|
||||
load_rule_file(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dump_policydb(file);
|
||||
destroy_policydb();
|
||||
|
||||
// Remove OnePlus stupid debug sepolicy and use our own
|
||||
if (access("/sepolicy_debug", F_OK) == 0) {
|
||||
@ -192,8 +189,7 @@ constexpr const char wrapper[] =
|
||||
;
|
||||
|
||||
static void sbin_overlay(const raw_data &self, const raw_data &config) {
|
||||
LOGD("Mount /sbin tmpfs overlay\n");
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755");
|
||||
mount_sbin();
|
||||
|
||||
// Dump binaries
|
||||
xmkdir(MAGISKTMP, 0755);
|
||||
@ -225,6 +221,48 @@ static void sbin_overlay(const raw_data &self, const raw_data &config) {
|
||||
xsymlink("./magiskinit", "/sbin/supolicy");
|
||||
}
|
||||
|
||||
static void recreate_sbin(const char *mirror) {
|
||||
int src = xopen(mirror, O_RDONLY | O_CLOEXEC);
|
||||
int dest = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
DIR *fp = fdopendir(src);
|
||||
char buf[256];
|
||||
bool use_bind_mount = true;
|
||||
for (dirent *entry; (entry = xreaddir(fp));) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
struct stat st;
|
||||
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
|
||||
xsymlinkat(buf, dest, entry->d_name);
|
||||
} else {
|
||||
char sbin_path[256];
|
||||
sprintf(buf, "%s/%s", mirror, entry->d_name);
|
||||
sprintf(sbin_path, "/sbin/%s", entry->d_name);
|
||||
|
||||
if (use_bind_mount) {
|
||||
// Create dummy
|
||||
if (S_ISDIR(st.st_mode))
|
||||
xmkdir(sbin_path, st.st_mode & 0777);
|
||||
else
|
||||
close(xopen(sbin_path, O_CREAT | O_WRONLY | O_CLOEXEC, st.st_mode & 0777));
|
||||
|
||||
if (xmount(buf, sbin_path, nullptr, MS_BIND, nullptr)) {
|
||||
// Bind mount failed, fallback to symlink
|
||||
remove(sbin_path);
|
||||
use_bind_mount = false;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
xsymlink(buf, sbin_path);
|
||||
}
|
||||
}
|
||||
close(src);
|
||||
close(dest);
|
||||
}
|
||||
|
||||
#define ROOTMIR MIRRDIR "/system_root"
|
||||
#define ROOTBLK BLOCKDIR "/system_root"
|
||||
#define MONOPOLICY "/sepolicy"
|
||||
@ -260,47 +298,19 @@ void SARBase::patch_rootdir() {
|
||||
sbin_overlay(self, config);
|
||||
|
||||
// Mount system_root mirror
|
||||
xmkdir(MIRRDIR, 0);
|
||||
xmkdir(ROOTMIR, 0);
|
||||
xmkdir(BLOCKDIR, 0);
|
||||
xmkdir(ROOTMIR, 0755);
|
||||
mknod(ROOTBLK, S_IFBLK | 0600, system_dev);
|
||||
if (xmount(ROOTBLK, ROOTMIR, "ext4", MS_RDONLY, nullptr))
|
||||
xmount(ROOTBLK, ROOTMIR, "erofs", MS_RDONLY, nullptr);
|
||||
|
||||
// Recreate original sbin structure
|
||||
int src = xopen(ROOTMIR "/sbin", O_RDONLY | O_CLOEXEC);
|
||||
int dest = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
DIR *fp = fdopendir(src);
|
||||
struct dirent *entry;
|
||||
struct stat st;
|
||||
char buf[256];
|
||||
while ((entry = xreaddir(fp))) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
|
||||
xsymlinkat(buf, dest, entry->d_name);
|
||||
} else {
|
||||
char spath[256];
|
||||
sprintf(buf, "/sbin/%s", entry->d_name);
|
||||
sprintf(spath, ROOTMIR "/sbin/%s", entry->d_name);
|
||||
// Create dummy
|
||||
if (S_ISDIR(st.st_mode))
|
||||
xmkdir(buf, st.st_mode & 0777);
|
||||
else
|
||||
close(xopen(buf, O_CREAT | O_WRONLY | O_CLOEXEC, st.st_mode & 0777));
|
||||
xmount(spath, buf, nullptr, MS_BIND, nullptr);
|
||||
}
|
||||
}
|
||||
close(src);
|
||||
close(dest);
|
||||
recreate_sbin(ROOTMIR "/sbin");
|
||||
|
||||
// Patch init
|
||||
raw_data init;
|
||||
file_attr attr;
|
||||
bool redirect = false;
|
||||
src = xopen("/init", O_RDONLY | O_CLOEXEC);
|
||||
int src = xopen("/init", O_RDONLY | O_CLOEXEC);
|
||||
fd_full_read(src, init.buf, init.sz);
|
||||
fgetattr(src, &attr);
|
||||
close(src);
|
||||
@ -320,7 +330,7 @@ void SARBase::patch_rootdir() {
|
||||
}
|
||||
}
|
||||
xmkdir(ROOTOVL, 0);
|
||||
dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC);
|
||||
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC);
|
||||
xwrite(dest, init.buf, init.sz);
|
||||
fsetattr(dest, &attr);
|
||||
close(dest);
|
||||
@ -423,17 +433,16 @@ static void patch_fstab(const string &fstab) {
|
||||
#define FSR "/first_stage_ramdisk"
|
||||
|
||||
void ABFirstStageInit::prepare() {
|
||||
DIR *dir = xopendir(FSR);
|
||||
auto dir = xopen_dir(FSR);
|
||||
if (!dir)
|
||||
return;
|
||||
string fstab(FSR "/");
|
||||
for (dirent *de; (de = readdir(dir));) {
|
||||
for (dirent *de; (de = xreaddir(dir.get()));) {
|
||||
if (strstr(de->d_name, "fstab")) {
|
||||
fstab += de->d_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
if (fstab.length() == sizeof(FSR))
|
||||
return;
|
||||
|
||||
@ -450,14 +459,13 @@ void ABFirstStageInit::prepare() {
|
||||
}
|
||||
|
||||
void AFirstStageInit::prepare() {
|
||||
DIR *dir = xopendir("/");
|
||||
for (dirent *de; (de = readdir(dir));) {
|
||||
auto dir = xopen_dir("/");
|
||||
for (dirent *de; (de = xreaddir(dir.get()));) {
|
||||
if (strstr(de->d_name, "fstab")) {
|
||||
patch_fstab(de->d_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
// Move stuffs for next stage
|
||||
xmkdir("/system", 0755);
|
||||
@ -483,18 +491,7 @@ int magisk_proxy_main(int argc, char *argv[]) {
|
||||
sbin_overlay(self, config);
|
||||
|
||||
// Create symlinks pointing back to /root
|
||||
char path[256];
|
||||
int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
DIR *dir = xopendir("/root");
|
||||
struct dirent *entry;
|
||||
while((entry = xreaddir(dir))) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
sprintf(path, "/root/%s", entry->d_name);
|
||||
xsymlinkat(path, sbin, entry->d_name);
|
||||
}
|
||||
close(sbin);
|
||||
closedir(dir);
|
||||
recreate_sbin("/root");
|
||||
|
||||
setenv("REMOUNT_ROOT", "1", 1);
|
||||
execv("/sbin/magisk", argv);
|
||||
|
@ -22,14 +22,14 @@ uint32_t dyn_img_hdr::j32 = 0;
|
||||
uint64_t dyn_img_hdr::j64 = 0;
|
||||
|
||||
static void decompress(format_t type, int fd, const void *in, size_t size) {
|
||||
auto ptr = get_decoder(type, make_stream<fd_stream>(fd));
|
||||
auto ptr = get_decoder(type, make_unique<fd_stream>(fd));
|
||||
ptr->write(in, size);
|
||||
}
|
||||
|
||||
static off_t compress(format_t type, int fd, const void *in, size_t size) {
|
||||
auto prev = lseek(fd, 0, SEEK_CUR);
|
||||
{
|
||||
auto strm = get_encoder(type, make_stream<fd_stream>(fd));
|
||||
auto strm = get_encoder(type, make_unique<fd_stream>(fd));
|
||||
strm->write(in, size);
|
||||
}
|
||||
auto now = lseek(fd, 0, SEEK_CUR);
|
||||
@ -282,33 +282,61 @@ void boot_img::parse_image(uint8_t *addr) {
|
||||
fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt2name[r_fmt]);
|
||||
}
|
||||
|
||||
void boot_img::find_kernel_dtb() {
|
||||
const int eof = static_cast<int>(hdr->kernel_size());
|
||||
for (int i = 0; i < eof - (int) sizeof(fdt_header); ++i) {
|
||||
auto fdt_hdr = reinterpret_cast<fdt_header *>(kernel + i);
|
||||
static int find_dtb_offset(uint8_t *buf, int sz) {
|
||||
for (int off = 0; off < sz - (int) sizeof(fdt_header); ++off) {
|
||||
auto fdt_hdr = reinterpret_cast<fdt_header *>(buf + off);
|
||||
if (fdt32_to_cpu(fdt_hdr->magic) != FDT_MAGIC)
|
||||
continue;
|
||||
|
||||
// Check that fdt_header.totalsize does not overflow kernel image size
|
||||
uint32_t totalsize = fdt32_to_cpu(fdt_hdr->totalsize);
|
||||
if (totalsize + i > eof)
|
||||
if (totalsize + off > sz)
|
||||
continue;
|
||||
|
||||
// Check that fdt_header.off_dt_struct does not overflow kernel image size
|
||||
uint32_t off_dt_struct = fdt32_to_cpu(fdt_hdr->off_dt_struct);
|
||||
if (off_dt_struct + i > eof)
|
||||
if (off_dt_struct + off > sz)
|
||||
continue;
|
||||
|
||||
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
|
||||
auto fdt_node_hdr = reinterpret_cast<fdt_node_header *>(kernel + i + off_dt_struct);
|
||||
auto fdt_node_hdr = reinterpret_cast<fdt_node_header *>(buf + off + off_dt_struct);
|
||||
if (fdt32_to_cpu(fdt_node_hdr->tag) != FDT_BEGIN_NODE)
|
||||
continue;
|
||||
|
||||
kernel_dtb = kernel + i;
|
||||
kernel_dt_size = eof - i;
|
||||
hdr->kernel_size() = i;
|
||||
return off;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void boot_img::find_kernel_dtb() {
|
||||
if (int off = find_dtb_offset(kernel, hdr->kernel_size()); off > 0) {
|
||||
kernel_dtb = kernel + off;
|
||||
kernel_dt_size = hdr->kernel_size() - off;
|
||||
hdr->kernel_size() = off;
|
||||
fprintf(stderr, "KERNEL_DTB [%u]\n", kernel_dt_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int split_image_dtb(const char *filename) {
|
||||
uint8_t *buf;
|
||||
size_t sz;
|
||||
mmap_ro(filename, buf, sz);
|
||||
run_finally f([=]{ munmap(buf, sz); });
|
||||
|
||||
if (int off = find_dtb_offset(buf, sz); off > 0) {
|
||||
format_t fmt = check_fmt(buf, sz);
|
||||
if (COMPRESSED(fmt)) {
|
||||
int fd = creat(KERNEL_FILE, 0644);
|
||||
decompress(fmt, fd, buf, off);
|
||||
close(fd);
|
||||
} else {
|
||||
dump(buf, off, KERNEL_FILE);
|
||||
}
|
||||
dump(buf + off, sz - off, KER_DTB_FILE);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Cannot find DTB in %s\n", filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ protected:
|
||||
ENCODE
|
||||
} mode;
|
||||
|
||||
gz_strm(mode_t mode, sFILE &&fp) : cpr_stream(std::move(fp)), mode(mode) {
|
||||
gz_strm(mode_t mode, stream_ptr &&base) : cpr_stream(std::move(base)), mode(mode) {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
inflateInit2(&strm, 15 | 16);
|
||||
@ -99,12 +99,12 @@ private:
|
||||
|
||||
class gz_decoder : public gz_strm {
|
||||
public:
|
||||
explicit gz_decoder(sFILE &&fp) : gz_strm(DECODE, std::move(fp)) {};
|
||||
explicit gz_decoder(stream_ptr &&base) : gz_strm(DECODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class gz_encoder : public gz_strm {
|
||||
public:
|
||||
explicit gz_encoder(sFILE &&fp) : gz_strm(ENCODE, std::move(fp)) {};
|
||||
explicit gz_encoder(stream_ptr &&base) : gz_strm(ENCODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class bz_strm : public cpr_stream {
|
||||
@ -131,7 +131,7 @@ protected:
|
||||
ENCODE
|
||||
} mode;
|
||||
|
||||
bz_strm(mode_t mode, sFILE &&fp) : cpr_stream(std::move(fp)), mode(mode) {
|
||||
bz_strm(mode_t mode, stream_ptr &&base) : cpr_stream(std::move(base)), mode(mode) {
|
||||
switch(mode) {
|
||||
case DECODE:
|
||||
BZ2_bzDecompressInit(&strm, 0, 0);
|
||||
@ -173,12 +173,12 @@ private:
|
||||
|
||||
class bz_decoder : public bz_strm {
|
||||
public:
|
||||
explicit bz_decoder(sFILE &&fp) : bz_strm(DECODE, std::move(fp)) {};
|
||||
explicit bz_decoder(stream_ptr &&base) : bz_strm(DECODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class bz_encoder : public bz_strm {
|
||||
public:
|
||||
explicit bz_encoder(sFILE &&fp) : bz_strm(ENCODE, std::move(fp)) {};
|
||||
explicit bz_encoder(stream_ptr &&base) : bz_strm(ENCODE, std::move(base)) {};
|
||||
};
|
||||
|
||||
class lzma_strm : public cpr_stream {
|
||||
@ -199,8 +199,8 @@ protected:
|
||||
ENCODE_LZMA
|
||||
} mode;
|
||||
|
||||
lzma_strm(mode_t mode, sFILE &&fp)
|
||||
: cpr_stream(std::move(fp)), mode(mode), strm(LZMA_STREAM_INIT) {
|
||||
lzma_strm(mode_t mode, stream_ptr &&base)
|
||||
: cpr_stream(std::move(base)), mode(mode), strm(LZMA_STREAM_INIT) {
|
||||
lzma_options_lzma opt;
|
||||
|
||||
// Initialize preset
|
||||
@ -247,22 +247,22 @@ private:
|
||||
|
||||
class lzma_decoder : public lzma_strm {
|
||||
public:
|
||||
explicit lzma_decoder(sFILE &&fp) : lzma_strm(DECODE, std::move(fp)) {}
|
||||
explicit lzma_decoder(stream_ptr &&base) : lzma_strm(DECODE, std::move(base)) {}
|
||||
};
|
||||
|
||||
class xz_encoder : public lzma_strm {
|
||||
public:
|
||||
explicit xz_encoder(sFILE &&fp) : lzma_strm(ENCODE_XZ, std::move(fp)) {}
|
||||
explicit xz_encoder(stream_ptr &&base) : lzma_strm(ENCODE_XZ, std::move(base)) {}
|
||||
};
|
||||
|
||||
class lzma_encoder : public lzma_strm {
|
||||
public:
|
||||
explicit lzma_encoder(sFILE &&fp) : lzma_strm(ENCODE_LZMA, std::move(fp)) {}
|
||||
explicit lzma_encoder(stream_ptr &&base) : lzma_strm(ENCODE_LZMA, std::move(base)) {}
|
||||
};
|
||||
|
||||
class LZ4F_decoder : public cpr_stream {
|
||||
public:
|
||||
explicit LZ4F_decoder(sFILE &&fp) : cpr_stream(std::move(fp)), outbuf(nullptr) {
|
||||
explicit LZ4F_decoder(stream_ptr &&base) : cpr_stream(std::move(base)), outbuf(nullptr) {
|
||||
LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
|
||||
}
|
||||
|
||||
@ -317,8 +317,8 @@ private:
|
||||
|
||||
class LZ4F_encoder : public cpr_stream {
|
||||
public:
|
||||
explicit LZ4F_encoder(sFILE &&fp)
|
||||
: cpr_stream(std::move(fp)), outbuf(nullptr), outCapacity(0) {
|
||||
explicit LZ4F_encoder(stream_ptr &&base)
|
||||
: cpr_stream(std::move(base)), outbuf(nullptr), outCapacity(0) {
|
||||
LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
|
||||
}
|
||||
|
||||
@ -378,8 +378,8 @@ private:
|
||||
|
||||
class LZ4_decoder : public cpr_stream {
|
||||
public:
|
||||
explicit LZ4_decoder(sFILE &&fp)
|
||||
: cpr_stream(std::move(fp)), out_buf(new char[LZ4_UNCOMPRESSED]),
|
||||
explicit LZ4_decoder(stream_ptr &&base)
|
||||
: cpr_stream(std::move(base)), out_buf(new char[LZ4_UNCOMPRESSED]),
|
||||
buffer(new char[LZ4_COMPRESSED]), init(false), block_sz(0), buf_off(0) {}
|
||||
|
||||
~LZ4_decoder() override {
|
||||
@ -439,8 +439,8 @@ private:
|
||||
|
||||
class LZ4_encoder : public cpr_stream {
|
||||
public:
|
||||
explicit LZ4_encoder(sFILE &&fp)
|
||||
: cpr_stream(std::move(fp)), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]),
|
||||
explicit LZ4_encoder(stream_ptr &&base)
|
||||
: cpr_stream(std::move(base)), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]),
|
||||
init(false), buf_off(0), in_total(0) {}
|
||||
|
||||
int write(const void *in, size_t size) override {
|
||||
@ -500,38 +500,38 @@ private:
|
||||
unsigned in_total;
|
||||
};
|
||||
|
||||
stream_ptr get_encoder(format_t type, sFILE &&fp) {
|
||||
stream_ptr get_encoder(format_t type, stream_ptr &&base) {
|
||||
switch (type) {
|
||||
case XZ:
|
||||
return make_unique<xz_encoder>(std::move(fp));
|
||||
return make_unique<xz_encoder>(std::move(base));
|
||||
case LZMA:
|
||||
return make_unique<lzma_encoder>(std::move(fp));
|
||||
return make_unique<lzma_encoder>(std::move(base));
|
||||
case BZIP2:
|
||||
return make_unique<bz_encoder>(std::move(fp));
|
||||
return make_unique<bz_encoder>(std::move(base));
|
||||
case LZ4:
|
||||
return make_unique<LZ4F_encoder>(std::move(fp));
|
||||
return make_unique<LZ4F_encoder>(std::move(base));
|
||||
case LZ4_LEGACY:
|
||||
return make_unique<LZ4_encoder>(std::move(fp));
|
||||
return make_unique<LZ4_encoder>(std::move(base));
|
||||
case GZIP:
|
||||
default:
|
||||
return make_unique<gz_encoder>(std::move(fp));
|
||||
return make_unique<gz_encoder>(std::move(base));
|
||||
}
|
||||
}
|
||||
|
||||
stream_ptr get_decoder(format_t type, sFILE &&fp) {
|
||||
stream_ptr get_decoder(format_t type, stream_ptr &&base) {
|
||||
switch (type) {
|
||||
case XZ:
|
||||
case LZMA:
|
||||
return make_unique<lzma_decoder>(std::move(fp));
|
||||
return make_unique<lzma_decoder>(std::move(base));
|
||||
case BZIP2:
|
||||
return make_unique<bz_decoder>(std::move(fp));
|
||||
return make_unique<bz_decoder>(std::move(base));
|
||||
case LZ4:
|
||||
return make_unique<LZ4F_decoder>(std::move(fp));
|
||||
return make_unique<LZ4F_decoder>(std::move(base));
|
||||
case LZ4_LEGACY:
|
||||
return make_unique<LZ4_decoder>(std::move(fp));
|
||||
return make_unique<LZ4_decoder>(std::move(base));
|
||||
case GZIP:
|
||||
default:
|
||||
return make_unique<gz_decoder>(std::move(fp));
|
||||
return make_unique<gz_decoder>(std::move(base));
|
||||
}
|
||||
}
|
||||
|
||||
@ -573,7 +573,7 @@ void decompress(char *infile, const char *outfile) {
|
||||
}
|
||||
|
||||
FILE *out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
|
||||
strm = get_decoder(type, make_sFILE(out_fp));
|
||||
strm = get_decoder(type, make_unique<fp_stream>(out_fp));
|
||||
if (ext) *ext = '.';
|
||||
}
|
||||
if (strm->write(buf, len) < 0)
|
||||
@ -614,7 +614,7 @@ void compress(const char *method, const char *infile, const char *outfile) {
|
||||
out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
|
||||
}
|
||||
|
||||
auto strm = get_encoder(it->second, make_sFILE(out_fp));
|
||||
auto strm = get_encoder(it->second, make_unique<fp_stream>(out_fp));
|
||||
|
||||
char buf[4096];
|
||||
size_t len;
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
#include "format.h"
|
||||
|
||||
stream_ptr get_encoder(format_t type, sFILE &&fp);
|
||||
stream_ptr get_encoder(format_t type, stream_ptr &&base);
|
||||
|
||||
stream_ptr get_decoder(format_t type, sFILE &&fp);
|
||||
stream_ptr get_decoder(format_t type, stream_ptr &&base);
|
||||
|
||||
void compress(const char *method, const char *infile, const char *outfile);
|
||||
|
||||
|
@ -1,93 +1,20 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <bitset>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
extern "C" {
|
||||
#include <libfdt.h>
|
||||
}
|
||||
#include <utils.h>
|
||||
#include <bitset>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "magiskboot.h"
|
||||
#include "dtb.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
|
||||
#define QCDT_MAGIC "QCDT"
|
||||
#define DTBH_MAGIC "DTBH"
|
||||
#define PXADT_MAGIC "PXA-DT"
|
||||
#define PXA19xx_MAGIC "PXA-19xx"
|
||||
#define SPRD_MAGIC "SPRD"
|
||||
|
||||
struct qcdt_hdr {
|
||||
char magic[4]; /* "QCDT" */
|
||||
uint32_t version; /* QCDT version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v1 {
|
||||
uint32_t cpu_info[3]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v2 {
|
||||
uint32_t cpu_info[4]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v3 {
|
||||
uint32_t cpu_info[8]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dtbh_hdr {
|
||||
char magic[4]; /* "DTBH" */
|
||||
uint32_t version; /* DTBH version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bhtable_v2 {
|
||||
uint32_t cpu_info[5]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in DTBH */
|
||||
uint32_t len; /* DTB size */
|
||||
uint32_t space; /* 0x00000020 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxadt_hdr {
|
||||
char magic[6]; /* "PXA-DT" */
|
||||
uint32_t version; /* PXA-* version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxa19xx_hdr {
|
||||
char magic[8]; /* "PXA-19xx" */
|
||||
uint32_t version; /* PXA-* version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxatable_v1 {
|
||||
uint32_t cpu_info[2]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in PXA-* */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sprd_hdr {
|
||||
char magic[4]; /* "SPRD" */
|
||||
uint32_t version; /* SPRD version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sprdtable_v1 {
|
||||
uint32_t cpu_info[3]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in SPRD */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dtb_blob {
|
||||
struct fdt_blob {
|
||||
void *fdt;
|
||||
uint32_t offset;
|
||||
uint32_t len;
|
||||
@ -206,7 +133,7 @@ static void dtb_print(const char *file, bool fstab) {
|
||||
// Loop through all the dtbs
|
||||
int dtb_num = 0;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
|
||||
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
|
||||
auto fdt = dtb + i;
|
||||
if (fstab) {
|
||||
int node = find_fstab(fdt);
|
||||
@ -266,19 +193,33 @@ static bool fdt_patch(Iter first, Iter last) {
|
||||
|
||||
template <class Table, class Header>
|
||||
static int dtb_patch(const Header *hdr, const char *in, const char *out) {
|
||||
map<uint32_t, dtb_blob> dtb_map;
|
||||
map<uint32_t, fdt_blob> dtb_map;
|
||||
auto buf = reinterpret_cast<const uint8_t *>(hdr);
|
||||
auto tables = reinterpret_cast<const Table *>(hdr + 1);
|
||||
|
||||
constexpr bool is_dt_table = std::is_same_v<Header, dt_table_header>;
|
||||
|
||||
using endian_conv = uint32_t (*)(uint32_t);
|
||||
endian_conv be_to_le;
|
||||
endian_conv le_to_be;
|
||||
if constexpr (is_dt_table) {
|
||||
be_to_le = fdt32_to_cpu;
|
||||
le_to_be = cpu_to_fdt32;
|
||||
} else {
|
||||
be_to_le = le_to_be = [](uint32_t x) -> auto { return x; };
|
||||
}
|
||||
|
||||
// Collect all dtbs
|
||||
for (int i = 0; i < hdr->num_dtbs; ++i) {
|
||||
if (dtb_map.find(tables[i].offset) == dtb_map.end()) {
|
||||
auto blob = buf + tables[i].offset;
|
||||
int size = fdt_totalsize(blob);
|
||||
auto num_dtb = be_to_le(hdr->num_dtbs);
|
||||
for (int i = 0; i < num_dtb; ++i) {
|
||||
auto offset = be_to_le(tables[i].offset);
|
||||
if (dtb_map.count(offset) == 0) {
|
||||
auto blob = buf + offset;
|
||||
uint32_t size = fdt_totalsize(blob);
|
||||
auto fdt = xmalloc(size + 256);
|
||||
memcpy(fdt, blob, size);
|
||||
fdt_open_into(fdt, fdt, size + 256);
|
||||
dtb_map[tables[i].offset] = { fdt, tables[i].offset };
|
||||
dtb_map[offset] = { fdt, offset };
|
||||
}
|
||||
}
|
||||
if (dtb_map.empty())
|
||||
@ -292,18 +233,23 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) {
|
||||
unlink(in);
|
||||
int fd = xopen(out, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
|
||||
|
||||
uint32_t total_size = 0;
|
||||
|
||||
// Copy headers and tables
|
||||
xwrite(fd, buf, dtb_map.begin()->first);
|
||||
total_size += xwrite(fd, buf, dtb_map.begin()->first);
|
||||
|
||||
// mmap rw to patch table values retroactively
|
||||
auto mmap_sz = lseek(fd, 0, SEEK_CUR);
|
||||
auto addr = (uint8_t *) xmmap(nullptr, mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
|
||||
// Guess page size using gcd
|
||||
// Guess alignment using gcd
|
||||
uint32_t align = 1;
|
||||
if constexpr (!is_dt_table) {
|
||||
auto it = dtb_map.begin();
|
||||
uint32_t page_size = (it++)->first;
|
||||
align = (it++)->first;
|
||||
for (; it != dtb_map.end(); ++it)
|
||||
page_size = binary_gcd(page_size, it->first);
|
||||
align = binary_gcd(align, it->first);
|
||||
}
|
||||
|
||||
// Write dtbs
|
||||
for (auto &val : dtb_map) {
|
||||
@ -311,18 +257,23 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) {
|
||||
auto fdt = val.second.fdt;
|
||||
fdt_pack(fdt);
|
||||
int size = fdt_totalsize(fdt);
|
||||
xwrite(fd, fdt, size);
|
||||
val.second.len = do_align(size, page_size);
|
||||
write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size));
|
||||
total_size += xwrite(fd, fdt, size);
|
||||
val.second.len = do_align(size, align);
|
||||
write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), align));
|
||||
// total_size += align_off(lseek(fd, 0, SEEK_CUR), align); /* Not needed */
|
||||
free(fdt);
|
||||
}
|
||||
|
||||
// Patch tables
|
||||
// Patch headers
|
||||
if constexpr (is_dt_table) {
|
||||
auto hdr_rw = reinterpret_cast<Header *>(addr);
|
||||
hdr_rw->total_size = le_to_be(total_size);
|
||||
}
|
||||
auto tables_rw = reinterpret_cast<Table *>(addr + sizeof(Header));
|
||||
for (int i = 0; i < hdr->num_dtbs; ++i) {
|
||||
auto &blob = dtb_map[tables_rw[i].offset];
|
||||
tables_rw[i].offset = blob.offset;
|
||||
tables_rw[i].len = blob.len;
|
||||
for (int i = 0; i < num_dtb; ++i) {
|
||||
auto &blob = dtb_map[be_to_le(tables_rw[i].offset)];
|
||||
tables_rw[i].offset = le_to_be(blob.offset);
|
||||
tables_rw[i].len = le_to_be(blob.len);
|
||||
}
|
||||
|
||||
munmap(addr, mmap_sz);
|
||||
@ -393,10 +344,19 @@ static int dtb_patch(const char *in, const char *out) {
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
} else if (MATCH(DT_TABLE_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<dt_table_header *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 0:
|
||||
fprintf(stderr, "DT_TABLE v0\n");
|
||||
return dtb_patch<dt_table_entry>(hdr, in, out);
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
vector<uint8_t *> fdt_list;
|
||||
for (int i = 0; i < dtb_sz; ++i) {
|
||||
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
|
||||
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
|
||||
int len = fdt_totalsize(dtb + i);
|
||||
auto fdt = static_cast<uint8_t *>(xmalloc(len + 256));
|
||||
memcpy(fdt, dtb + i, len);
|
||||
|
104
native/jni/magiskboot/dtb.h
Normal file
104
native/jni/magiskboot/dtb.h
Normal file
@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define FDT_MAGIC_STR "\xd0\x0d\xfe\xed"
|
||||
#define DT_TABLE_MAGIC "\xd7\xb7\xab\x1e"
|
||||
#define QCDT_MAGIC "QCDT"
|
||||
#define DTBH_MAGIC "DTBH"
|
||||
#define PXADT_MAGIC "PXA-DT"
|
||||
#define PXA19xx_MAGIC "PXA-19xx"
|
||||
#define SPRD_MAGIC "SPRD"
|
||||
|
||||
struct qcdt_hdr {
|
||||
char magic[4]; /* "QCDT" */
|
||||
uint32_t version; /* QCDT version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v1 {
|
||||
uint32_t cpu_info[3]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v2 {
|
||||
uint32_t cpu_info[4]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct qctable_v3 {
|
||||
uint32_t cpu_info[8]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in QCDT */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dtbh_hdr {
|
||||
char magic[4]; /* "DTBH" */
|
||||
uint32_t version; /* DTBH version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bhtable_v2 {
|
||||
uint32_t cpu_info[5]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in DTBH */
|
||||
uint32_t len; /* DTB size */
|
||||
uint32_t space; /* 0x00000020 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxadt_hdr {
|
||||
char magic[6]; /* "PXA-DT" */
|
||||
uint32_t version; /* PXA-* version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxa19xx_hdr {
|
||||
char magic[8]; /* "PXA-19xx" */
|
||||
uint32_t version; /* PXA-* version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pxatable_v1 {
|
||||
uint32_t cpu_info[2]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in PXA-* */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sprd_hdr {
|
||||
char magic[4]; /* "SPRD" */
|
||||
uint32_t version; /* SPRD version */
|
||||
uint32_t num_dtbs; /* Number of DTBs */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sprdtable_v1 {
|
||||
uint32_t cpu_info[3]; /* Some CPU info */
|
||||
uint32_t offset; /* DTB offset in SPRD */
|
||||
uint32_t len; /* DTB size */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* AOSP DTB/DTBO partition layout */
|
||||
|
||||
struct dt_table_header {
|
||||
uint32_t magic; /* DT_TABLE_MAGIC */
|
||||
uint32_t total_size; /* includes dt_table_header + all dt_table_entry */
|
||||
uint32_t header_size; /* sizeof(dt_table_header) */
|
||||
|
||||
uint32_t dt_entry_size; /* sizeof(dt_table_entry) */
|
||||
uint32_t num_dtbs; /* number of dt_table_entry */
|
||||
uint32_t dt_entries_offset; /* offset to the first dt_table_entry */
|
||||
|
||||
uint32_t page_size; /* flash page size we assume */
|
||||
uint32_t version; /* DTBO image version */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dt_table_entry {
|
||||
uint32_t len; /* DTB size */
|
||||
uint32_t offset;
|
||||
|
||||
uint32_t id;
|
||||
uint32_t rev;
|
||||
uint32_t flags;
|
||||
|
||||
uint32_t custom[3];
|
||||
} __attribute__((packed));
|
@ -14,6 +14,7 @@
|
||||
|
||||
int unpack(const char *image, bool nodecomp = false, bool hdr = false);
|
||||
void repack(const char* src_img, const char* out_img, bool nocomp = false);
|
||||
int split_image_dtb(const char *filename);
|
||||
int hexpatch(const char *image, const char *from, const char *to);
|
||||
int cpio_commands(int argc, char *argv[]);
|
||||
int dtb_commands(int argc, char *argv[]);
|
||||
|
@ -84,6 +84,15 @@ Supported actions:
|
||||
If [OUT] is not specified, it will directly output to <input>
|
||||
Configure with env variables: KEEPVERITY TWOSTAGEINIT
|
||||
|
||||
split <input>
|
||||
Split image.*-dtb into kernel + kernel_dtb
|
||||
|
||||
sha1 <file>
|
||||
Print the SHA1 checksum for <file>
|
||||
|
||||
cleanup
|
||||
Cleanup the current working directory
|
||||
|
||||
compress[=method] <infile> [outfile]
|
||||
Compress <infile> with [method] (default: gzip), optionally to [outfile]
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT
|
||||
@ -102,16 +111,7 @@ Supported actions:
|
||||
for (auto &it : name2fmt)
|
||||
fprintf(stderr, "%s ", it.first.data());
|
||||
|
||||
fprintf(stderr, R"EOF(
|
||||
|
||||
sha1 <file>
|
||||
Print the SHA1 checksum for <file>
|
||||
|
||||
cleanup
|
||||
Cleanup the current working directory
|
||||
|
||||
)EOF");
|
||||
|
||||
fprintf(stderr, "\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -147,6 +147,8 @@ int main(int argc, char *argv[]) {
|
||||
printf("%02x", i);
|
||||
printf("\n");
|
||||
munmap(buf, size);
|
||||
} else if (argc > 2 && action == "split") {
|
||||
return split_image_dtb(argv[2]);
|
||||
} else if (argc > 2 && action == "unpack") {
|
||||
int idx = 2;
|
||||
bool nodecomp = false;
|
||||
|
@ -5,54 +5,58 @@
|
||||
|
||||
#include "magiskboot.h"
|
||||
|
||||
#define MATCH(p) else if (strncmp(s + skip, p, sizeof(p) - 1) == 0) skip += (sizeof(p) - 1)
|
||||
|
||||
static int check_verity_pattern(const char *s) {
|
||||
int skip = 0;
|
||||
if (s[0] == ',') ++skip;
|
||||
if (strncmp(s + skip, "verify", 6) == 0)
|
||||
skip += 6;
|
||||
else if (strncmp(s + skip, "avb", 3) == 0)
|
||||
skip += 3;
|
||||
else
|
||||
return -1;
|
||||
int skip = s[0] == ',';
|
||||
|
||||
if (0) {}
|
||||
MATCH("verify");
|
||||
MATCH("avb");
|
||||
MATCH("support_scfs");
|
||||
else return -1;
|
||||
|
||||
if (s[skip] == '=') {
|
||||
while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') ++skip;
|
||||
while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',')
|
||||
++skip;
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
#undef MATCH
|
||||
#define MATCH(p) else if (strncmp(s, p, sizeof(p) - 1) == 0) return (sizeof(p) - 1)
|
||||
|
||||
static int check_encryption_pattern(const char *s) {
|
||||
static const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe" };
|
||||
for (auto enc : encrypt_list) {
|
||||
int len = strlen(enc);
|
||||
if (strncmp(s, enc, len) == 0)
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
if (0) {}
|
||||
MATCH("forceencrypt");
|
||||
MATCH("forcefdeorfbe");
|
||||
MATCH("fileencryption");
|
||||
else return -1;
|
||||
}
|
||||
|
||||
char *patch_verity(const void *buf, uint32_t &size, bool inplace) {
|
||||
auto src = static_cast<const char *>(buf);
|
||||
auto dest = (char *)(inplace ? buf : xmalloc(size));
|
||||
int src_size = size;
|
||||
bool found = false;
|
||||
auto patched = (char *)(inplace ? buf : xmalloc(size));
|
||||
int write = 0;
|
||||
for (int read = 0; read < src_size; ++read, ++write) {
|
||||
for (int read = 0; read < src_size;) {
|
||||
if (int skip; (skip = check_verity_pattern(src + read)) > 0) {
|
||||
fprintf(stderr, "Found pattern [%.*s]\n", skip, src + read);
|
||||
size -= skip;
|
||||
read += skip;
|
||||
found = true;
|
||||
} else {
|
||||
dest[write++] = src[read++];
|
||||
}
|
||||
patched[write] = src[read];
|
||||
}
|
||||
patched[write] = '\0';
|
||||
dest[write] = '\0';
|
||||
if (!found) {
|
||||
if (!inplace)
|
||||
free(patched);
|
||||
free(dest);
|
||||
return nullptr;
|
||||
}
|
||||
return patched;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void patch_encryption(void *buf, uint32_t &size) {
|
||||
|
@ -47,8 +47,11 @@ void magisk_cpio::patch() {
|
||||
for (auto it = entries.begin(); it != entries.end();) {
|
||||
auto cur = it++;
|
||||
bool fstab = (!keepverity || !keepforceencrypt) &&
|
||||
S_ISREG(cur->second->mode) &&
|
||||
!str_starts(cur->first, ".backup") &&
|
||||
str_contains(cur->first, "fstab") && S_ISREG(cur->second->mode);
|
||||
!str_contains(cur->first, "twrp") &&
|
||||
!str_contains(cur->first, "recovery") &&
|
||||
str_contains(cur->first, "fstab");
|
||||
if (!keepverity) {
|
||||
if (fstab) {
|
||||
fprintf(stderr, "Found fstab file [%s]\n", cur->first.data());
|
||||
@ -244,7 +247,7 @@ void magisk_cpio::compress() {
|
||||
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
auto strm = make_stream(get_encoder(XZ, make_stream<byte_stream>(data, len)));
|
||||
auto strm = make_stream_fp(get_encoder(XZ, make_unique<byte_stream>(data, len)));
|
||||
dump(strm.release());
|
||||
|
||||
entries.clear();
|
||||
@ -264,7 +267,7 @@ void magisk_cpio::decompress() {
|
||||
char *data;
|
||||
size_t len;
|
||||
{
|
||||
auto strm = get_decoder(XZ, make_stream<byte_stream>(data, len));
|
||||
auto strm = get_decoder(XZ, make_unique<byte_stream>(data, len));
|
||||
strm->write(it->second->data, it->second->filesize);
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ void hide_unmount(int pid) {
|
||||
|
||||
// Unmount dummy skeletons and /sbin links
|
||||
parse_mnt("/proc/self/mounts", [&](mntent *mentry) {
|
||||
if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(sbin))
|
||||
if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(sbin) || TMPFS_MNT(product))
|
||||
targets.emplace_back(mentry->mnt_dir);
|
||||
return true;
|
||||
});
|
||||
|
@ -183,7 +183,7 @@ bool init_list() {
|
||||
|
||||
// Migrate old hide list into database
|
||||
if (access(LEGACY_LIST, R_OK) == 0) {
|
||||
file_readline(LEGACY_LIST, [](string_view s) -> bool {
|
||||
file_readline(true, LEGACY_LIST, [](string_view s) -> bool {
|
||||
add_list(s.data());
|
||||
return true;
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "magiskpolicy.h"
|
||||
#include <magiskpolicy.h>
|
||||
|
||||
#include "sepolicy.h"
|
||||
|
||||
//#define vprint(fmt, ...) printf(fmt, __VA_ARGS__)
|
||||
|
@ -1,459 +1,59 @@
|
||||
/* magiskpolicy.cpp - Main function for policy patching
|
||||
*
|
||||
* Includes all the parsing logic for the policy statements
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <logging.h>
|
||||
#include <utils.h>
|
||||
#include <flags.h>
|
||||
#include <magiskpolicy.h>
|
||||
|
||||
#include "sepolicy.h"
|
||||
#include "magiskpolicy.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const char *type_msg_1 =
|
||||
"Type 1:\n"
|
||||
"\"<rule_name> source_type target_type class perm_set\"\n"
|
||||
"Rules: allow, deny, auditallow, dontaudit\n";
|
||||
|
||||
static const char *type_msg_2 =
|
||||
"Type 2:\n"
|
||||
"\"<rule_name> source_type target_type class operation xperm_set\"\n"
|
||||
"Rules: allowxperm, auditallowxperm, dontauditxperm\n"
|
||||
"* The only supported operation is ioctl\n"
|
||||
"* The only supported xperm_set format is range ([low-high])\n";
|
||||
|
||||
static const char *type_msg_3 =
|
||||
"Type 3:\n"
|
||||
"\"<rule_name> class\"\n"
|
||||
"Rules: create, permissive, enforcing\n";
|
||||
|
||||
static const char *type_msg_4 =
|
||||
"Type 4:\n"
|
||||
"\"attradd class attribute\"\n";
|
||||
|
||||
static const char *type_msg_5 =
|
||||
"Type 5:\n"
|
||||
"\"<rule_name> source_type target_type class default_type\"\n"
|
||||
"Rules: type_transition, type_change, type_member\n";
|
||||
|
||||
static const char *type_msg_6 =
|
||||
"Type 6:\n"
|
||||
"\"name_transition source_type target_type class default_type object_name\"\n";
|
||||
|
||||
|
||||
[[noreturn]] static void statements() {
|
||||
fprintf(stderr,
|
||||
"One policy statement should be treated as one parameter;\n"
|
||||
"this means a full policy statement should be enclosed in quotes;\n"
|
||||
"multiple policy statements can be provided in a single command\n"
|
||||
"\n"
|
||||
"The statements has a format of \"<rule_name> [args...]\"\n"
|
||||
"Multiple types and permissions can be grouped into collections\n"
|
||||
"wrapped in curly brackets.\n"
|
||||
"'*' represents a collection containing all valid matches.\n"
|
||||
"\n"
|
||||
"Supported policy statements:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"%s\n"
|
||||
"%s\n"
|
||||
"%s\n"
|
||||
"%s\n"
|
||||
"%s\n"
|
||||
"Notes:\n"
|
||||
"* Type 4 - 6 does not support collections\n"
|
||||
"* Object classes cannot be collections\n"
|
||||
"* source_type and target_type can also be attributes\n"
|
||||
"\n"
|
||||
"Example: allow { s1 s2 } { t1 t2 } class *\n"
|
||||
"Will be expanded to:\n"
|
||||
"\n"
|
||||
"allow s1 t1 class { all permissions }\n"
|
||||
"allow s1 t2 class { all permissions }\n"
|
||||
"allow s2 t1 class { all permissions }\n"
|
||||
"allow s2 t2 class { all permissions }\n"
|
||||
"\n",
|
||||
type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5, type_msg_6);
|
||||
exit(0);
|
||||
}
|
||||
using namespace std::literals;
|
||||
|
||||
[[noreturn]] static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
FULL_VER(MagiskPolicy) "\n\n"
|
||||
"Usage: %s [--options...] [policy statements...]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" --help show help message for policy statements\n"
|
||||
" --load FILE load policies from FILE\n"
|
||||
" --load-split load from preloaded sepolicy or compile\n"
|
||||
" split policies\n"
|
||||
" --compile-split compile split cil policies\n"
|
||||
" --save FILE save policies to FILE\n"
|
||||
" --live directly apply sepolicy live\n"
|
||||
" --magisk inject built-in rules for a minimal\n"
|
||||
" Magisk selinux environment\n"
|
||||
"\n"
|
||||
"If neither --load or --compile-split is specified, it will load\n"
|
||||
"from current live policies (" SELINUX_POLICY ")\n"
|
||||
"\n",
|
||||
arg0);
|
||||
FULL_VER(MagiskPolicy) R"EOF(
|
||||
|
||||
Usage: %s [--options...] [policy statements...]
|
||||
|
||||
Options:
|
||||
--help show help message for policy statements
|
||||
--load FILE load policies from FILE
|
||||
--load-split load from precompiled sepolicy or compile
|
||||
split policies
|
||||
--compile-split compile split cil policies
|
||||
--save FILE save policies to FILE
|
||||
--live directly apply sepolicy live
|
||||
--magisk inject built-in rules for a minimal
|
||||
Magisk selinux environment
|
||||
--apply FILE apply rules from FILE, read and parsed
|
||||
line by line as policy statements
|
||||
|
||||
If neither --load or --compile-split is specified, it will load
|
||||
from current live policies (/sys/fs/selinux/policy)
|
||||
|
||||
)EOF", arg0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int parse_bracket(char *tok, char *&stmt, vector<const char *> *vec) {
|
||||
if (tok == nullptr || tok[0] != '{') {
|
||||
// Not in a bracket
|
||||
vec->push_back(tok);
|
||||
} else {
|
||||
if (stmt)
|
||||
stmt[-1] = ' ';
|
||||
tok = strchr(tok, '{') + 1;
|
||||
char *end = strchr(tok, '}');
|
||||
if (end == nullptr) // Bracket not closed
|
||||
return 1;
|
||||
*end = '\0';
|
||||
char *cur;
|
||||
while ((cur = strtok_r(nullptr, " ", &tok)) != nullptr)
|
||||
vec->push_back(cur);
|
||||
stmt = end + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 1: action { source } { target } class { permission }
|
||||
static int parse_pattern_1(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*, const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_allow;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_deny;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_auditallow;
|
||||
break;
|
||||
case 3:
|
||||
action_func = sepol_dontaudit;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
char *cur, *cls;
|
||||
vector<const char*> source, target, permission;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
vector<const char *> *vec;
|
||||
switch (state) {
|
||||
case 0:
|
||||
vec = &source;
|
||||
break;
|
||||
case 1:
|
||||
vec = ⌖
|
||||
break;
|
||||
case 2:
|
||||
vec = nullptr;
|
||||
cls = cur;
|
||||
break;
|
||||
case 3:
|
||||
vec = &permission;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vec && parse_bracket(cur, stmt, vec))
|
||||
return 1;
|
||||
++state;
|
||||
}
|
||||
if (state != 4 || source.empty() || target.empty() || permission.empty())
|
||||
return 1;
|
||||
|
||||
for (auto src : source)
|
||||
for (auto tgt : target)
|
||||
for (auto perm : permission)
|
||||
if (action_func(src, tgt, cls, perm))
|
||||
fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, perm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 2: action { source } { target } { class } ioctl range
|
||||
static int parse_pattern_2(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*, const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_allowxperm;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_auditallowxperm;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_dontauditxperm;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
char *cur, *range;
|
||||
vector<const char *> source, target, classes;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
vector<const char *> *vec;
|
||||
switch (state) {
|
||||
case 0:
|
||||
vec = &source;
|
||||
break;
|
||||
case 1:
|
||||
vec = ⌖
|
||||
break;
|
||||
case 2:
|
||||
vec = &classes;
|
||||
break;
|
||||
case 3:
|
||||
// Currently only support ioctl
|
||||
if (strcmp(cur, "ioctl") != 0)
|
||||
return 1;
|
||||
vec = nullptr;
|
||||
break;
|
||||
case 4:
|
||||
vec = nullptr;
|
||||
range = cur;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vec && parse_bracket(cur, stmt, vec))
|
||||
return 1;
|
||||
++state;
|
||||
}
|
||||
if (state != 5 || source.empty() || target.empty() || classes.empty())
|
||||
return 1;
|
||||
|
||||
for (auto src : source)
|
||||
for (auto tgt : target)
|
||||
for (auto cls : classes)
|
||||
if (action_func(src, tgt, cls, range))
|
||||
fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, range);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 3: action { type }
|
||||
static int parse_pattern_3(int action, const char *action_str, char* stmt) {
|
||||
int (*action_func)(const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_create;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_permissive;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_enforce;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cur;
|
||||
vector<const char *> domains;
|
||||
while ((cur = strtok_r(nullptr, " {}", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
domains.push_back(cur);
|
||||
}
|
||||
|
||||
if (domains.empty())
|
||||
return 1;
|
||||
|
||||
for (auto dom : domains)
|
||||
if (action_func(dom))
|
||||
fprintf(stderr, "Error in: %s %s\n", action_str, dom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 4: action { class } { attribute }
|
||||
static int parse_pattern_4(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_attradd;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
char *cur;
|
||||
vector<const char *> classes, attribute;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
vector<const char *> *vec;
|
||||
switch (state) {
|
||||
case 0:
|
||||
vec = &classes;
|
||||
break;
|
||||
case 1:
|
||||
vec = &attribute;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parse_bracket(cur, stmt, vec))
|
||||
return 1;
|
||||
++state;
|
||||
}
|
||||
if (state != 2 || classes.empty() || attribute.empty())
|
||||
return 1;
|
||||
|
||||
for (auto cls : classes)
|
||||
for (auto attr : attribute)
|
||||
if (action_func(cls, attr))
|
||||
fprintf(stderr, "Error in: %s %s %s\n", action_str, cls, attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 5: action source target class default
|
||||
static int parse_pattern_5(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*, const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_typetrans;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_typechange;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_typemember;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
int state = 0;
|
||||
char *cur;
|
||||
char *source, *target, *cls, *def;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
switch(state) {
|
||||
case 0:
|
||||
source = cur;
|
||||
break;
|
||||
case 1:
|
||||
target = cur;
|
||||
break;
|
||||
case 2:
|
||||
cls = cur;
|
||||
break;
|
||||
case 3:
|
||||
def = cur;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
++state;
|
||||
}
|
||||
if (state < 4) return 1;
|
||||
if (action_func(source, target, cls, def))
|
||||
fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, source, target, cls, def);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 6: action source target class default filename
|
||||
static int parse_pattern_6(int action, const char *action_str, char *stmt) {
|
||||
int state = 0;
|
||||
char *cur;
|
||||
char *source, *target, *cls, *def, *filename;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
switch(state) {
|
||||
case 0:
|
||||
source = cur;
|
||||
break;
|
||||
case 1:
|
||||
target = cur;
|
||||
break;
|
||||
case 2:
|
||||
cls = cur;
|
||||
break;
|
||||
case 3:
|
||||
def = cur;
|
||||
break;
|
||||
case 4:
|
||||
filename = cur;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
++state;
|
||||
}
|
||||
if (state < 4) return 1;
|
||||
if (sepol_nametrans(source, target, cls, def, filename))
|
||||
fprintf(stderr, "Error in: %s %s %s %s %s %s\n",
|
||||
action_str, source, target, cls, def, filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define add_action(name, type, num) \
|
||||
else if (strcmp(name, action) == 0) { \
|
||||
if (parse_pattern_##type(num, name, remain)) \
|
||||
fprintf(stderr, "Syntax error in '%s'\n\n%s\n", orig.c_str(), type_msg_##type); \
|
||||
}
|
||||
|
||||
static void parse_statement(char *statement) {
|
||||
char *action, *remain;
|
||||
|
||||
// strtok will modify the origin string, duplicate the statement for error messages
|
||||
string orig(statement);
|
||||
|
||||
action = strtok_r(statement, " ", &remain);
|
||||
if (remain == nullptr) remain = &action[strlen(action)];
|
||||
|
||||
if (0) {}
|
||||
add_action("allow", 1, 0)
|
||||
add_action("deny", 1, 1)
|
||||
add_action("auditallow", 1, 2)
|
||||
add_action("dontaudit", 1, 3)
|
||||
add_action("allowxperm", 2, 0)
|
||||
add_action("auditallowxperm", 2, 1)
|
||||
add_action("dontauditxperm", 2, 2)
|
||||
add_action("create", 3, 0)
|
||||
add_action("permissive", 3, 1)
|
||||
add_action("enforce", 3, 2)
|
||||
add_action("attradd", 4, 0)
|
||||
add_action("type_transition", 5, 0)
|
||||
add_action("type_change", 5, 1)
|
||||
add_action("type_member", 5, 2)
|
||||
add_action("name_transition", 6, 0)
|
||||
else { fprintf(stderr, "Unknown statement: '%s'\n\n", orig.c_str()); }
|
||||
}
|
||||
|
||||
int magiskpolicy_main(int argc, char *argv[]) {
|
||||
cmdline_logging();
|
||||
const char *outfile = nullptr;
|
||||
bool magisk = false, live = false;
|
||||
const char *out_file = nullptr;
|
||||
const char *rule_file = nullptr;
|
||||
bool magisk = false;
|
||||
bool live = false;
|
||||
|
||||
if (argc < 2) usage(argv[0]);
|
||||
int i = 1;
|
||||
for (; i < argc; ++i) {
|
||||
// Parse options
|
||||
if (argv[i][0] == '-' && argv[i][1] == '-') {
|
||||
if (strcmp(argv[i] + 2, "live") == 0)
|
||||
auto option = argv[i] + 2;
|
||||
if (option == "live"sv)
|
||||
live = true;
|
||||
else if (strcmp(argv[i] + 2, "magisk") == 0)
|
||||
else if (option == "magisk"sv)
|
||||
magisk = true;
|
||||
else if (strcmp(argv[i] + 2, "load") == 0) {
|
||||
else if (option == "load"sv) {
|
||||
if (argv[i + 1] == nullptr)
|
||||
usage(argv[0]);
|
||||
if (load_policydb(argv[i + 1])) {
|
||||
@ -461,23 +61,28 @@ int magiskpolicy_main(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
++i;
|
||||
} else if (strcmp(argv[i] + 2, "load-split") == 0) {
|
||||
} else if (option == "load-split"sv) {
|
||||
if (load_split_cil()) {
|
||||
fprintf(stderr, "Cannot load split cil\n");
|
||||
return 1;
|
||||
}
|
||||
} else if (strcmp(argv[i] + 2, "compile-split") == 0) {
|
||||
} else if (option == "compile-split"sv) {
|
||||
if (compile_split_cil()) {
|
||||
fprintf(stderr, "Cannot compile split cil\n");
|
||||
return 1;
|
||||
}
|
||||
} else if (strcmp(argv[i] + 2, "save") == 0) {
|
||||
} else if (option == "save"sv) {
|
||||
if (argv[i + 1] == nullptr)
|
||||
usage(argv[0]);
|
||||
outfile = argv[i + 1];
|
||||
out_file = argv[i + 1];
|
||||
++i;
|
||||
} else if (strcmp(argv[i] + 2, "help") == 0) {
|
||||
statements();
|
||||
} else if (option == "apply"sv) {
|
||||
if (argv[i + 1] == nullptr)
|
||||
usage(argv[0]);
|
||||
rule_file = argv[i + 1];
|
||||
++i;
|
||||
} else if (option == "help"sv) {
|
||||
statement_help();
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
@ -495,6 +100,9 @@ int magiskpolicy_main(int argc, char *argv[]) {
|
||||
if (magisk)
|
||||
sepol_magisk_rules();
|
||||
|
||||
if (rule_file)
|
||||
load_rule_file(rule_file);
|
||||
|
||||
for (; i < argc; ++i)
|
||||
parse_statement(argv[i]);
|
||||
|
||||
@ -503,8 +111,8 @@ int magiskpolicy_main(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (outfile && dump_policydb(outfile)) {
|
||||
fprintf(stderr, "Cannot dump policy to %s\n", outfile);
|
||||
if (out_file && dump_policydb(out_file)) {
|
||||
fprintf(stderr, "Cannot dump policy to %s\n", out_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,12 @@
|
||||
#include <utils.h>
|
||||
#include <logging.h>
|
||||
#include <stream.h>
|
||||
#include <magiskpolicy.h>
|
||||
|
||||
#include "magiskpolicy.h"
|
||||
#include "sepolicy.h"
|
||||
|
||||
int load_policydb(const char *file) {
|
||||
LOGD("Load policy from: %s\n", file);
|
||||
if (magisk_policydb)
|
||||
destroy_policydb();
|
||||
|
||||
@ -178,7 +179,7 @@ int dump_policydb(const char *file) {
|
||||
size_t len;
|
||||
|
||||
{
|
||||
auto fp = make_stream<byte_stream>(data, len);
|
||||
auto fp = make_stream_fp<byte_stream>(data, len);
|
||||
struct policy_file pf;
|
||||
policy_file_init(&pf);
|
||||
pf.type = PF_USE_STDIO;
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <logging.h>
|
||||
#include <flags.h>
|
||||
#include <magiskpolicy.h>
|
||||
|
||||
#include "magiskpolicy.h"
|
||||
#include "sepolicy.h"
|
||||
|
||||
static void allowSuClient(const char *target) {
|
||||
|
425
native/jni/magiskpolicy/statement.cpp
Normal file
425
native/jni/magiskpolicy/statement.cpp
Normal file
@ -0,0 +1,425 @@
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <magiskpolicy.h>
|
||||
#include <logging.h>
|
||||
#include <utils.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const char *type_msg_1 =
|
||||
R"EOF(Type 1:
|
||||
"<rule_name> source_type target_type class perm_set"
|
||||
Rules: allow, deny, auditallow, dontaudit
|
||||
)EOF";
|
||||
|
||||
static const char *type_msg_2 =
|
||||
R"EOF(Type 2:
|
||||
"<rule_name> source_type target_type class operation xperm_set"
|
||||
Rules: allowxperm, auditallowxperm, dontauditxperm
|
||||
* The only supported operation is ioctl
|
||||
* The only supported xperm_set format is range ([low-high])
|
||||
)EOF";
|
||||
|
||||
static const char *type_msg_3 =
|
||||
R"EOF(Type 3:
|
||||
"<rule_name> class"
|
||||
Rules: create, permissive, enforcing
|
||||
)EOF";
|
||||
|
||||
static const char *type_msg_4 =
|
||||
R"EOF(Type 4:
|
||||
"attradd class attribute"
|
||||
)EOF";
|
||||
|
||||
static const char *type_msg_5 =
|
||||
R"EOF(Type 5:
|
||||
"<rule_name> source_type target_type class default_type"
|
||||
Rules: type_transition, type_change, type_member
|
||||
)EOF";
|
||||
|
||||
static const char *type_msg_6 =
|
||||
R"EOF(Type 6:
|
||||
"name_transition source_type target_type class default_type object_name"
|
||||
)EOF";
|
||||
|
||||
void statement_help() {
|
||||
fprintf(stderr,
|
||||
R"EOF(One policy statement should be treated as one parameter;
|
||||
this means a full policy statement should be enclosed in quotes.
|
||||
Multiple policy statements can be provided in a single command.
|
||||
|
||||
The statements has a format of "<rule_name> [args...]"
|
||||
Multiple types and permissions can be grouped into collections
|
||||
wrapped in curly brackets.
|
||||
'*' represents a collection containing all valid matches.
|
||||
|
||||
Supported policy statements:
|
||||
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
Notes:
|
||||
* Type 4 - 6 does not support collections
|
||||
* Object classes cannot be collections
|
||||
* source_type and target_type can also be attributes
|
||||
|
||||
Example: allow { s1 s2 } { t1 t2 } class *
|
||||
Will be expanded to:
|
||||
|
||||
allow s1 t1 class { all-permissions }
|
||||
allow s1 t2 class { all-permissions }
|
||||
allow s2 t1 class { all-permissions }
|
||||
allow s2 t2 class { all-permissions }
|
||||
|
||||
)EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5, type_msg_6);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int parse_bracket(char *tok, char *&stmt, vector<const char *> &vec) {
|
||||
if (tok == nullptr || tok[0] != '{') {
|
||||
// Not in a bracket
|
||||
vec.push_back(tok);
|
||||
} else {
|
||||
if (stmt)
|
||||
stmt[-1] = ' ';
|
||||
tok = strchr(tok, '{') + 1;
|
||||
char *end = strchr(tok, '}');
|
||||
if (end == nullptr) // Bracket not closed
|
||||
return 1;
|
||||
*end = '\0';
|
||||
char *cur;
|
||||
while ((cur = strtok_r(nullptr, " ", &tok)) != nullptr)
|
||||
vec.push_back(cur);
|
||||
stmt = end + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 1: action { source } { target } class { permission }
|
||||
static int parse_pattern_1(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*, const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_allow;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_deny;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_auditallow;
|
||||
break;
|
||||
case 3:
|
||||
action_func = sepol_dontaudit;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
char *cur, *cls;
|
||||
vector<const char*> source, target, permission;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
vector<const char *> *vec;
|
||||
switch (state) {
|
||||
case 0:
|
||||
vec = &source;
|
||||
break;
|
||||
case 1:
|
||||
vec = ⌖
|
||||
break;
|
||||
case 2:
|
||||
vec = nullptr;
|
||||
cls = cur;
|
||||
break;
|
||||
case 3:
|
||||
vec = &permission;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vec && parse_bracket(cur, stmt, *vec))
|
||||
return 1;
|
||||
++state;
|
||||
}
|
||||
if (state != 4 || source.empty() || target.empty() || permission.empty())
|
||||
return 1;
|
||||
|
||||
for (auto src : source)
|
||||
for (auto tgt : target)
|
||||
for (auto perm : permission)
|
||||
if (action_func(src, tgt, cls, perm))
|
||||
LOGW("Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, perm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 2: action { source } { target } { class } ioctl range
|
||||
static int parse_pattern_2(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*, const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_allowxperm;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_auditallowxperm;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_dontauditxperm;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
char *cur, *range;
|
||||
vector<const char *> source, target, classes;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
vector<const char *> *vec;
|
||||
switch (state) {
|
||||
case 0:
|
||||
vec = &source;
|
||||
break;
|
||||
case 1:
|
||||
vec = ⌖
|
||||
break;
|
||||
case 2:
|
||||
vec = &classes;
|
||||
break;
|
||||
case 3:
|
||||
// Currently only support ioctl
|
||||
if (strcmp(cur, "ioctl") != 0)
|
||||
return 1;
|
||||
vec = nullptr;
|
||||
break;
|
||||
case 4:
|
||||
vec = nullptr;
|
||||
range = cur;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vec && parse_bracket(cur, stmt, *vec))
|
||||
return 1;
|
||||
++state;
|
||||
}
|
||||
if (state != 5 || source.empty() || target.empty() || classes.empty())
|
||||
return 1;
|
||||
|
||||
for (auto src : source)
|
||||
for (auto tgt : target)
|
||||
for (auto cls : classes)
|
||||
if (action_func(src, tgt, cls, range))
|
||||
LOGW("Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, range);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 3: action { type }
|
||||
static int parse_pattern_3(int action, const char *action_str, char* stmt) {
|
||||
int (*action_func)(const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_create;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_permissive;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_enforce;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cur;
|
||||
vector<const char *> domains;
|
||||
while ((cur = strtok_r(nullptr, " {}", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
domains.push_back(cur);
|
||||
}
|
||||
|
||||
if (domains.empty())
|
||||
return 1;
|
||||
|
||||
for (auto dom : domains)
|
||||
if (action_func(dom))
|
||||
LOGW("Error in: %s %s\n", action_str, dom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 4: action { class } { attribute }
|
||||
static int parse_pattern_4(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_attradd;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
char *cur;
|
||||
vector<const char *> classes, attribute;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
if (cur[0] == '*') cur = ALL;
|
||||
vector<const char *> *vec;
|
||||
switch (state) {
|
||||
case 0:
|
||||
vec = &classes;
|
||||
break;
|
||||
case 1:
|
||||
vec = &attribute;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parse_bracket(cur, stmt, *vec))
|
||||
return 1;
|
||||
++state;
|
||||
}
|
||||
if (state != 2 || classes.empty() || attribute.empty())
|
||||
return 1;
|
||||
|
||||
for (auto cls : classes)
|
||||
for (auto attr : attribute)
|
||||
if (action_func(cls, attr))
|
||||
LOGW("Error in: %s %s %s\n", action_str, cls, attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 5: action source target class default
|
||||
static int parse_pattern_5(int action, const char *action_str, char *stmt) {
|
||||
int (*action_func)(const char*, const char*, const char*, const char*);
|
||||
switch (action) {
|
||||
case 0:
|
||||
action_func = sepol_typetrans;
|
||||
break;
|
||||
case 1:
|
||||
action_func = sepol_typechange;
|
||||
break;
|
||||
case 2:
|
||||
action_func = sepol_typemember;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
int state = 0;
|
||||
char *cur;
|
||||
char *source, *target, *cls, *def;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
switch(state) {
|
||||
case 0:
|
||||
source = cur;
|
||||
break;
|
||||
case 1:
|
||||
target = cur;
|
||||
break;
|
||||
case 2:
|
||||
cls = cur;
|
||||
break;
|
||||
case 3:
|
||||
def = cur;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
++state;
|
||||
}
|
||||
if (state < 4) return 1;
|
||||
if (action_func(source, target, cls, def))
|
||||
LOGW("Error in: %s %s %s %s %s\n", action_str, source, target, cls, def);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Pattern 6: action source target class default filename
|
||||
static int parse_pattern_6(int action, const char *action_str, char *stmt) {
|
||||
int state = 0;
|
||||
char *cur;
|
||||
char *source, *target, *cls, *def, *filename;
|
||||
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
|
||||
switch(state) {
|
||||
case 0:
|
||||
source = cur;
|
||||
break;
|
||||
case 1:
|
||||
target = cur;
|
||||
break;
|
||||
case 2:
|
||||
cls = cur;
|
||||
break;
|
||||
case 3:
|
||||
def = cur;
|
||||
break;
|
||||
case 4:
|
||||
filename = cur;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
++state;
|
||||
}
|
||||
if (state < 4) return 1;
|
||||
if (sepol_nametrans(source, target, cls, def, filename))
|
||||
LOGW("Error in: %s %s %s %s %s %s\n", action_str, source, target, cls, def, filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define add_action(name, type, num) \
|
||||
else if (strcmp(name, action) == 0) { \
|
||||
if (parse_pattern_##type(num, name, remain)) \
|
||||
LOGW("Syntax error in '%s'\n\n%s\n", statement, type_msg_##type); \
|
||||
}
|
||||
|
||||
void parse_statement(const char *statement) {
|
||||
char *action, *remain;
|
||||
|
||||
// strtok will modify strings, duplicate the statement
|
||||
string stmt(statement);
|
||||
|
||||
action = strtok_r(stmt.data(), " ", &remain);
|
||||
|
||||
if (remain == nullptr) {
|
||||
LOGE("Syntax error in '%s'\n\n", statement);
|
||||
return;
|
||||
}
|
||||
|
||||
if (0) {}
|
||||
add_action("allow", 1, 0)
|
||||
add_action("deny", 1, 1)
|
||||
add_action("auditallow", 1, 2)
|
||||
add_action("dontaudit", 1, 3)
|
||||
add_action("allowxperm", 2, 0)
|
||||
add_action("auditallowxperm", 2, 1)
|
||||
add_action("dontauditxperm", 2, 2)
|
||||
add_action("create", 3, 0)
|
||||
add_action("permissive", 3, 1)
|
||||
add_action("enforce", 3, 2)
|
||||
add_action("attradd", 4, 0)
|
||||
add_action("type_transition", 5, 0)
|
||||
add_action("type_change", 5, 1)
|
||||
add_action("type_member", 5, 2)
|
||||
add_action("name_transition", 6, 0)
|
||||
else { LOGW("Unknown statement: '%s'\n\n", statement); }
|
||||
}
|
||||
|
||||
void load_rule_file(const char *file) {
|
||||
file_readline(true, file, [](string_view line) -> bool {
|
||||
if (line.empty() || line[0] == '#')
|
||||
return true;
|
||||
parse_statement(line.data());
|
||||
return true;
|
||||
});
|
||||
}
|
@ -1,8 +1,4 @@
|
||||
/* file.cpp - Contains all files related utilities
|
||||
*/
|
||||
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
@ -359,7 +355,7 @@ void write_zero(int fd, size_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
void file_readline(const char *file, const function<bool (string_view)> &fn, bool trim) {
|
||||
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn) {
|
||||
FILE *fp = xfopen(file, "re");
|
||||
if (fp == nullptr)
|
||||
return;
|
||||
@ -384,7 +380,7 @@ void file_readline(const char *file, const function<bool (string_view)> &fn, boo
|
||||
}
|
||||
|
||||
void parse_prop_file(const char *file, const function<bool (string_view, string_view)> &fn) {
|
||||
file_readline(file, [&](string_view line_view) -> bool {
|
||||
file_readline(true, file, [&](string_view line_view) -> bool {
|
||||
char *line = (char *) line_view.data();
|
||||
if (line[0] == '#')
|
||||
return true;
|
||||
@ -393,7 +389,7 @@ void parse_prop_file(const char *file, const function<bool (string_view, string_
|
||||
return true;
|
||||
*eql = '\0';
|
||||
return fn(line, eql + 1);
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
void parse_mnt(const char *file, const function<bool (mntent*)> &fn) {
|
||||
|
@ -1,17 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <mntent.h>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
#include "xwrap.h"
|
||||
|
||||
#define do_align(p, a) (((p) + (a) - 1) / (a) * (a))
|
||||
#define align_off(p, a) (do_align(p, a) - (p))
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
static inline sFILE make_sFILE(FILE *fp = nullptr) {
|
||||
return sFILE(fp, fclose);
|
||||
}
|
||||
|
||||
struct file_attr {
|
||||
struct stat st;
|
||||
char con[128];
|
||||
@ -36,12 +35,15 @@ void clone_attr(const char *source, const char *target);
|
||||
void fd_full_read(int fd, void **buf, size_t *size);
|
||||
void full_read(const char *filename, void **buf, size_t *size);
|
||||
void write_zero(int fd, size_t size);
|
||||
|
||||
void file_readline(const char *file, const std::function<bool (std::string_view)> &fn, bool trim = false);
|
||||
void parse_prop_file(const char *file, const std::function
|
||||
<bool(std::string_view, std::string_view)> &fn);
|
||||
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
|
||||
static inline void file_readline(const char *file,
|
||||
const std::function<bool(std::string_view)> &fn) {
|
||||
file_readline(false, file, fn);
|
||||
}
|
||||
void parse_prop_file(const char *file,
|
||||
const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||
void *__mmap(const char *filename, size_t *size, bool rw);
|
||||
void frm_rf(int dirfd, std::initializer_list<const char *> excl = std::initializer_list<const char *>());
|
||||
void frm_rf(int dirfd, std::initializer_list<const char *> excl = {});
|
||||
void clone_dir(int src, int dest, bool overwrite = true);
|
||||
void parse_mnt(const char *file, const std::function<bool(mntent*)> &fn);
|
||||
|
||||
@ -80,3 +82,22 @@ void mmap_rw(const char *filename, B &buf, L &sz) {
|
||||
buf = (B) __mmap(filename, &__sz, true);
|
||||
sz = __sz;
|
||||
}
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
|
||||
static inline sDIR open_dir(const char *path) {
|
||||
return sDIR(opendir(path), closedir);
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(const char *path) {
|
||||
return sDIR(xopendir(path), closedir);
|
||||
}
|
||||
|
||||
static inline sFILE open_file(const char *path, const char *mode) {
|
||||
return sFILE(fopen(path, mode), fclose);
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(const char *path, const char *mode) {
|
||||
return sFILE(xfopen(path, mode), fclose);
|
||||
}
|
||||
|
@ -5,17 +5,6 @@
|
||||
|
||||
#include "../files.h"
|
||||
|
||||
class stream;
|
||||
|
||||
using stream_ptr = std::unique_ptr<stream>;
|
||||
|
||||
sFILE make_stream(stream_ptr &&strm);
|
||||
|
||||
template <class T, class... Args>
|
||||
sFILE make_stream(Args &&... args) {
|
||||
return make_stream(stream_ptr(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
class stream {
|
||||
public:
|
||||
virtual int read(void *buf, size_t len);
|
||||
@ -24,35 +13,22 @@ public:
|
||||
virtual ~stream() = default;
|
||||
};
|
||||
|
||||
// Delegates all operations to the base FILE pointer
|
||||
using stream_ptr = std::unique_ptr<stream>;
|
||||
|
||||
// Delegates all operations to base stream
|
||||
class filter_stream : public stream {
|
||||
public:
|
||||
filter_stream(sFILE &&fp = make_sFILE()) : fp(std::move(fp)) {}
|
||||
filter_stream(stream_ptr &&base) : base(std::move(base)) {}
|
||||
|
||||
int read(void *buf, size_t len) override;
|
||||
int write(const void *buf, size_t len) override;
|
||||
|
||||
void set_base(sFILE &&f);
|
||||
template <class T, class... Args >
|
||||
void set_base(Args&&... args) {
|
||||
set_base(make_stream<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
protected:
|
||||
sFILE fp;
|
||||
};
|
||||
|
||||
// Handy interface for classes that need custom seek logic
|
||||
class seekable_stream : public stream {
|
||||
protected:
|
||||
size_t _pos = 0;
|
||||
|
||||
off_t seek_pos(off_t off, int whence);
|
||||
virtual size_t end_pos() = 0;
|
||||
stream_ptr base;
|
||||
};
|
||||
|
||||
// Byte stream that dynamically allocates memory
|
||||
class byte_stream : public seekable_stream {
|
||||
class byte_stream : public stream {
|
||||
public:
|
||||
byte_stream(uint8_t *&buf, size_t &len);
|
||||
template <class byte>
|
||||
@ -64,10 +40,10 @@ public:
|
||||
private:
|
||||
uint8_t *&_buf;
|
||||
size_t &_len;
|
||||
size_t _pos = 0;
|
||||
size_t _cap = 0;
|
||||
|
||||
void resize(size_t new_pos, bool zero = false);
|
||||
size_t end_pos() final { return _len; }
|
||||
};
|
||||
|
||||
// File stream but does not close the file descriptor at any time
|
||||
@ -81,3 +57,28 @@ public:
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
/* ****************************************
|
||||
* Bridge between stream class and C stdio
|
||||
* ****************************************/
|
||||
|
||||
// sFILE -> stream_ptr
|
||||
class fp_stream final : public stream {
|
||||
public:
|
||||
fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {}
|
||||
fp_stream(sFILE &&fp) : fp(std::move(fp)) {}
|
||||
int read(void *buf, size_t len) override;
|
||||
int write(const void *buf, size_t len) override;
|
||||
off_t seek(off_t off, int whence) override;
|
||||
|
||||
private:
|
||||
sFILE fp;
|
||||
};
|
||||
|
||||
// stream_ptr -> sFILE
|
||||
sFILE make_stream_fp(stream_ptr &&strm);
|
||||
|
||||
template <class T, class... Args>
|
||||
sFILE make_stream_fp(Args &&... args) {
|
||||
return make_stream_fp(stream_ptr(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
@ -1,14 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <poll.h>
|
||||
#include <mntent.h>
|
||||
|
||||
#include "../missing.h"
|
||||
#include "../xwrap.h"
|
||||
#include "../files.h"
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
@ -55,10 +55,7 @@ static int __setcon(const char *ctx) {
|
||||
static int __getfilecon(const char *path, char **ctx) {
|
||||
char buf[1024];
|
||||
int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
|
||||
if (rc < 0) {
|
||||
errno = -rc;
|
||||
return -1;
|
||||
}
|
||||
if (rc >= 0)
|
||||
*ctx = strdup(buf);
|
||||
return rc;
|
||||
}
|
||||
@ -66,10 +63,7 @@ static int __getfilecon(const char *path, char **ctx) {
|
||||
static int __lgetfilecon(const char *path, char **ctx) {
|
||||
char buf[1024];
|
||||
int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
|
||||
if (rc < 0) {
|
||||
errno = -rc;
|
||||
return -1;
|
||||
}
|
||||
if (rc >= 0)
|
||||
*ctx = strdup(buf);
|
||||
return rc;
|
||||
}
|
||||
@ -77,39 +71,21 @@ static int __lgetfilecon(const char *path, char **ctx) {
|
||||
static int __fgetfilecon(int fd, char **ctx) {
|
||||
char buf[1024];
|
||||
int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
|
||||
if (rc < 0) {
|
||||
errno = -rc;
|
||||
return -1;
|
||||
}
|
||||
if (rc >= 0)
|
||||
*ctx = strdup(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __setfilecon(const char *path, const char *ctx) {
|
||||
int rc = syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
if (rc) {
|
||||
errno = -rc;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
}
|
||||
|
||||
static int __lsetfilecon(const char *path, const char *ctx) {
|
||||
int rc = syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
if (rc) {
|
||||
errno = -rc;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
}
|
||||
|
||||
static int __fsetfilecon(int fd, const char *ctx) {
|
||||
int rc = syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
if (rc) {
|
||||
errno = -rc;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
}
|
||||
|
||||
// Function pointers
|
||||
@ -231,11 +207,10 @@ void restore_rootcon() {
|
||||
setfilecon(MIRRDIR, ROOT_CON);
|
||||
setfilecon(BLOCKDIR, ROOT_CON);
|
||||
|
||||
struct dirent *entry;
|
||||
DIR *dir = xopendir("/sbin");
|
||||
int dfd = dirfd(dir);
|
||||
auto dir = xopen_dir("/sbin");
|
||||
int dfd = dirfd(dir.get());
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
||||
continue;
|
||||
setfilecon_at(dfd, entry->d_name, ROOT_CON);
|
||||
@ -244,6 +219,4 @@ void restore_rootcon() {
|
||||
setfilecon("/sbin/magisk.bin", MAGISK_CON);
|
||||
setfilecon("/sbin/magisk", MAGISK_CON);
|
||||
setfilecon("/sbin/magiskinit", MAGISK_CON);
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ static int strm_close(void *v) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sFILE make_stream(stream_ptr &&strm) {
|
||||
sFILE make_stream_fp(stream_ptr &&strm) {
|
||||
sFILE fp(funopen(strm.release(), strm_read, strm_write, strm_seek, strm_close), fclose);
|
||||
setbuf(fp.get(), nullptr);
|
||||
return fp;
|
||||
@ -44,29 +44,24 @@ off_t stream::seek(off_t off, int whence) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int filter_stream::read(void *buf, size_t len) {
|
||||
int fp_stream::read(void *buf, size_t len) {
|
||||
return fread(buf, 1, len, fp.get());
|
||||
}
|
||||
|
||||
int filter_stream::write(const void *buf, size_t len) {
|
||||
int fp_stream::write(const void *buf, size_t len) {
|
||||
return fwrite(buf, 1, len, fp.get());
|
||||
}
|
||||
|
||||
void filter_stream::set_base(sFILE &&f) {
|
||||
fp = std::move(f);
|
||||
off_t fp_stream::seek(off_t off, int whence) {
|
||||
return fseek(fp.get(), off, whence);
|
||||
}
|
||||
|
||||
off_t seekable_stream::seek_pos(off_t off, int whence) {
|
||||
switch (whence) {
|
||||
case SEEK_CUR:
|
||||
return _pos + off;
|
||||
case SEEK_END:
|
||||
return end_pos() + off;
|
||||
case SEEK_SET:
|
||||
return off;
|
||||
default:
|
||||
return -1;
|
||||
int filter_stream::read(void *buf, size_t len) {
|
||||
return base->read(buf, len);
|
||||
}
|
||||
|
||||
int filter_stream::write(const void *buf, size_t len) {
|
||||
return base->write(buf, len);
|
||||
}
|
||||
|
||||
byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) {
|
||||
@ -89,9 +84,20 @@ int byte_stream::write(const void *buf, size_t len) {
|
||||
}
|
||||
|
||||
off_t byte_stream::seek(off_t off, int whence) {
|
||||
off_t np = seek_pos(off, whence);
|
||||
if (np < 0)
|
||||
off_t np;
|
||||
switch (whence) {
|
||||
case SEEK_CUR:
|
||||
np = _pos + off;
|
||||
break;
|
||||
case SEEK_END:
|
||||
np = _len + off;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
np = off;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
resize(np, true);
|
||||
_pos = np;
|
||||
return np;
|
||||
|
@ -1,17 +1,6 @@
|
||||
/* xwrap.cpp - wrappers around existing library functions.
|
||||
*
|
||||
* Functions with the x prefix are wrappers that either succeed or log the
|
||||
* error message. They usually have the same arguments and return value
|
||||
* as the function they wrap.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
|
@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode);
|
||||
FILE *xfdopen(int fd, const char *mode);
|
||||
int xopen(const char *pathname, int flags);
|
||||
|
@ -70,9 +70,8 @@ main() {
|
||||
|
||||
remove_system_su
|
||||
find_manager_apk
|
||||
patch_boot_image
|
||||
install_magisk
|
||||
|
||||
cd /
|
||||
# Cleanups
|
||||
$BOOTMODE || recovery_cleanup
|
||||
rm -rf $TMPDIR
|
||||
|
@ -13,20 +13,15 @@
|
||||
#
|
||||
# File name Type Description
|
||||
#
|
||||
# boot_patch.sh script A script to patch boot. Expect path to boot image as parameter.
|
||||
# boot_patch.sh script A script to patch boot image for Magisk.
|
||||
# (this file) The script will use binaries and files in its same directory
|
||||
# to complete the patching process
|
||||
# util_functions.sh script A script which hosts all functions requires for this script
|
||||
# util_functions.sh script A script which hosts all functions required for this script
|
||||
# to work properly
|
||||
# magiskinit binary The binary to replace /init, which has the magisk binary embedded
|
||||
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk,
|
||||
# and patch the ramdisk for Magisk support
|
||||
# chromeos folder This folder should store all the utilities and keys to sign
|
||||
# (optional) a chromeos device. Used for Pixel C
|
||||
#
|
||||
# If the script is not running as root, then the input boot image should be a stock image
|
||||
# or have a backup included in ramdisk internally, since we cannot access the stock boot
|
||||
# image placed under /data we've created when previously installed
|
||||
# magiskinit binary The binary to replace /init; magisk binary embedded
|
||||
# magiskboot binary A tool to manipulate boot images
|
||||
# chromeos folder This folder includes all the utilities and keys to sign
|
||||
# (optional) chromeos boot images. Currently only used for Pixel C
|
||||
#
|
||||
##########################################################################################
|
||||
##########################################################################################
|
||||
@ -104,10 +99,8 @@ fi
|
||||
case $((STATUS & 3)) in
|
||||
0 ) # Stock boot
|
||||
ui_print "- Stock boot image detected"
|
||||
ui_print "- Backing up stock boot image"
|
||||
SHA1=`./magiskboot sha1 "$BOOTIMAGE" 2>/dev/null`
|
||||
STOCKDUMP=stock_boot_${SHA1}.img.gz
|
||||
./magiskboot compress "$BOOTIMAGE" $STOCKDUMP
|
||||
cat $BOOTIMAGE > stock_boot.img
|
||||
cp -af ramdisk.cpio ramdisk.cpio.orig 2>/dev/null
|
||||
;;
|
||||
1 ) # Magisk patched
|
||||
@ -158,7 +151,7 @@ rm -f ramdisk.cpio.orig config
|
||||
##########################################################################################
|
||||
|
||||
for dt in dtb kernel_dtb extra recovery_dtbo; do
|
||||
[ -f $dt ] && ./magiskboot dtb $dt patch && ui_print "- Patching fstab in $dt"
|
||||
[ -f $dt ] && ./magiskboot dtb $dt patch && ui_print "- Patch fstab in $dt"
|
||||
done
|
||||
|
||||
if [ -f kernel ]; then
|
||||
|
@ -53,7 +53,7 @@ ui_print "- Target image: $BOOTIMAGE"
|
||||
# Detect version and architecture
|
||||
api_level_arch_detect
|
||||
|
||||
[ $API -lt 17 ] && abort "! Magisk is only for Android 4.2 and above"
|
||||
[ $API -lt 17 ] && abort "! Magisk only support Android 4.2 and above"
|
||||
|
||||
ui_print "- Device platform: $ARCH"
|
||||
|
||||
@ -108,9 +108,8 @@ $BOOTMODE || recovery_actions
|
||||
# Boot/DTBO Patching
|
||||
##########################################################################################
|
||||
|
||||
patch_boot_image
|
||||
install_magisk
|
||||
|
||||
cd /
|
||||
# Cleanups
|
||||
$BOOTMODE || recovery_cleanup
|
||||
rm -rf $TMPDIR
|
||||
|
@ -15,6 +15,7 @@ TMPDIR=/dev/tmp
|
||||
|
||||
INSTALLER=$TMPDIR/install
|
||||
CHROMEDIR=$INSTALLER/chromeos
|
||||
PERSISTDIR=/sbin/.magisk/mirror/persist
|
||||
|
||||
# Default permissions
|
||||
umask 022
|
||||
@ -50,6 +51,7 @@ chmod -R 755 $MAGISKBIN
|
||||
check_data
|
||||
$DATA_DE || abort "! Cannot access /data, please uninstall with Magisk Manager"
|
||||
$BOOTMODE || recovery_actions
|
||||
run_migrations
|
||||
|
||||
##########################################################################################
|
||||
# Uninstall
|
||||
@ -57,7 +59,6 @@ $BOOTMODE || recovery_actions
|
||||
|
||||
get_flags
|
||||
find_boot_image
|
||||
find_dtbo_image
|
||||
|
||||
[ -e $BOOTIMAGE ] || abort "! Unable to detect boot image"
|
||||
ui_print "- Found target image: $BOOTIMAGE"
|
||||
@ -96,16 +97,18 @@ case $((STATUS & 3)) in
|
||||
1 ) # Magisk patched
|
||||
ui_print "- Magisk patched image detected"
|
||||
# Find SHA1 of stock boot image
|
||||
[ -z $SHA1 ] && SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
|
||||
STOCKDTBO=/data/stock_dtbo.img.gz
|
||||
if [ -f $STOCKBOOT ]; then
|
||||
SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null`
|
||||
BACKUPDIR=/data/magisk_backup_$SHA1
|
||||
if [ -d $BACKUPDIR ]; then
|
||||
ui_print "- Restoring stock boot image"
|
||||
flash_image $STOCKBOOT $BOOTIMAGE
|
||||
if [ -f $STOCKDTBO -a -b "$DTBOIMAGE" ]; then
|
||||
ui_print "- Restoring stock dtbo image"
|
||||
flash_image $STOCKDTBO $DTBOIMAGE
|
||||
fi
|
||||
flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE
|
||||
for name in dtb dtbo; do
|
||||
[ -f $BACKUPDIR/${name}.img.gz ] || continue
|
||||
IMAGE=`find_block $name$SLOT`
|
||||
[ -z $IMAGE ] && continue
|
||||
ui_print "- Restoring stock $name image"
|
||||
flash_image $BACKUPDIR/${name}.img.gz $IMAGE
|
||||
done
|
||||
else
|
||||
ui_print "! Boot image backup unavailable"
|
||||
ui_print "- Restoring ramdisk with internal backup"
|
||||
@ -128,9 +131,10 @@ case $((STATUS & 3)) in
|
||||
esac
|
||||
|
||||
ui_print "- Removing Magisk files"
|
||||
rm -rf /cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \
|
||||
rm -rf \
|
||||
/cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \
|
||||
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* \
|
||||
/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* 2>/dev/null
|
||||
/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* $PERSISTDIR/magisk 2>/dev/null
|
||||
|
||||
if [ -f /system/addon.d/99-magisk.sh ]; then
|
||||
mount -o rw,remount /system
|
||||
|
@ -1,12 +1,15 @@
|
||||
#!/sbin/sh
|
||||
|
||||
TMPDIR=/dev/tmp
|
||||
MOUNTPATH=/dev/magisk_img
|
||||
#################
|
||||
# Initialization
|
||||
#################
|
||||
|
||||
# Default permissions
|
||||
umask 022
|
||||
|
||||
# Initial cleanup
|
||||
# Global vars
|
||||
TMPDIR=/dev/tmp
|
||||
PERSISTDIR=/sbin/.magisk/mirror/persist
|
||||
|
||||
rm -rf $TMPDIR 2>/dev/null
|
||||
mkdir -p $TMPDIR
|
||||
|
||||
@ -14,20 +17,33 @@ mkdir -p $TMPDIR
|
||||
ui_print() { echo "$1"; }
|
||||
|
||||
require_new_magisk() {
|
||||
ui_print "***********************************"
|
||||
ui_print " Please install the latest Magisk! "
|
||||
ui_print "***********************************"
|
||||
ui_print "*******************************"
|
||||
ui_print " Please install Magisk v19.0+! "
|
||||
ui_print "*******************************"
|
||||
exit 1
|
||||
}
|
||||
|
||||
imageless_magisk() {
|
||||
[ $MAGISK_VER_CODE -gt 18100 ]
|
||||
is_legacy_script() {
|
||||
unzip -l "$ZIPFILE" install.sh | grep -q install.sh
|
||||
return $?
|
||||
}
|
||||
|
||||
##########################################################################################
|
||||
print_modname() {
|
||||
local len
|
||||
len=`echo -n $MODNAME | wc -c`
|
||||
len=$((len + 2))
|
||||
local pounds=`printf "%${len}s" | tr ' ' '*'`
|
||||
ui_print "$pounds"
|
||||
ui_print " $MODNAME "
|
||||
ui_print "$pounds"
|
||||
ui_print "*******************"
|
||||
ui_print " Powered by Magisk "
|
||||
ui_print "*******************"
|
||||
}
|
||||
|
||||
##############
|
||||
# Environment
|
||||
##########################################################################################
|
||||
##############
|
||||
|
||||
OUTFD=$2
|
||||
ZIPFILE=$3
|
||||
@ -35,12 +51,9 @@ ZIPFILE=$3
|
||||
mount /data 2>/dev/null
|
||||
|
||||
# Load utility functions
|
||||
if [ -f /data/adb/magisk/util_functions.sh ]; then
|
||||
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
|
||||
. /data/adb/magisk/util_functions.sh
|
||||
NVBASE=/data/adb
|
||||
else
|
||||
require_new_magisk
|
||||
fi
|
||||
[ $MAGISK_VER_CODE -gt 18100 ] || require_new_magisk
|
||||
|
||||
# Preperation for flashable zips
|
||||
setup_flashable
|
||||
@ -54,98 +67,107 @@ api_level_arch_detect
|
||||
# Setup busybox and binaries
|
||||
$BOOTMODE && boot_actions || recovery_actions
|
||||
|
||||
##########################################################################################
|
||||
##############
|
||||
# Preparation
|
||||
##########################################################################################
|
||||
##############
|
||||
|
||||
# Extract common files
|
||||
unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2
|
||||
# Extract prop file
|
||||
unzip -o "$ZIPFILE" module.prop -d $TMPDIR >&2
|
||||
[ ! -f $TMPDIR/module.prop ] && abort "! Unable to extract zip file!"
|
||||
|
||||
[ ! -f $TMPDIR/install.sh ] && abort "! Unable to extract zip file!"
|
||||
# Load install script
|
||||
. $TMPDIR/install.sh
|
||||
|
||||
if imageless_magisk; then
|
||||
$BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules
|
||||
MODULEROOT=$NVBASE/$MODDIRNAME
|
||||
else
|
||||
$BOOTMODE && IMGNAME=magisk_merge.img || IMGNAME=magisk.img
|
||||
IMG=$NVBASE/$IMGNAME
|
||||
request_zip_size_check "$ZIPFILE"
|
||||
mount_magisk_img
|
||||
MODULEROOT=$MOUNTPATH
|
||||
fi
|
||||
|
||||
MODID=`grep_prop id $TMPDIR/module.prop`
|
||||
MODPATH=$MODULEROOT/$MODID
|
||||
|
||||
print_modname
|
||||
|
||||
ui_print "******************************"
|
||||
ui_print "Powered by Magisk (@topjohnwu)"
|
||||
ui_print "******************************"
|
||||
|
||||
##########################################################################################
|
||||
# Install
|
||||
##########################################################################################
|
||||
MODNAME=`grep_prop name $TMPDIR/module.prop`
|
||||
|
||||
# Create mod paths
|
||||
rm -rf $MODPATH 2>/dev/null
|
||||
mkdir -p $MODPATH
|
||||
|
||||
on_install
|
||||
##########
|
||||
# Install
|
||||
##########
|
||||
|
||||
# Remove placeholder
|
||||
rm -f $MODPATH/system/placeholder 2>/dev/null
|
||||
if is_legacy_script; then
|
||||
unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2
|
||||
|
||||
# Load install script
|
||||
. $TMPDIR/install.sh
|
||||
|
||||
# Callbacks
|
||||
print_modname
|
||||
on_install
|
||||
|
||||
# Custom uninstaller
|
||||
[ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh
|
||||
|
||||
# Auto Mount
|
||||
if imageless_magisk; then
|
||||
# Skip mount
|
||||
$SKIPMOUNT && touch $MODPATH/skip_mount
|
||||
else
|
||||
$SKIPMOUNT || touch $MODPATH/auto_mount
|
||||
fi
|
||||
|
||||
# prop files
|
||||
# prop file
|
||||
$PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop
|
||||
|
||||
# Module info
|
||||
cp -af $TMPDIR/module.prop $MODPATH/module.prop
|
||||
if $BOOTMODE; then
|
||||
# Update info for Magisk Manager
|
||||
if imageless_magisk; then
|
||||
mktouch $NVBASE/modules/$MODID/update
|
||||
cp -af $TMPDIR/module.prop $NVBASE/modules/$MODID/module.prop
|
||||
else
|
||||
mktouch /sbin/.magisk/img/$MODID/update
|
||||
cp -af $TMPDIR/module.prop /sbin/.magisk/img/$MODID/module.prop
|
||||
fi
|
||||
fi
|
||||
|
||||
# post-fs-data mode scripts
|
||||
# post-fs-data scripts
|
||||
$POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh
|
||||
|
||||
# service mode scripts
|
||||
# service scripts
|
||||
$LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh
|
||||
|
||||
# Handle replace folders
|
||||
for TARGET in $REPLACE; do
|
||||
mktouch $MODPATH$TARGET/.replace
|
||||
done
|
||||
|
||||
ui_print "- Setting permissions"
|
||||
set_permissions
|
||||
else
|
||||
print_modname
|
||||
|
||||
##########################################################################################
|
||||
unzip -o "$ZIPFILE" customize.sh -d $MODPATH >&2
|
||||
|
||||
if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null; then
|
||||
ui_print "- Extracting module files"
|
||||
unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2
|
||||
|
||||
# Default permissions
|
||||
set_perm_recursive $MODPATH 0 0 0755 0644
|
||||
fi
|
||||
|
||||
# Load customization script
|
||||
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
|
||||
fi
|
||||
|
||||
# Handle replace folders
|
||||
for TARGET in $REPLACE; do
|
||||
ui_print "- Replace target: $TARGET"
|
||||
mktouch $MODPATH$TARGET/.replace
|
||||
done
|
||||
|
||||
if $BOOTMODE; then
|
||||
# Update info for Magisk Manager
|
||||
mktouch $NVBASE/modules/$MODID/update
|
||||
cp -af $MODPATH/module.prop $NVBASE/modules/$MODID/module.prop
|
||||
fi
|
||||
|
||||
# Copy over custom sepolicy rules
|
||||
if [ -f $MODPATH/sepolicy.rule -a -e $PERSISTDIR ]; then
|
||||
ui_print "- Installing custom sepolicy patch"
|
||||
PERSISTMOD=$PERSISTDIR/magisk/$MODID
|
||||
mkdir -p $PERSISTMOD
|
||||
cp -af $MODPATH/sepolicy.rule $PERSISTMOD/sepolicy.rule
|
||||
fi
|
||||
|
||||
# Remove stuffs that don't belong to modules
|
||||
rm -rf \
|
||||
$MODPATH/system/placeholder $MODPATH/customize.sh \
|
||||
$MODPATH/README.md $MODPATH/.git* 2>/dev/null
|
||||
|
||||
##############
|
||||
# Finalizing
|
||||
##########################################################################################
|
||||
##############
|
||||
|
||||
cd /
|
||||
imageless_magisk || unmount_magisk_img
|
||||
$BOOTMODE || recovery_cleanup
|
||||
rm -rf $TMPDIR $MOUNTPATH
|
||||
rm -rf $TMPDIR
|
||||
|
||||
ui_print "- Done"
|
||||
exit 0
|
||||
|
@ -5,27 +5,8 @@
|
||||
#
|
||||
#########################################
|
||||
|
||||
##########
|
||||
# Presets
|
||||
##########
|
||||
|
||||
#MAGISK_VERSION_STUB
|
||||
|
||||
# Detect whether in boot mode
|
||||
[ -z $BOOTMODE ] && BOOTMODE=false
|
||||
$BOOTMODE || ps | grep zygote | grep -qv grep && BOOTMODE=true
|
||||
$BOOTMODE || ps -A 2>/dev/null | grep zygote | grep -qv grep && BOOTMODE=true
|
||||
|
||||
# Presets
|
||||
MAGISKTMP=/sbin/.magisk
|
||||
NVBASE=/data/adb
|
||||
[ -z $TMPDIR ] && TMPDIR=/dev/tmp
|
||||
|
||||
# Bootsigner related stuff
|
||||
BOOTSIGNERCLASS=a.a
|
||||
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK \$BOOTSIGNERCLASS"
|
||||
BOOTSIGNED=false
|
||||
|
||||
###################
|
||||
# Helper Functions
|
||||
###################
|
||||
@ -128,15 +109,15 @@ recovery_actions() {
|
||||
}
|
||||
|
||||
recovery_cleanup() {
|
||||
ui_print "- Unmounting partitions"
|
||||
umount -l /system 2>/dev/null
|
||||
umount -l /system_root 2>/dev/null
|
||||
umount -l /vendor 2>/dev/null
|
||||
umount -l /dev/random 2>/dev/null
|
||||
export PATH=$OLD_PATH
|
||||
[ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB
|
||||
[ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE
|
||||
[ -z $OLD_LD_CFG ] || export LD_CONFIG_FILE=$OLD_LD_CFG
|
||||
ui_print "- Unmounting partitions"
|
||||
umount -l /system_root 2>/dev/null
|
||||
umount -l /system 2>/dev/null
|
||||
umount -l /vendor 2>/dev/null
|
||||
umount -l /dev/random 2>/dev/null
|
||||
}
|
||||
|
||||
#######################
|
||||
@ -165,19 +146,29 @@ find_block() {
|
||||
return 1
|
||||
}
|
||||
|
||||
mount_part() {
|
||||
$BOOTMODE && return
|
||||
# mount_name <partname> <mountpoint> <flag>
|
||||
mount_name() {
|
||||
local PART=$1
|
||||
local POINT=/${PART}
|
||||
local POINT=$2
|
||||
local FLAG=$3
|
||||
[ -L $POINT ] && rm -f $POINT
|
||||
mkdir $POINT 2>/dev/null
|
||||
mkdir -p $POINT 2>/dev/null
|
||||
is_mounted $POINT && return
|
||||
ui_print "- Mounting $PART"
|
||||
mount -o ro $POINT 2>/dev/null
|
||||
ui_print "- Mounting $POINT"
|
||||
# First try mounting with fstab
|
||||
mount $FLAG $POINT 2>/dev/null
|
||||
if ! is_mounted $POINT; then
|
||||
local BLOCK=`find_block $PART$SLOT`
|
||||
mount -o ro $BLOCK $POINT
|
||||
local BLOCK=`find_block $PART`
|
||||
mount $FLAG $BLOCK $POINT
|
||||
fi
|
||||
}
|
||||
|
||||
mount_ro_ensure() {
|
||||
# We handle ro partitions only in recovery
|
||||
$BOOTMODE && return
|
||||
local PART=$1$SLOT
|
||||
local POINT=/$1
|
||||
mount_name $PART $POINT '-o ro'
|
||||
is_mounted $POINT || abort "! Cannot mount $POINT"
|
||||
}
|
||||
|
||||
@ -190,7 +181,8 @@ mount_partitions() {
|
||||
fi
|
||||
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
|
||||
|
||||
mount_part system
|
||||
# Mount ro partitions
|
||||
mount_ro_ensure system
|
||||
if [ -f /system/init.rc ]; then
|
||||
SYSTEM_ROOT=true
|
||||
[ -L /system_root ] && rm -f /system_root
|
||||
@ -201,8 +193,20 @@ mount_partitions() {
|
||||
grep ' / ' /proc/mounts | grep -qv 'rootfs' || grep -q ' /system_root ' /proc/mounts \
|
||||
&& SYSTEM_ROOT=true || SYSTEM_ROOT=false
|
||||
fi
|
||||
[ -L /system/vendor ] && mount_part vendor
|
||||
[ -L /system/vendor ] && mount_ro_ensure vendor
|
||||
$SYSTEM_ROOT && ui_print "- Device is system-as-root"
|
||||
|
||||
# Mount persist partition in recovery
|
||||
if ! $BOOTMODE && [ ! -z $PERSISTDIR ]; then
|
||||
# Try to mount persist
|
||||
PERSISTDIR=/persist
|
||||
mount_name persist /persist
|
||||
if ! is_mounted /persist; then
|
||||
# Fallback to cache
|
||||
mount_name cache /cache
|
||||
is_mounted /cache && PERSISTDIR=/cache || PERSISTDIR=
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
get_flags() {
|
||||
@ -272,30 +276,29 @@ flash_image() {
|
||||
return 0
|
||||
}
|
||||
|
||||
find_dtbo_image() {
|
||||
DTBOIMAGE=`find_block dtbo$SLOT`
|
||||
}
|
||||
|
||||
patch_dtbo_image() {
|
||||
find_dtbo_image
|
||||
if [ ! -z $DTBOIMAGE ]; then
|
||||
ui_print "- DTBO image: $DTBOIMAGE"
|
||||
local PATCHED=$TMPDIR/dtbo
|
||||
if $MAGISKBIN/magiskboot dtb $DTBOIMAGE patch $PATCHED; then
|
||||
ui_print "- Backing up stock DTBO image"
|
||||
$MAGISKBIN/magiskboot compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
|
||||
ui_print "- Patching DTBO to remove avb-verity"
|
||||
cat $PATCHED /dev/zero > $DTBOIMAGE
|
||||
rm -f $PATCHED
|
||||
return 0
|
||||
patch_dtb_partitions() {
|
||||
local result=1
|
||||
cd $MAGISKBIN
|
||||
for name in dtb dtbo; do
|
||||
local IMAGE=`find_block $name$SLOT`
|
||||
if [ ! -z $IMAGE ]; then
|
||||
ui_print "- $name image: $IMAGE"
|
||||
if ./magiskboot dtb $IMAGE patch dt.patched; then
|
||||
result=0
|
||||
ui_print "- Backing up stock $name image"
|
||||
cat $IMAGE > stock_${name}.img
|
||||
ui_print "- Flashing patched $name"
|
||||
cat dt.patched /dev/zero > $IMAGE
|
||||
rm -f dt.patched
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
done
|
||||
cd /
|
||||
return $result
|
||||
}
|
||||
|
||||
patch_boot_image() {
|
||||
# Common installation script for flash_script.sh (updater-script) and addon.d.sh
|
||||
SOURCEDMODE=true
|
||||
# Common installation script for flash_script.sh and addon.d.sh
|
||||
install_magisk() {
|
||||
cd $MAGISKBIN
|
||||
|
||||
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
|
||||
@ -304,6 +307,7 @@ patch_boot_image() {
|
||||
$IS64BIT && mv -f magiskinit64 magiskinit 2>/dev/null || rm -f magiskinit64
|
||||
|
||||
# Source the boot patcher
|
||||
SOURCEDMODE=true
|
||||
. ./boot_patch.sh "$BOOTIMAGE"
|
||||
|
||||
ui_print "- Flashing new boot image"
|
||||
@ -318,18 +322,8 @@ patch_boot_image() {
|
||||
./magiskboot cleanup
|
||||
rm -f new-boot.img
|
||||
|
||||
if [ -f stock_boot* ]; then
|
||||
rm -f /data/stock_boot* 2>/dev/null
|
||||
$DATA && mv stock_boot* /data
|
||||
fi
|
||||
|
||||
# Patch DTBO together with boot image
|
||||
$KEEPVERITY || patch_dtbo_image
|
||||
|
||||
if [ -f stock_dtbo* ]; then
|
||||
rm -f /data/stock_dtbo* 2>/dev/null
|
||||
$DATA && mv stock_dtbo* /data
|
||||
fi
|
||||
patch_dtb_partitions
|
||||
run_migrations
|
||||
}
|
||||
|
||||
sign_chromeos() {
|
||||
@ -411,6 +405,41 @@ find_manager_apk() {
|
||||
[ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner"
|
||||
}
|
||||
|
||||
run_migrations() {
|
||||
local LOCSHA1
|
||||
local TARGET
|
||||
# Legacy app installation
|
||||
local BACKUP=/data/adb/magisk/stock_boot*.gz
|
||||
if [ -f $BACKUP ]; then
|
||||
cp $BACKUP /data
|
||||
rm -f $BACKUP
|
||||
fi
|
||||
|
||||
# Legacy backup
|
||||
for gz in /data/stock_boot*.gz; do
|
||||
[ -f $gz ] || break
|
||||
LOCSHA1=`basename $gz | sed -e 's/stock_boot_//' -e 's/.img.gz//'`
|
||||
[ -z $LOCSHA1 ] && break
|
||||
mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null
|
||||
mv $gz /data/magisk_backup_${LOCSHA1}/boot.img.gz
|
||||
done
|
||||
|
||||
# Stock backups
|
||||
LOCSHA1=$SHA1
|
||||
for name in boot dtb dtbo; do
|
||||
BACKUP=/data/adb/magisk/stock_${name}.img
|
||||
[ -f $BACKUP ] || continue
|
||||
if [ $name = 'boot' ]; then
|
||||
LOCSHA1=`$MAGISKBIN/magiskboot sha1 $BACKUP`
|
||||
mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null
|
||||
fi
|
||||
TARGET=/data/magisk_backup_${LOCSHA1}/${name}.img
|
||||
cp $BACKUP $TARGET
|
||||
rm -f $BACKUP
|
||||
gzip -9f $TARGET
|
||||
done
|
||||
}
|
||||
|
||||
#################
|
||||
# Module Related
|
||||
#################
|
||||
@ -446,27 +475,24 @@ request_zip_size_check() {
|
||||
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
|
||||
}
|
||||
|
||||
##################################
|
||||
# Backwards Compatibile Functions
|
||||
##################################
|
||||
|
||||
get_outfd() { setup_flashable; }
|
||||
|
||||
mount_magisk_img() {
|
||||
$BOOTMODE && MODULE_BASE=modules_update || MODULE_BASE=modules
|
||||
MODULEPATH=$NVBASE/$MODULE_BASE
|
||||
mkdir -p $MODULEPATH 2>/dev/null
|
||||
ln -s $MODULEPATH $MOUNTPATH
|
||||
}
|
||||
|
||||
unmount_magisk_img() {
|
||||
rm -f $MOUNTPATH 2>/dev/null
|
||||
}
|
||||
|
||||
boot_actions() { return; }
|
||||
|
||||
########
|
||||
# Setup
|
||||
########
|
||||
##########
|
||||
# Presets
|
||||
##########
|
||||
|
||||
# Detect whether in boot mode
|
||||
[ -z $BOOTMODE ] && ps | grep zygote | grep -qv grep && BOOTMODE=true
|
||||
[ -z $BOOTMODE ] && ps -A 2>/dev/null | grep zygote | grep -qv grep && BOOTMODE=true
|
||||
[ -z $BOOTMODE ] && BOOTMODE=false
|
||||
|
||||
MAGISKTMP=/sbin/.magisk
|
||||
NVBASE=/data/adb
|
||||
[ -z $TMPDIR ] && TMPDIR=/dev/tmp
|
||||
|
||||
# Bootsigner related stuff
|
||||
BOOTSIGNERCLASS=a.a
|
||||
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK \$BOOTSIGNERCLASS"
|
||||
BOOTSIGNED=false
|
||||
|
||||
resolve_vars
|
||||
|
@ -34,7 +34,7 @@ repositories {
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
|
||||
def bcVer = '1.63'
|
||||
def bcVer = '1.64'
|
||||
api "org.bouncycastle:bcprov-jdk15on:${bcVer}"
|
||||
api "org.bouncycastle:bcpkix-jdk15on:${bcVer}"
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Mettre à jour vers la version complête de Magisk Manager pour finir l\'installation. Télécharger et installer?</string>
|
||||
<string name="no_internet_msg">Veuillez vous connecter à Internet! Une mise à niveau complête vers le Gestionnaire Magisk est requise.</string>
|
||||
<string name="upgrade_msg">Une mise à niveau de Magisk Manager en version complète est nécessaire afin de terminer l’installation. Souhaitez‑vous procéder à son téléchargement et son installation ?</string>
|
||||
<string name="no_internet_msg">Veuillez vous connecter à Internet ! Une mise à niveau complète de Magisk Manager est requise.</string>
|
||||
<string name="dling">Téléchargement en cours</string>
|
||||
</resources>
|
||||
|
@ -1,4 +1,5 @@
|
||||
<resources>
|
||||
<string name="upgrade_msg">需要升級到完整版 Magisk Manager。是否下載並安裝?</string>
|
||||
<string name="no_internet_msg">請連上網路!升級到完整版 Magisk Manager 是必須的。</string>
|
||||
<string name="dling">下載中</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user