Commit bec5edca authored by topjohnwu's avatar topjohnwu

Avoiding using shell I/O

parent 6fb20b3e
...@@ -10,8 +10,9 @@ import androidx.multidex.MultiDex ...@@ -10,8 +10,9 @@ import androidx.multidex.MultiDex
import androidx.work.WorkManager import androidx.work.WorkManager
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.utils.AppShellInit
import com.topjohnwu.magisk.core.utils.BusyBoxInit
import com.topjohnwu.magisk.core.utils.IODispatcherExecutor import com.topjohnwu.magisk.core.utils.IODispatcherExecutor
import com.topjohnwu.magisk.core.utils.RootInit
import com.topjohnwu.magisk.core.utils.updateConfig import com.topjohnwu.magisk.core.utils.updateConfig
import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.magisk.ktx.unwrap import com.topjohnwu.magisk.ktx.unwrap
...@@ -32,7 +33,7 @@ open class App() : Application() { ...@@ -32,7 +33,7 @@ open class App() : Application() {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
Shell.setDefaultBuilder(Shell.Builder.create() Shell.setDefaultBuilder(Shell.Builder.create()
.setFlags(Shell.FLAG_MOUNT_MASTER) .setFlags(Shell.FLAG_MOUNT_MASTER)
.setInitializers(RootInit::class.java) .setInitializers(BusyBoxInit::class.java, AppShellInit::class.java)
.setTimeout(2)) .setTimeout(2))
Shell.EXECUTOR = IODispatcherExecutor() Shell.EXECUTOR = IODispatcherExecutor()
......
package com.topjohnwu.magisk.core package com.topjohnwu.magisk.core
import android.os.Build
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.model.UpdateInfo import com.topjohnwu.magisk.core.model.UpdateInfo
...@@ -34,9 +35,10 @@ object Info { ...@@ -34,9 +35,10 @@ object Info {
@JvmStatic val isFDE get() = crypto == "block" @JvmStatic val isFDE get() = crypto == "block"
@JvmField var ramdisk = false @JvmField var ramdisk = false
@JvmField var hasGMS = true @JvmField var hasGMS = true
@JvmField var isPixel = false @JvmField val isPixel = Build.BRAND == "google"
@JvmField val isEmulator = getProperty("ro.kernel.qemu", "0") == "1" @JvmField val isEmulator = getProperty("ro.kernel.qemu", "0") == "1"
var crypto = "" var crypto = ""
var noDataExec = false
val isConnected by lazy { val isConnected by lazy {
ObservableBoolean(false).also { field -> ObservableBoolean(false).also { field ->
......
...@@ -8,9 +8,9 @@ import androidx.core.os.postDelayed ...@@ -8,9 +8,9 @@ import androidx.core.os.postDelayed
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.utils.BusyBoxInit
import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.core.utils.LightShellInit
import com.topjohnwu.magisk.core.utils.MediaStoreUtils import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
...@@ -27,8 +27,6 @@ import com.topjohnwu.superuser.ShellUtils ...@@ -27,8 +27,6 @@ import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.internal.NOPList import com.topjohnwu.superuser.internal.NOPList
import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileInputStream
import com.topjohnwu.superuser.io.SuFileOutputStream
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.jpountz.lz4.LZ4FrameInputStream import net.jpountz.lz4.LZ4FrameInputStream
...@@ -53,7 +51,9 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -53,7 +51,9 @@ abstract class MagiskInstallImpl protected constructor(
protected lateinit var installDir: File protected lateinit var installDir: File
private lateinit var srcBoot: File private lateinit var srcBoot: File
private var tarOut: TarOutputStream? = null private lateinit var shell: Shell
private var closeShell = false
private val service: NetworkService by inject() private val service: NetworkService by inject()
protected val context: Context by inject(Protected) protected val context: Context by inject(Protected)
...@@ -68,7 +68,7 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -68,7 +68,7 @@ abstract class MagiskInstallImpl protected constructor(
return true return true
} }
private fun findSecondaryImage(): Boolean { private fun findSecondary(): Boolean {
val slot = "echo \$SLOT".fsh() val slot = "echo \$SLOT".fsh()
val target = if (slot == "_a") "_b" else "_a" val target = if (slot == "_a") "_b" else "_a"
console.add("- Target slot: $target") console.add("- Target slot: $target")
...@@ -86,26 +86,13 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -86,26 +86,13 @@ abstract class MagiskInstallImpl protected constructor(
return true return true
} }
private fun installDirFile(name: String): File {
return if (installDir is SuFile)
SuFile(installDir, name)
else
File(installDir, name)
}
private fun extractFiles(): Boolean { private fun extractFiles(): Boolean {
console.add("- Device platform: ${Const.CPU_ABI}") console.add("- Device platform: ${Const.CPU_ABI}")
console.add("- Installing: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") console.add("- Installing: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
val binDir = File(context.filesDir.parent, "install") installDir = File(context.filesDir.parent, "install")
binDir.deleteRecursively() installDir.deleteRecursively()
binDir.mkdirs() installDir.mkdirs()
installDir = if (Shell.rootAccess()) {
SuFile("${Const.TMPDIR}/install")
} else {
binDir
}
try { try {
// Extract binaries // Extract binaries
...@@ -116,7 +103,7 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -116,7 +103,7 @@ abstract class MagiskInstallImpl protected constructor(
}.forEach { }.forEach {
val n = it.name.substring(it.name.lastIndexOf('/') + 1) val n = it.name.substring(it.name.lastIndexOf('/') + 1)
val name = n.substring(3, n.length - 3) val name = n.substring(3, n.length - 3)
val dest = File(binDir, name) val dest = File(installDir, name)
zf.getInputStream(it).writeTo(dest) zf.getInputStream(it).writeTo(dest)
} }
} else { } else {
...@@ -125,21 +112,21 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -125,21 +112,21 @@ abstract class MagiskInstallImpl protected constructor(
} ?: emptyArray() } ?: emptyArray()
for (lib in libs) { for (lib in libs) {
val name = lib.name.substring(3, lib.name.length - 3) val name = lib.name.substring(3, lib.name.length - 3)
val bin = File(binDir, name) val bin = File(installDir, name)
symlink(lib.path, bin.path) symlink(lib.path, bin.path)
} }
} }
// Extract scripts // Extract scripts
for (script in listOf("util_functions.sh", "boot_patch.sh", "addon.d.sh")) { for (script in listOf("util_functions.sh", "boot_patch.sh", "addon.d.sh")) {
val dest = File(binDir, script) val dest = File(installDir, script)
context.assets.open(script).writeTo(dest) context.assets.open(script).writeTo(dest)
} }
// Extract chromeos tools // Extract chromeos tools
File(binDir, "chromeos").mkdir() File(installDir, "chromeos").mkdir()
for (file in listOf("futility", "kernel_data_key.vbprivk", "kernel.keyblock")) { for (file in listOf("futility", "kernel_data_key.vbprivk", "kernel.keyblock")) {
val name = "chromeos/$file" val name = "chromeos/$file"
val dest = File(binDir, name) val dest = File(installDir, name)
context.assets.open(name).writeTo(dest) context.assets.open(name).writeTo(dest)
} }
} catch (e: Exception) { } catch (e: Exception) {
...@@ -148,19 +135,10 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -148,19 +135,10 @@ abstract class MagiskInstallImpl protected constructor(
return false return false
} }
if (installDir !== binDir) {
arrayOf(
"rm -rf $installDir",
"mkdir -p $installDir",
"cp_readlink $binDir $installDir",
"rm -rf $binDir"
).sh()
}
return true return true
} }
private fun newEntry(name: String, size: Long): TarEntry { private fun newTarEntry(name: String, size: Long): TarEntry {
console.add("-- Writing: $name") console.add("-- Writing: $name")
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */)) return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
} }
...@@ -172,8 +150,13 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -172,8 +150,13 @@ abstract class MagiskInstallImpl protected constructor(
TarInputStream(input).use { tarIn -> TarInputStream(input).use { tarIn ->
lateinit var entry: TarEntry lateinit var entry: TarEntry
fun decompressedStream() = fun decompressedStream(): InputStream {
if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn val src = if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
return object : FilterInputStream(src) {
override fun available() = 0 /* Workaround bug in LZ4FrameInputStream */
override fun close() { /* Never close src stream */ }
}
}
while (tarIn.nextEntry?.let { entry = it } != null) { while (tarIn.nextEntry?.let { entry = it } != null) {
if (entry.name.contains("boot.img") || if (entry.name.contains("boot.img") ||
...@@ -181,15 +164,10 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -181,15 +164,10 @@ abstract class MagiskInstallImpl protected constructor(
val name = entry.name.replace(".lz4", "") val name = entry.name.replace(".lz4", "")
console.add("-- Extracting: $name") console.add("-- Extracting: $name")
val extract = installDirFile(name) val extract = File(installDir, name)
SuFileOutputStream(extract).use { decompressedStream().copyTo(it) } decompressedStream().writeTo(extract)
} else if (entry.name.contains("vbmeta.img")) { } else if (entry.name.contains("vbmeta.img")) {
// DO NOT USE readBytes() DUE TO BUG IN LZ4FrameInputStream val rawData = decompressedStream().readBytes()
val rawData = decompressedStream().run {
val buffer = ByteArrayOutputStream()
copyTo(buffer)
buffer.toByteArray()
}
// Valid vbmeta.img should be at least 256 bytes // Valid vbmeta.img should be at least 256 bytes
if (rawData.size < 256) if (rawData.size < 256)
continue continue
...@@ -198,7 +176,7 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -198,7 +176,7 @@ abstract class MagiskInstallImpl protected constructor(
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED // AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
console.add("-- Patching: vbmeta.img") console.add("-- Patching: vbmeta.img")
ByteBuffer.wrap(rawData).putInt(120, 3) ByteBuffer.wrap(rawData).putInt(120, 3)
tarOut.putNextEntry(newEntry("vbmeta.img", rawData.size.toLong())) tarOut.putNextEntry(newTarEntry("vbmeta.img", rawData.size.toLong()))
tarOut.write(rawData) tarOut.write(rawData)
} else { } else {
console.add("-- Copying: ${entry.name}") console.add("-- Copying: ${entry.name}")
...@@ -207,19 +185,21 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -207,19 +185,21 @@ abstract class MagiskInstallImpl protected constructor(
} }
} }
} }
val boot = installDirFile("boot.img") val boot = File(installDir, "boot.img")
val recovery = installDirFile("recovery.img") val recovery = File(installDir, "recovery.img")
if (Config.recovery && recovery.exists() && boot.exists()) { if (Config.recovery && recovery.exists() && boot.exists()) {
// Install Magisk to recovery // Install to recovery
srcBoot = recovery srcBoot = recovery
// Repack boot image to prevent restore // Repack boot image to prevent auto restore
arrayOf( arrayOf(
"cd $installDir",
"./magiskboot unpack boot.img", "./magiskboot unpack boot.img",
"./magiskboot repack boot.img", "./magiskboot repack boot.img",
"./magiskboot cleanup", "./magiskboot cleanup",
"mv new-boot.img boot.img").sh() "mv new-boot.img boot.img",
SuFileInputStream(boot).use { "cd /").sh()
tarOut.putNextEntry(newEntry("boot.img", boot.length())) boot.inputStream().use {
tarOut.putNextEntry(newTarEntry("boot.img", boot.length()))
it.copyTo(tarOut) it.copyTo(tarOut)
} }
boot.delete() boot.delete()
...@@ -263,9 +243,9 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -263,9 +243,9 @@ abstract class MagiskInstallImpl protected constructor(
handleTar(src, outFile!!.uri.outputStream()) handleTar(src, outFile!!.uri.outputStream())
} else { } else {
// Raw image // Raw image
srcBoot = installDirFile("boot.img") srcBoot = File(installDir, "boot.img")
console.add("- Copying image to cache") console.add("- Copying image to cache")
SuFileOutputStream(srcBoot).use { src.copyTo(it) } src.writeTo(srcBoot)
outFile = MediaStoreUtils.getFile("$filename.img", true) outFile = MediaStoreUtils.getFile("$filename.img", true)
outFile!!.uri.outputStream() outFile!!.uri.outputStream()
} }
...@@ -285,12 +265,12 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -285,12 +265,12 @@ abstract class MagiskInstallImpl protected constructor(
// Output file // Output file
try { try {
val patched = installDirFile("new-boot.img") val patched = File(installDir, "new-boot.img")
if (outStream is TarOutputStream) { if (outStream is TarOutputStream) {
val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img" val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img"
outStream.putNextEntry(newEntry(name, patched.length())) outStream.putNextEntry(newTarEntry(name, patched.length()))
} }
withStreams(SuFileInputStream(patched), outStream) { src, out -> src.copyTo(out) } withStreams(patched.inputStream(), outStream) { src, out -> src.copyTo(out) }
patched.delete() patched.delete()
console.add("") console.add("")
...@@ -306,22 +286,56 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -306,22 +286,56 @@ abstract class MagiskInstallImpl protected constructor(
} }
// Fix up binaries // Fix up binaries
if (installDir is SuFile) { srcBoot.delete()
"fix_env $installDir".sh() "cp_readlink $installDir".sh()
} else {
"cp_readlink $installDir".sh()
}
return true return true
} }
private fun rootInputStream(file: File): InputStream {
return if (file is SuFile)
object : BufferedInputStream(null) {
private val tmp =
File.createTempFile(file.name, null, context.cacheDir).also {
// Copy to tmp file and read from there
"cat $file > $it".sh()
`in` = it.inputStream()
}
override fun close() {
super.close()
tmp.delete()
}
}
else
file.inputStream().buffered()
}
private fun patchBoot(): Boolean { private fun patchBoot(): Boolean {
"cd $installDir".sh() val inRootDir = shell.isRoot && Info.noDataExec
val newBootImg: File
if (inRootDir) {
// Migrate everything to tmpfs to workaround Samsung bullshit
SuFile("${Const.TMPDIR}/install").also {
arrayOf(
"rm -rf $it",
"mkdir -p $it",
"cp_readlink $installDir $it",
"rm -rf $installDir"
).sh()
installDir = it
}
newBootImg = SuFile(installDir, "new-boot.img")
} else {
newBootImg = File(installDir, "new-boot.img")
// Create the output file before hand
newBootImg.createNewFile()
}
var isSigned = false var isSigned = false
if (srcBoot.let { it !is SuFile || !it.isCharacter }) { if (srcBoot.let { it !is SuFile || !it.isCharacter }) {
try { try {
SuFileInputStream(srcBoot).use { rootInputStream(srcBoot).use {
if (SignBoot.verifySignature(it, null)) { if (SignBoot.verifySignature(it, null)) {
isSigned = true isSigned = true
console.add("- Boot image is signed with AVB 1.0") console.add("- Boot image is signed with AVB 1.0")
...@@ -333,90 +347,99 @@ abstract class MagiskInstallImpl protected constructor( ...@@ -333,90 +347,99 @@ abstract class MagiskInstallImpl protected constructor(
} }
} }
val FLAGS = val flags =
"KEEPFORCEENCRYPT=${Config.keepEnc} " + "KEEPFORCEENCRYPT=${Config.keepEnc} " +
"KEEPVERITY=${Config.keepVerity} " + "KEEPVERITY=${Config.keepVerity} " +
"RECOVERYMODE=${Config.recovery}" "RECOVERYMODE=${Config.recovery}"
if (!"$FLAGS sh boot_patch.sh $srcBoot".sh().isSuccess) if (!"cd $installDir; $flags sh boot_patch.sh $srcBoot".sh().isSuccess)
return false return false
val job = Shell.sh("./magiskboot cleanup", "cd /") val job = shell.newJob().add("./magiskboot cleanup", "cd /")
val patched = installDirFile("new-boot.img")
if (isSigned) { if (isSigned) {
console.add("- Signing boot image with verity keys") console.add("- Signing boot image with verity keys")
val signed = installDirFile("signed.img") val signed = File.createTempFile("signed", ".img", context.cacheDir)
try { try {
withStreams(SuFileInputStream(patched), SuFileOutputStream(signed)) { withStreams(rootInputStream(newBootImg), signed.outputStream().buffered()) {
input, out -> SignBoot.doSignature(null, null, input, out, "/boot") src, out -> SignBoot.doSignature(null, null, src, out, "/boot")
} }
} catch (e: IOException) { } catch (e: IOException) {
console.add("! Unable to sign image") console.add("! Unable to sign image")
Timber.e(e) Timber.e(e)
return false return false
} }
if (inRootDir) {
job.add("mv -f $signed $patched") job.add("cp -f $signed $newBootImg", "rm -f $signed")
} else {
signed.copyTo(newBootImg, overwrite = true)
signed.delete()
}
} }
job.exec() job.exec()
return true return true
} }
private fun flashBoot(): Boolean { private fun flashBoot() = "direct_install $installDir $srcBoot".sh().isSuccess
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
return false
arrayOf("run_migrations", "copy_sepolicy_rules").sh()
return true
}
private suspend fun postOTA(): Boolean { private suspend fun postOTA(): Boolean {
val bootctl = SuFile("/data/adb/bootctl")
try { try {
withStreams(service.fetchBootctl().byteStream(), SuFileOutputStream(bootctl)) { val bootctl = File.createTempFile("bootctl", null, context.cacheDir)
it, out -> it.copyTo(out) service.fetchBootctl().byteStream().writeTo(bootctl)
} "post_ota $bootctl".sh()
} catch (e: IOException) { } catch (e: IOException) {
console.add("! Unable to download bootctl") console.add("! Unable to download bootctl")
Timber.e(e) Timber.e(e)
return false return false
} }
"post_ota ${bootctl.parent}".sh()
console.add("***************************************") console.add("***************************************")
console.add(" Next reboot will boot to second slot!") console.add(" Next reboot will boot to second slot!")
console.add("***************************************") console.add("***************************************")
return true return true
} }
protected fun uninstall(): Boolean { private fun setupShell(forceNonRoot: Boolean = false): Boolean {
val apk = if (isRunningAsStub) { shell = Shell.getShell()
DynAPK.current(context).path if (forceNonRoot && shell.isRoot) {
} else { shell = Shell.Builder.create()
context.packageCodePath .setFlags(Shell.FLAG_NON_ROOT_SHELL)
.setInitializers(BusyBoxInit::class.java, LightShellInit::class.java)
.build()
closeShell = true
} }
return "run_uninstaller $apk".sh().isSuccess return true
} }
private fun String.sh() = Shell.sh(this).to(console, logs).exec() private fun String.sh() = shell.newJob().add(this).to(console, logs).exec()
private fun Array<String>.sh() = Shell.sh(*this).to(console, logs).exec() private fun Array<String>.sh() = shell.newJob().add(*this).to(console, logs).exec()
private fun String.fsh() = ShellUtils.fastCmd(this) private fun String.fsh() = ShellUtils.fastCmd(shell, this)
private fun Array<String>.fsh() = ShellUtils.fastCmd(*this) private fun Array<String>.fsh() = ShellUtils.fastCmd(shell, *this)
protected fun doPatchFile(patchFile: Uri) = extractFiles() && handleFile(patchFile) protected fun doPatchFile(patchFile: Uri) =
setupShell(true) && extractFiles() && handleFile(patchFile)
protected fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot() protected fun direct() =
setupShell() && findImage() && extractFiles() && patchBoot() && flashBoot()
protected suspend fun secondSlot() = protected suspend fun secondSlot() =
findSecondaryImage() && extractFiles() && patchBoot() && flashBoot() && postOTA() setupShell() && findSecondary() && extractFiles() && patchBoot() && flashBoot() && postOTA()
protected fun fixEnv() = extractFiles() && "fix_env $installDir".sh().isSuccess protected fun fixEnv() =
setupShell() && extractFiles() && "fix_env $installDir".sh().isSuccess
protected fun uninstall() =
setupShell() && "run_uninstaller ${AssetHack.apk}".sh().isSuccess
@WorkerThread @WorkerThread
protected abstract suspend fun operations(): Boolean protected abstract suspend fun operations(): Boolean
open suspend fun exec() = withContext(Dispatchers.IO) { operations() } open suspend fun exec() = withContext(Dispatchers.IO) {
val result = operations()
if (closeShell)
shell.close()
result
}
} }
abstract class MagiskInstaller( abstract class MagiskInstaller(
......
...@@ -14,20 +14,19 @@ import com.topjohnwu.superuser.ShellUtils ...@@ -14,20 +14,19 @@ import com.topjohnwu.superuser.ShellUtils
import java.io.File import java.io.File
import java.util.jar.JarFile import java.util.jar.JarFile
class RootInit : Shell.Initializer() { abstract class BaseShellInit : Shell.Initializer() {
final override fun onInit(context: Context, shell: Shell): Boolean {
override fun onInit(context: Context, shell: Shell): Boolean {
return init(context.wrap(), shell) return init(context.wrap(), shell)
} }
private fun init(context: Context, shell: Shell): Boolean { abstract fun init(context: Context, shell: Shell): Boolean
}
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
fun getVar(name: String) = fastCmd("echo \$$name")
fun getBool(name: String) = getVar(name).toBoolean()
class BusyBoxInit : BaseShellInit() {
override fun init(context: Context, shell: Shell): Boolean {
shell.newJob().apply { shell.newJob().apply {
add("export SDK_INT=${Build.VERSION.SDK_INT}")
add("export ASH_STANDALONE=1") add("export ASH_STANDALONE=1")
val localBB: File val localBB: File
...@@ -41,27 +40,58 @@ class RootInit : Shell.Initializer() { ...@@ -41,27 +40,58 @@ class RootInit : Shell.Initializer() {
localBB = File(Const.NATIVE_LIB_DIR, "libbusybox.so") localBB = File(Const.NATIVE_LIB_DIR, "libbusybox.so")
} }
if (!shell.isRoot) { if (shell.isRoot) {
// Directly execute the file add("export MAGISKTMP=\$(magisk --path)/.magisk")
add("exec $localBB sh") // Test if we can properly execute stuff in /data
} else { Info.noDataExec = !shell.newJob().add("$localBB true").exec().isSuccess
// Copy it out of /data to workaround Samsung BS }
if (Info.noDataExec) {
// Copy it out of /data to workaround Samsung bullshit
add( add(
"export MAGISKTMP=\$(magisk --path)/.magisk",
"if [ -x \$MAGISKTMP/busybox/busybox ]; then", "if [ -x \$MAGISKTMP/busybox/busybox ]; then",
" cp -af $localBB \$MAGISKTMP/busybox/busybox", " cp -af $localBB \$MAGISKTMP/busybox/busybox",
" exec \$MAGISKTMP/busybox/busybox sh", " exec \$MAGISKTMP/busybox/busybox sh",
"else", "else",
" exec $localBB sh", " cp -af $localBB /dev/.busybox",
" exec /dev/.busybox sh",
"fi" "fi"
) )
} else {
// Directly execute the file
add("exec $localBB sh")
} }
}.exec()
return true
}
}
class LightShellInit : BaseShellInit() {
override fun init(context: Context, shell: Shell): Boolean {
shell.newJob().apply {
add("export SDK_INT=${Build.VERSION.SDK_INT}")
add(context.rawResource(R.raw.manager))
}.exec()
return true
}
}
class AppShellInit : BaseShellInit() {
override fun init(context: Context, shell: Shell): Boolean {
fun fastCmd(cmd: String) = ShellUtils.fastCmd(shell, cmd)
fun getVar(name: String) = fastCmd("echo \$$name")
fun getBool(name: String) = getVar(name).toBoolean()
shell.newJob().apply {
add("export SDK_INT=${Build.VERSION.SDK_INT}")
add(context.rawResource(R.raw.manager)) add(context.rawResource(R.raw.manager))
if (shell.isRoot) { if (shell.isRoot) {
add(context.assets.open("util_functions.sh")) add(context.assets.open("util_functions.sh"))
} }
add("mm_init") add("app_init")
}.exec() }.exec()
Const.MAGISKTMP = getVar("MAGISKTMP") Const.MAGISKTMP = getVar("MAGISKTMP")
...@@ -69,7 +99,6 @@ class RootInit : Shell.Initializer() { ...@@ -69,7 +99,6 @@ class RootInit : Shell.Initializer() {
Info.ramdisk = getBool("RAMDISKEXIST") Info.ramdisk = getBool("RAMDISKEXIST")
Info.isAB = getBool("ISAB") Info.isAB = getBool("ISAB")
Info.crypto = getVar("CRYPTOTYPE") Info.crypto = getVar("CRYPTOTYPE")
Info.isPixel = fastCmd("getprop ro.product.brand") == "google"
// Default presets // Default presets
Config.recovery = getBool("RECOVERYMODE") Config.recovery = getBool("RECOVERYMODE")
......
...@@ -36,7 +36,7 @@ fix_env() { ...@@ -36,7 +36,7 @@ fix_env() {
rm -rf $MAGISKBIN/* rm -rf $MAGISKBIN/*
mkdir -p $MAGISKBIN 2>/dev/null mkdir -p $MAGISKBIN 2>/dev/null
chmod 700 $NVBASE chmod 700 $NVBASE
cp -af $1/. $MAGISKBIN cp_readlink $1 $MAGISKBIN
rm -rf $1 rm -rf $1
chown -R 0:0 $MAGISKBIN chown -R 0:0 $MAGISKBIN
} }
...@@ -57,6 +57,8 @@ direct_install() { ...@@ -57,6 +57,8 @@ direct_install() {
rm -f $1/new-boot.img rm -f $1/new-boot.img
fix_env $1 fix_env $1
run_migrations
copy_sepolicy_rules
return 0 return 0
} }
...@@ -87,15 +89,17 @@ restore_imgs() { ...@@ -87,15 +89,17 @@ restore_imgs() {
} }
post_ota() { post_ota() {
cd $1 cd /data/adb
cp -f $1 bootctl
rm -f $1
chmod 755 bootctl chmod 755 bootctl
./bootctl hal-info || return ./bootctl hal-info || return
[ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0 [ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
./bootctl set-active-boot-slot $SLOT_NUM ./bootctl set-active-boot-slot $SLOT_NUM
cat << EOF > post-fs-data.d/post_ota.sh cat << EOF > post-fs-data.d/post_ota.sh
${1}/bootctl mark-boot-successful /data/adb/bootctl mark-boot-successful
rm -f ${1}/bootctl rm -f /data/adb/bootctl
rm -f ${1}/post-fs-data.d/post_ota.sh rm -f /data/adb/post-fs-data.d/post_ota.sh
EOF EOF
chmod 755 post-fs-data.d/post_ota.sh chmod 755 post-fs-data.d/post_ota.sh
cd / cd /
...@@ -196,8 +200,7 @@ grep_prop() { return; } ...@@ -196,8 +200,7 @@ grep_prop() { return; }
# Initialize # Initialize
############# #############
mm_init() { app_init() {
export BOOTMODE=true
mount_partitions mount_partitions
get_flags get_flags
run_migrations run_migrations
...@@ -207,3 +210,5 @@ mm_init() { ...@@ -207,3 +210,5 @@ mm_init() {
# Make sure RECOVERYMODE has value # Make sure RECOVERYMODE has value
[ -z $RECOVERYMODE ] && RECOVERYMODE=false [ -z $RECOVERYMODE ] && RECOVERYMODE=false
} }
export BOOTMODE=true
...@@ -28,6 +28,6 @@ kapt.incremental.apt=true ...@@ -28,6 +28,6 @@ kapt.incremental.apt=true
# Magisk # Magisk
magisk.stubVersion=17 magisk.stubVersion=17
magisk.versionCode=21405 magisk.versionCode=21406
magisk.ndkVersion=21d magisk.ndkVersion=21d
magisk.fullNdkVersion=21.3.6528147 magisk.fullNdkVersion=21.3.6528147
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment