mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57: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
|
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")
|
||||||
console.add("- Copying image to cache")
|
if (headMagic.contentEquals("CrAU".toByteArray())) {
|
||||||
src.cleanPump(srcBoot.newOutputStream())
|
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 = MediaStoreUtils.getFile("$filename.img", true)
|
||||||
outFile!!.uri.outputStream()
|
outFile!!.uri.outputStream()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user