mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-21 23:47:39 +00:00
Add zip and payload.bin support to Magisk app
This commit is contained in:
parent
dd93556ad8
commit
ec31cab5a7
@ -1,6 +1,9 @@
|
||||
package com.topjohnwu.magisk.core.tasks
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.system.ErrnoException
|
||||
import android.system.Os
|
||||
import android.widget.Toast
|
||||
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.writeTo
|
||||
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.outputStream
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
@ -40,6 +44,7 @@ import java.util.*
|
||||
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(
|
||||
protected val console: MutableList<String> = NOPList.getInstance(),
|
||||
@ -119,7 +124,8 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
} ?: emptyArray()
|
||||
|
||||
// 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) {
|
||||
libs += File(lib32, "libmagisk32.so")
|
||||
}
|
||||
@ -173,6 +179,73 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
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)
|
||||
private fun processTar(input: InputStream, output: OutputStream): OutputStream {
|
||||
console.add("- Processing tar file")
|
||||
@ -259,7 +332,10 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
uri.inputStream().buffered().use { src ->
|
||||
src.mark(500)
|
||||
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")
|
||||
return false
|
||||
}
|
||||
@ -280,10 +356,16 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
outFile = MediaStoreUtils.getFile("$filename.tar", true)
|
||||
processTar(src, outFile!!.uri.outputStream())
|
||||
} else {
|
||||
// raw image
|
||||
srcBoot = installDir.getChildFile("boot.img")
|
||||
console.add("- Copying image to cache")
|
||||
src.cleanPump(srcBoot.newOutputStream())
|
||||
if (headMagic.contentEquals("CrAU".toByteArray())) {
|
||||
processPayload(uri)
|
||||
} else if (headMagic.contentEquals("PK\u0003\u0004".toByteArray())) {
|
||||
processZip(src)
|
||||
} else {
|
||||
console.add("- Copying image to cache")
|
||||
src.cleanPump(srcBoot.newOutputStream())
|
||||
}
|
||||
// raw image
|
||||
outFile = MediaStoreUtils.getFile("$filename.img", true)
|
||||
outFile!!.uri.outputStream()
|
||||
}
|
||||
|
@ -102,6 +102,8 @@ object MediaStoreUtils {
|
||||
|
||||
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() {
|
||||
if (scheme == "file") {
|
||||
// Simple uri wrapper over file, directly get file name
|
||||
|
@ -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="setup_title">Additional Setup</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="flash_screen_title">Installation</string>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
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 protobuf::{EnumFull, Message};
|
||||
@ -24,7 +24,11 @@ macro_rules! data_err {
|
||||
static PAYLOAD_MAGIC: &str = "CrAU";
|
||||
|
||||
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];
|
||||
reader.read_exact(buf)?;
|
||||
@ -79,7 +83,11 @@ fn do_extract_boot_from_payload(in_path: &str, out_path: &str) -> io::Result<()>
|
||||
.block_size
|
||||
.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() {
|
||||
let data_len = operation
|
||||
|
Loading…
x
Reference in New Issue
Block a user