mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 16:07:39 +00:00
parent
52063b3652
commit
cacc60b1ac
@ -70,15 +70,14 @@ configurations.all {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":app:shared"))
|
implementation(project(":app:shared"))
|
||||||
|
|
||||||
implementation("com.github.topjohnwu:jtar:1.1.0")
|
|
||||||
implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7")
|
implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7")
|
||||||
implementation("com.github.topjohnwu:lz4-java:1.7.1")
|
|
||||||
implementation("com.jakewharton.timber:timber:5.0.1")
|
implementation("com.jakewharton.timber:timber:5.0.1")
|
||||||
implementation("org.bouncycastle:bcpkix-jdk18on:1.78.1")
|
implementation("org.bouncycastle:bcpkix-jdk18on:1.78.1")
|
||||||
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0")
|
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0")
|
||||||
implementation("dev.rikka.rikkax.insets:insets:1.3.0")
|
implementation("dev.rikka.rikkax.insets:insets:1.3.0")
|
||||||
implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.2")
|
implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.2")
|
||||||
implementation("io.noties.markwon:core:4.6.2")
|
implementation("io.noties.markwon:core:4.6.2")
|
||||||
|
implementation("org.apache.commons:commons-compress:1.26.2")
|
||||||
|
|
||||||
val vLibsu = "6.0.0"
|
val vLibsu = "6.0.0"
|
||||||
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
||||||
|
@ -35,37 +35,53 @@ import com.topjohnwu.superuser.nio.ExtendedFile
|
|||||||
import com.topjohnwu.superuser.nio.FileSystemManager
|
import com.topjohnwu.superuser.nio.FileSystemManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.jpountz.lz4.LZ4FrameInputStream
|
import org.apache.commons.compress.archivers.tar.TarArchiveEntry
|
||||||
import org.kamranzafar.jtar.TarEntry
|
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
|
||||||
import org.kamranzafar.jtar.TarHeader
|
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
|
||||||
import org.kamranzafar.jtar.TarInputStream
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
|
||||||
import org.kamranzafar.jtar.TarOutputStream
|
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||||
|
import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.*
|
import java.io.File
|
||||||
|
import java.io.FilterInputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.io.PushbackInputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
import java.util.Arrays
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
|
|
||||||
abstract class MagiskInstallImpl protected constructor(
|
abstract class MagiskInstallImpl protected constructor(
|
||||||
protected val console: MutableList<String>,
|
protected val console: MutableList<String>,
|
||||||
private val logs: MutableList<String>
|
private val logs: MutableList<String>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
protected lateinit var installDir: ExtendedFile
|
private lateinit var installDir: ExtendedFile
|
||||||
private lateinit var srcBoot: ExtendedFile
|
private lateinit var srcBoot: ExtendedFile
|
||||||
|
|
||||||
private val shell = Shell.getShell()
|
private val shell = Shell.getShell()
|
||||||
private val service get() = ServiceLocator.networkService
|
|
||||||
protected val context get() = ServiceLocator.deContext
|
|
||||||
private val useRootDir = shell.isRoot && Info.noDataExec
|
private val useRootDir = shell.isRoot && Info.noDataExec
|
||||||
|
protected val context get() = ServiceLocator.deContext
|
||||||
|
|
||||||
private val rootFS get() = RootUtils.fs
|
private val rootFS get() = RootUtils.fs
|
||||||
private val localFS get() = FileSystemManager.getLocal()
|
private val localFS get() = FileSystemManager.getLocal()
|
||||||
|
|
||||||
|
private val destName: String by lazy {
|
||||||
|
val alpha = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
val alphaNum = "$alpha${alpha.uppercase(Locale.ROOT)}0123456789"
|
||||||
|
val random = SecureRandom()
|
||||||
|
StringBuilder("magisk_patched-${BuildConfig.VERSION_CODE}_").run {
|
||||||
|
for (i in 1..5) {
|
||||||
|
append(alphaNum[random.nextInt(alphaNum.length)])
|
||||||
|
}
|
||||||
|
toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun findImage(): Boolean {
|
private fun findImage(): Boolean {
|
||||||
val bootPath = "RECOVERYMODE=${Config.recovery} find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
val bootPath = "RECOVERYMODE=${Config.recovery} find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
|
||||||
if (bootPath.isEmpty()) {
|
if (bootPath.isEmpty()) {
|
||||||
@ -106,8 +122,8 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
try {
|
try {
|
||||||
// Extract binaries
|
// Extract binaries
|
||||||
if (isRunningAsStub) {
|
if (isRunningAsStub) {
|
||||||
ZipFile(StubApk.current(context)).use { zf ->
|
ZipFile.builder().setFile(StubApk.current(context)).get().use { zf ->
|
||||||
zf.entries().asSequence().filter {
|
zf.entries.asSequence().filter {
|
||||||
!it.isDirectory && it.name.startsWith("/lib/${Const.CPU_ABI}/")
|
!it.isDirectory && it.name.startsWith("/lib/${Const.CPU_ABI}/")
|
||||||
}.forEach {
|
}.forEach {
|
||||||
val n = it.name.substring(it.name.lastIndexOf('/') + 1)
|
val n = it.name.substring(it.name.lastIndexOf('/') + 1)
|
||||||
@ -120,13 +136,14 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
val abi32 = Const.CPU_ABI_32
|
val abi32 = Const.CPU_ABI_32
|
||||||
if (Process.is64Bit() && abi32 != null) {
|
if (Process.is64Bit() && abi32 != null) {
|
||||||
val magisk32 = File(installDir, "magisk32")
|
val magisk32 = File(installDir, "magisk32")
|
||||||
zf.getInputStream(ZipEntry("lib/$abi32/libmagisk.so")).writeTo(magisk32)
|
val entry = zf.getEntry("lib/$abi32/libmagisk.so")
|
||||||
|
zf.getInputStream(entry).writeTo(magisk32)
|
||||||
magisk32.setExecutable(true)
|
magisk32.setExecutable(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val info = context.applicationInfo
|
val info = context.applicationInfo
|
||||||
var libs = File(info.nativeLibraryDir).listFiles { _, name ->
|
val libs = File(info.nativeLibraryDir).listFiles { _, name ->
|
||||||
name.startsWith("lib") && name.endsWith(".so")
|
name.startsWith("lib") && name.endsWith(".so")
|
||||||
} ?: emptyArray()
|
} ?: emptyArray()
|
||||||
|
|
||||||
@ -179,78 +196,107 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
|
|
||||||
private suspend fun InputStream.copyAndCloseOut(out: OutputStream) = out.use { copyAll(it) }
|
private suspend fun InputStream.copyAndCloseOut(out: OutputStream) = out.use { copyAll(it) }
|
||||||
|
|
||||||
private fun newTarEntry(name: String, size: Long): TarEntry {
|
|
||||||
console.add("-- Writing: $name")
|
|
||||||
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NoAvailableStream(s: InputStream) : FilterInputStream(s) {
|
private class NoAvailableStream(s: InputStream) : FilterInputStream(s) {
|
||||||
// Make sure available is never called on the actual stream and always return 0
|
// Make sure available is never called on the actual stream and always return 0
|
||||||
// 1. Workaround bug in LZ4FrameInputStream
|
// to reduce max buffer size and avoid OOM
|
||||||
// 2. Reduce max buffer size to prevent OOM
|
|
||||||
override fun available() = 0
|
override fun available() = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoBootException : IOException()
|
private class NoBootException : IOException()
|
||||||
|
|
||||||
|
inner class BootItem(private val entry: TarArchiveEntry) {
|
||||||
|
val name = entry.name.replace(".lz4", "")
|
||||||
|
var file = installDir.getChildFile(name)
|
||||||
|
|
||||||
|
suspend fun copyTo(tarOut: TarArchiveOutputStream) {
|
||||||
|
entry.name = name
|
||||||
|
entry.size = file.length()
|
||||||
|
file.newInputStream().use {
|
||||||
|
console.add("-- Writing : $name")
|
||||||
|
tarOut.putArchiveEntry(entry)
|
||||||
|
it.copyAll(tarOut)
|
||||||
|
tarOut.closeArchiveEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private suspend fun processTar(tarIn: TarInputStream, tarOut: TarOutputStream): ExtendedFile {
|
private suspend fun processTar(
|
||||||
|
tarIn: TarArchiveInputStream,
|
||||||
|
tarOut: TarArchiveOutputStream
|
||||||
|
): BootItem {
|
||||||
console.add("- Processing tar file")
|
console.add("- Processing tar file")
|
||||||
lateinit var entry: TarEntry
|
lateinit var entry: TarArchiveEntry
|
||||||
|
|
||||||
fun decompressedStream(): InputStream {
|
fun decompressedStream(): InputStream {
|
||||||
val stream = if (entry.name.endsWith(".lz4")) LZ4FrameInputStream(tarIn) else tarIn
|
val stream = if (entry.name.endsWith(".lz4"))
|
||||||
|
FramedLZ4CompressorInputStream(tarIn, true) else tarIn
|
||||||
return NoAvailableStream(stream)
|
return NoAvailableStream(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
while (tarIn.nextEntry?.let { entry = it } != null) {
|
var boot: BootItem? = null
|
||||||
if (entry.name.startsWith("boot.img") ||
|
var initBoot: BootItem? = null
|
||||||
entry.name.startsWith("init_boot.img") ||
|
var recovery: BootItem? = null
|
||||||
(Config.recovery && entry.name.contains("recovery.img"))) {
|
|
||||||
val name = entry.name.replace(".lz4", "")
|
|
||||||
console.add("-- Extracting: $name")
|
|
||||||
|
|
||||||
val extract = installDir.getChildFile(name)
|
while (true) {
|
||||||
decompressedStream().copyAndCloseOut(extract.newOutputStream())
|
entry = tarIn.nextEntry ?: break
|
||||||
|
|
||||||
|
val bootItem: BootItem?
|
||||||
|
if (entry.name.startsWith("boot.img")) {
|
||||||
|
bootItem = BootItem(entry)
|
||||||
|
boot = bootItem
|
||||||
|
} else if (entry.name.startsWith("init_boot.img")) {
|
||||||
|
bootItem = BootItem(entry)
|
||||||
|
initBoot = bootItem
|
||||||
|
} else if (Config.recovery && entry.name.contains("recovery.img")) {
|
||||||
|
bootItem = BootItem(entry)
|
||||||
|
recovery = bootItem
|
||||||
|
} else {
|
||||||
|
bootItem = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bootItem != null) {
|
||||||
|
console.add("-- Extracting: ${bootItem.name}")
|
||||||
|
decompressedStream().copyAndCloseOut(bootItem.file.newOutputStream())
|
||||||
} else if (entry.name.contains("vbmeta.img")) {
|
} else if (entry.name.contains("vbmeta.img")) {
|
||||||
val rawData = decompressedStream().readBytes()
|
val rawData = decompressedStream().readBytes()
|
||||||
// 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
|
||||||
|
|
||||||
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED |
|
|
||||||
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
|
||||||
console.add("-- Patching: vbmeta.img")
|
|
||||||
ByteBuffer.wrap(rawData).putInt(120, 3)
|
|
||||||
tarOut.putNextEntry(newTarEntry("vbmeta.img", rawData.size.toLong()))
|
|
||||||
tarOut.write(rawData)
|
|
||||||
// vbmeta partition exist, disable boot vbmeta patch
|
// vbmeta partition exist, disable boot vbmeta patch
|
||||||
Info.patchBootVbmeta = false
|
Info.patchBootVbmeta = false
|
||||||
|
|
||||||
|
val name = entry.name.replace(".lz4", "")
|
||||||
|
console.add("-- Patching : $name")
|
||||||
|
|
||||||
|
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED |
|
||||||
|
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
||||||
|
ByteBuffer.wrap(rawData).putInt(120, 3)
|
||||||
|
|
||||||
|
// Update entry with new information
|
||||||
|
entry.name = name
|
||||||
|
entry.size = rawData.size.toLong()
|
||||||
|
|
||||||
|
// Write output
|
||||||
|
tarOut.putArchiveEntry(entry)
|
||||||
|
tarOut.write(rawData)
|
||||||
|
tarOut.closeArchiveEntry()
|
||||||
} else if (entry.name.contains("userdata.img")) {
|
} else if (entry.name.contains("userdata.img")) {
|
||||||
|
console.add("-- Skipping : ${entry.name}")
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
console.add("-- Copying: ${entry.name}")
|
console.add("-- Copying : ${entry.name}")
|
||||||
tarOut.putNextEntry(entry)
|
tarOut.putArchiveEntry(entry)
|
||||||
tarIn.copyAll(tarOut, bufferSize = 1024 * 1024)
|
tarIn.copyAll(tarOut, bufferSize = 1024 * 1024)
|
||||||
|
tarOut.closeArchiveEntry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val boot = installDir.getChildFile("boot.img")
|
|
||||||
val initBoot = installDir.getChildFile("init_boot.img")
|
|
||||||
val recovery = installDir.getChildFile("recovery.img")
|
|
||||||
|
|
||||||
suspend fun ExtendedFile.copyToTar() {
|
|
||||||
newInputStream().use {
|
|
||||||
tarOut.putNextEntry(newTarEntry(name, length()))
|
|
||||||
it.copyAll(tarOut)
|
|
||||||
}
|
|
||||||
delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch priority: recovery > init_boot > boot
|
// Patch priority: recovery > init_boot > boot
|
||||||
return when {
|
return when {
|
||||||
recovery.exists() -> {
|
recovery != null -> {
|
||||||
if (boot.exists()) {
|
if (boot != null) {
|
||||||
// Repack boot image to prevent auto restore
|
// Repack boot image to prevent auto restore
|
||||||
arrayOf(
|
arrayOf(
|
||||||
"cd $installDir",
|
"cd $installDir",
|
||||||
@ -261,27 +307,27 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
"./magiskboot cleanup",
|
"./magiskboot cleanup",
|
||||||
"rm -f new-boot.img",
|
"rm -f new-boot.img",
|
||||||
"cd /").sh()
|
"cd /").sh()
|
||||||
boot.copyToTar()
|
boot.copyTo(tarOut)
|
||||||
}
|
}
|
||||||
recovery
|
recovery
|
||||||
}
|
}
|
||||||
initBoot.exists() -> {
|
initBoot != null -> {
|
||||||
if (boot.exists())
|
boot?.copyTo(tarOut)
|
||||||
boot.copyToTar()
|
|
||||||
initBoot
|
initBoot
|
||||||
}
|
}
|
||||||
boot.exists() -> boot
|
boot != null -> boot
|
||||||
else -> throw NoBootException()
|
else -> throw NoBootException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private suspend fun processZip(zipIn: ZipInputStream): ExtendedFile {
|
private suspend fun processZip(zipIn: ZipArchiveInputStream): ExtendedFile {
|
||||||
console.add("- Processing zip file")
|
console.add("- Processing zip file")
|
||||||
val boot = installDir.getChildFile("boot.img")
|
val boot = installDir.getChildFile("boot.img")
|
||||||
val initBoot = installDir.getChildFile("init_boot.img")
|
val initBoot = installDir.getChildFile("init_boot.img")
|
||||||
lateinit var entry: ZipEntry
|
var entry: ZipArchiveEntry
|
||||||
while (zipIn.nextEntry?.also { entry = it } != null) {
|
while (true) {
|
||||||
|
entry = zipIn.nextEntry ?: break
|
||||||
if (entry.isDirectory) continue
|
if (entry.isDirectory) continue
|
||||||
when (entry.name.substringAfterLast('/')) {
|
when (entry.name.substringAfterLast('/')) {
|
||||||
"payload.bin" -> {
|
"payload.bin" -> {
|
||||||
@ -376,9 +422,10 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleFile(uri: Uri): Boolean {
|
private suspend fun processFile(uri: Uri): Boolean {
|
||||||
val outStream: OutputStream
|
val outStream: OutputStream
|
||||||
val outFile: MediaStoreUtils.UriFile
|
val outFile: MediaStoreUtils.UriFile
|
||||||
|
var bootItem: BootItem? = null
|
||||||
|
|
||||||
// Process input file
|
// Process input file
|
||||||
try {
|
try {
|
||||||
@ -393,23 +440,17 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
val magic = head.copyOf(4)
|
val magic = head.copyOf(4)
|
||||||
val tarMagic = Arrays.copyOfRange(head, 257, 262)
|
val tarMagic = Arrays.copyOfRange(head, 257, 262)
|
||||||
|
|
||||||
val alpha = "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
val alphaNum = "$alpha${alpha.uppercase(Locale.ROOT)}0123456789"
|
|
||||||
val random = SecureRandom()
|
|
||||||
val filename = StringBuilder("magisk_patched-${BuildConfig.VERSION_CODE}_").run {
|
|
||||||
for (i in 1..5) {
|
|
||||||
append(alphaNum[random.nextInt(alphaNum.length)])
|
|
||||||
}
|
|
||||||
toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
srcBoot = if (tarMagic.contentEquals("ustar".toByteArray())) {
|
srcBoot = if (tarMagic.contentEquals("ustar".toByteArray())) {
|
||||||
// tar file
|
// tar file
|
||||||
outFile = MediaStoreUtils.getFile("$filename.tar", true)
|
outFile = MediaStoreUtils.getFile("$destName.tar", true)
|
||||||
outStream = TarOutputStream(outFile.uri.outputStream())
|
outStream = TarArchiveOutputStream(outFile.uri.outputStream()).also {
|
||||||
|
it.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
||||||
|
it.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
processTar(TarInputStream(src), outStream)
|
bootItem = processTar(TarArchiveInputStream(src), outStream)
|
||||||
|
bootItem.file
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
outStream.close()
|
outStream.close()
|
||||||
outFile.delete()
|
outFile.delete()
|
||||||
@ -417,14 +458,14 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// raw image
|
// raw image
|
||||||
outFile = MediaStoreUtils.getFile("$filename.img", true)
|
outFile = MediaStoreUtils.getFile("$destName.img", true)
|
||||||
outStream = outFile.uri.outputStream()
|
outStream = outFile.uri.outputStream()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (magic.contentEquals("CrAU".toByteArray())) {
|
if (magic.contentEquals("CrAU".toByteArray())) {
|
||||||
processPayload(src)
|
processPayload(src)
|
||||||
} else if (magic.contentEquals("PK\u0003\u0004".toByteArray())) {
|
} else if (magic.contentEquals("PK\u0003\u0004".toByteArray())) {
|
||||||
processZip(ZipInputStream(src))
|
processZip(ZipArchiveInputStream(src))
|
||||||
} else {
|
} else {
|
||||||
console.add("- Copying image to cache")
|
console.add("- Copying image to cache")
|
||||||
installDir.getChildFile("boot.img").also {
|
installDir.getChildFile("boot.img").also {
|
||||||
@ -455,17 +496,12 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
// Output file
|
// Output file
|
||||||
try {
|
try {
|
||||||
val newBoot = installDir.getChildFile("new-boot.img")
|
val newBoot = installDir.getChildFile("new-boot.img")
|
||||||
if (outStream is TarOutputStream) {
|
if (bootItem != null) {
|
||||||
val name = with(srcBoot.path) {
|
bootItem.file = newBoot
|
||||||
when {
|
bootItem.copyTo(outStream as TarArchiveOutputStream)
|
||||||
contains("recovery") -> "recovery.img"
|
} else {
|
||||||
contains("init_boot") -> "init_boot.img"
|
|
||||||
else -> "boot.img"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
outStream.putNextEntry(newTarEntry(name, newBoot.length()))
|
|
||||||
}
|
|
||||||
newBoot.newInputStream().copyAndClose(outStream)
|
newBoot.newInputStream().copyAndClose(outStream)
|
||||||
|
}
|
||||||
newBoot.delete()
|
newBoot.delete()
|
||||||
|
|
||||||
console.add("")
|
console.add("")
|
||||||
@ -536,7 +572,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
private fun String.fsh() = ShellUtils.fastCmd(shell, this)
|
private fun String.fsh() = ShellUtils.fastCmd(shell, this)
|
||||||
private fun Array<String>.fsh() = ShellUtils.fastCmd(shell, *this)
|
private fun Array<String>.fsh() = ShellUtils.fastCmd(shell, *this)
|
||||||
|
|
||||||
protected suspend fun patchFile(file: Uri) = extractFiles() && handleFile(file)
|
protected suspend fun patchFile(file: Uri) = extractFiles() && processFile(file)
|
||||||
|
|
||||||
protected suspend fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot()
|
protected suspend fun direct() = findImage() && extractFiles() && patchBoot() && flashBoot()
|
||||||
|
|
||||||
@ -553,9 +589,14 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
open suspend fun exec(): Boolean {
|
open suspend fun exec(): Boolean {
|
||||||
if (haveActiveSession.getAndSet(true))
|
if (haveActiveSession.getAndSet(true))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
val result = withContext(Dispatchers.IO) { operations() }
|
val result = withContext(Dispatchers.IO) { operations() }
|
||||||
haveActiveSession.set(false)
|
haveActiveSession.set(false)
|
||||||
return result
|
if (result)
|
||||||
|
return true
|
||||||
|
|
||||||
|
Shell.cmd("rm -rf $installDir").submit()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -573,7 +614,6 @@ abstract class MagiskInstaller(
|
|||||||
if (success) {
|
if (success) {
|
||||||
console.add("- All done!")
|
console.add("- All done!")
|
||||||
} else {
|
} else {
|
||||||
Shell.cmd("rm -rf $installDir").submit()
|
|
||||||
console.add("! Installation failed")
|
console.add("! Installation failed")
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
|
@ -1,24 +1,36 @@
|
|||||||
package com.topjohnwu.magisk.core.utils
|
package com.topjohnwu.magisk.core.utils
|
||||||
|
|
||||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
suspend fun File.unzip(folder: File, path: String = "", junkPath: Boolean = false) {
|
suspend fun File.unzip(folder: File, path: String = "", junkPath: Boolean = false) {
|
||||||
inputStream().buffered().use {
|
ZipFile.Builder().setFile(this).get().use { zip ->
|
||||||
it.unzip(folder, path, junkPath)
|
for (entry in zip.entries) {
|
||||||
|
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
||||||
|
// Ignore directories, only create files
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val name = if (junkPath)
|
||||||
|
entry.name.substring(entry.name.lastIndexOf('/') + 1)
|
||||||
|
else
|
||||||
|
entry.name
|
||||||
|
val dest = File(folder, name)
|
||||||
|
dest.parentFile?.mkdirs()
|
||||||
|
dest.outputStream().use { out -> zip.getInputStream(entry).copyAll(out) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
suspend fun InputStream.unzip(folder: File, path: String, junkPath: Boolean) {
|
suspend fun InputStream.unzip(folder: File, path: String = "", junkPath: Boolean = false) {
|
||||||
try {
|
ZipArchiveInputStream(this).use { zin ->
|
||||||
val zin = ZipInputStream(this)
|
var entry: ZipArchiveEntry
|
||||||
var entry: ZipEntry
|
|
||||||
while (true) {
|
while (true) {
|
||||||
entry = zin.nextEntry ?: break
|
entry = zin.nextEntry ?: break
|
||||||
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
||||||
@ -31,13 +43,8 @@ suspend fun InputStream.unzip(folder: File, path: String, junkPath: Boolean) {
|
|||||||
entry.name
|
entry.name
|
||||||
|
|
||||||
val dest = File(folder, name)
|
val dest = File(folder, name)
|
||||||
dest.parentFile!!.let {
|
dest.parentFile?.mkdirs()
|
||||||
if (!it.exists())
|
|
||||||
it.mkdirs()
|
|
||||||
}
|
|
||||||
dest.outputStream().use { out -> zin.copyAll(out) }
|
dest.outputStream().use { out -> zin.copyAll(out) }
|
||||||
}
|
}
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
throw IOException(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user