Add zip and payload.bin support to Magisk app

This commit is contained in:
LoveSy 2022-12-20 21:17:55 +08:00 committed by John Wu
parent dd93556ad8
commit ec31cab5a7
4 changed files with 101 additions and 9 deletions

View File

@ -1,6 +1,9 @@
package com.topjohnwu.magisk.core.tasks package com.topjohnwu.magisk.core.tasks
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.ParcelFileDescriptor
import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.widget.Toast import android.widget.Toast
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
@ -15,6 +18,7 @@ import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.core.ktx.withStreams import com.topjohnwu.magisk.core.ktx.withStreams
import com.topjohnwu.magisk.core.ktx.writeTo import com.topjohnwu.magisk.core.ktx.writeTo
import com.topjohnwu.magisk.core.utils.MediaStoreUtils import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.fileDescriptor
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
import com.topjohnwu.magisk.core.utils.RootUtils import com.topjohnwu.magisk.core.utils.RootUtils
@ -40,6 +44,7 @@ import java.util.*
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipFile 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> = NOPList.getInstance(), protected val console: MutableList<String> = NOPList.getInstance(),
@ -119,7 +124,8 @@ abstract class MagiskInstallImpl protected constructor(
} ?: emptyArray() } ?: emptyArray()
// Also symlink magisk32 on non 64-bit only 64-bit devices // Also symlink magisk32 on non 64-bit only 64-bit devices
val lib32 = info.javaClass.getDeclaredField("secondaryNativeLibraryDir").get(info) as String? val lib32 = info.javaClass.getDeclaredField("secondaryNativeLibraryDir")
.get(info) as String?
if (lib32 != null) { if (lib32 != null) {
libs += File(lib32, "libmagisk32.so") libs += File(lib32, "libmagisk32.so")
} }
@ -173,6 +179,73 @@ abstract class MagiskInstallImpl protected constructor(
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */)) return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
} }
@Throws(IOException::class)
private fun processZip(input: InputStream) {
ZipInputStream(input).use { zipIn ->
lateinit var entry: ZipEntry
while (zipIn.nextEntry?.also { entry = it } != null) {
if (entry.isDirectory) continue
when (entry.name.substringAfterLast('/')) {
"payload.bin" -> {
console.add("- Extracting payload")
val dest = File(installDir, "payload.bin")
FileOutputStream(dest).use { zipIn.copyTo(it) }
processPayload(Uri.fromFile(dest))
break
}
"init_boot.img" -> {
console.add("- Extracting init_boot image")
FileOutputStream("$installDir/boot.img").use { zipIn.copyTo(it) }
break
}
"boot.img" -> {
console.add("- Extracting boot image")
FileOutputStream("$installDir/boot.img").use { zipIn.copyTo(it) }
// no break here since there might be an init_boot.img
}
}
}
}
}
@Throws(IOException::class)
@Synchronized
private fun processPayload(input: Uri) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
throw IOException("Payload is only supported on Android Oreo or above")
}
try {
console.add("- Processing payload.bin")
console.add("-- Extracting boot.img")
input.fileDescriptor("r").use { fd ->
val bk = ParcelFileDescriptor.fromFd(0)
try {
Os.dup2(fd.fileDescriptor, 0)
val process = ProcessBuilder()
.redirectInput(ProcessBuilder.Redirect.INHERIT)
.command(
"$installDir/magiskboot",
"extract",
"-",
"$installDir/boot.img"
)
.start()
if (process.waitFor() != 0) {
throw IOException(
"magiskboot extract failed with code ${
process.errorStream.readBytes().toString(Charsets.UTF_8)
}"
)
}
} finally {
Os.dup2(bk.fileDescriptor, 0)
}
}
} catch (e: ErrnoException) {
throw IOException(e)
}
}
@Throws(IOException::class) @Throws(IOException::class)
private fun processTar(input: InputStream, output: OutputStream): OutputStream { private fun processTar(input: InputStream, output: OutputStream): OutputStream {
console.add("- Processing tar file") console.add("- Processing tar file")
@ -259,7 +332,10 @@ abstract class MagiskInstallImpl protected constructor(
uri.inputStream().buffered().use { src -> uri.inputStream().buffered().use { src ->
src.mark(500) src.mark(500)
val magic = ByteArray(5) val magic = ByteArray(5)
if (src.skip(257) != 257L || src.read(magic) != magic.size) { val headMagic = ByteArray(4)
if (src.read(headMagic) != headMagic.size || src.skip(253) != 253L ||
src.read(magic) != magic.size
) {
console.add("! Invalid input file") console.add("! Invalid input file")
return false return false
} }
@ -280,10 +356,16 @@ abstract class MagiskInstallImpl protected constructor(
outFile = MediaStoreUtils.getFile("$filename.tar", true) outFile = MediaStoreUtils.getFile("$filename.tar", true)
processTar(src, outFile!!.uri.outputStream()) processTar(src, outFile!!.uri.outputStream())
} else { } else {
// raw image
srcBoot = installDir.getChildFile("boot.img") srcBoot = installDir.getChildFile("boot.img")
if (headMagic.contentEquals("CrAU".toByteArray())) {
processPayload(uri)
} else if (headMagic.contentEquals("PK\u0003\u0004".toByteArray())) {
processZip(src)
} else {
console.add("- Copying image to cache") console.add("- Copying image to cache")
src.cleanPump(srcBoot.newOutputStream()) src.cleanPump(srcBoot.newOutputStream())
}
// raw image
outFile = MediaStoreUtils.getFile("$filename.img", true) outFile = MediaStoreUtils.getFile("$filename.img", true)
outFile!!.uri.outputStream() outFile!!.uri.outputStream()
} }

View File

@ -102,6 +102,8 @@ object MediaStoreUtils {
fun Uri.outputStream() = cr.openOutputStream(this, "rwt") ?: throw FileNotFoundException() fun Uri.outputStream() = cr.openOutputStream(this, "rwt") ?: throw FileNotFoundException()
fun Uri.fileDescriptor(mode: String) = cr.openFileDescriptor(this, mode) ?: throw FileNotFoundException()
val Uri.displayName: String get() { val Uri.displayName: String get() {
if (scheme == "file") { if (scheme == "file") {
// Simple uri wrapper over file, directly get file name // Simple uri wrapper over file, directly get file name

View File

@ -46,7 +46,7 @@
<string name="install_inactive_slot_msg">Your device will be FORCED to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue?</string> <string name="install_inactive_slot_msg">Your device will be FORCED to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue?</string>
<string name="setup_title">Additional Setup</string> <string name="setup_title">Additional Setup</string>
<string name="select_patch_file">Select and Patch a File</string> <string name="select_patch_file">Select and Patch a File</string>
<string name="patch_file_msg">Select a raw image (*.img) or an ODIN tarfile (*.tar)</string> <string name="patch_file_msg">Select a raw image (*.img) or an ODIN tarfile (*.tar) or a payload.bin (*.bin)</string>
<string name="reboot_delay_toast">Rebooting in 5 seconds…</string> <string name="reboot_delay_toast">Rebooting in 5 seconds…</string>
<string name="flash_screen_title">Installation</string> <string name="flash_screen_title">Installation</string>

View File

@ -1,7 +1,7 @@
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::io::{BufReader, ErrorKind, Read, Seek, SeekFrom, Write}; use std::io::{BufReader, ErrorKind, Read, Seek, SeekFrom, Write};
use std::os::fd::AsRawFd; use std::os::fd::{AsRawFd, FromRawFd};
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use protobuf::{EnumFull, Message}; use protobuf::{EnumFull, Message};
@ -24,7 +24,11 @@ macro_rules! data_err {
static PAYLOAD_MAGIC: &str = "CrAU"; static PAYLOAD_MAGIC: &str = "CrAU";
fn do_extract_boot_from_payload(in_path: &str, out_path: &str) -> io::Result<()> { fn do_extract_boot_from_payload(in_path: &str, out_path: &str) -> io::Result<()> {
let mut reader = BufReader::new(File::open(in_path)?); let mut reader = BufReader::new(if in_path == "-" {
unsafe { File::from_raw_fd(0) }
} else {
File::open(in_path)?
});
let buf = &mut [0u8; 4]; let buf = &mut [0u8; 4];
reader.read_exact(buf)?; reader.read_exact(buf)?;
@ -79,7 +83,11 @@ fn do_extract_boot_from_payload(in_path: &str, out_path: &str) -> io::Result<()>
.block_size .block_size
.ok_or(data_err!("block size not found"))? as u64; .ok_or(data_err!("block size not found"))? as u64;
let mut out_file = File::create(out_path)?; let mut out_file = if out_path == "-" {
unsafe { File::from_raw_fd(1) }
} else {
File::create(out_path)?
};
for operation in boot.operations.iter() { for operation in boot.operations.iter() {
let data_len = operation let data_len = operation