diff --git a/README.md b/README.md
index 16f10cc..97106a1 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# Android_boot_image_editor
-[](https://app.codacy.com/manual/cfig97/Android_boot_image_editor?utm_source=github.com&utm_medium=referral&utm_content=cfig/Android_boot_image_editor&utm_campaign=Badge_Grade_Dashboard)
[](https://travis-ci.org/cfig/Android_boot_image_editor)
+[](https://app.codacy.com/manual/cfig97/Android_boot_image_editor?utm_source=github.com&utm_medium=referral&utm_content=cfig/Android_boot_image_editor&utm_campaign=Badge_Grade_Dashboard)
[](http://www.apache.org/licenses/LICENSE-2.0.html)
-A tool for reverse engineering Android ROM images. (working on  and )
+A tool for reverse engineering Android ROM images. (working on (Ubuntu 18.04+) and )
## Getting Started
diff --git a/aosp/avb/avbtool.v1.2.py b/aosp/avb/avbtool.v1.2.py
index 7dfbbc0..1211df3 100755
--- a/aosp/avb/avbtool.v1.2.py
+++ b/aosp/avb/avbtool.v1.2.py
@@ -2385,12 +2385,13 @@ class Avb(object):
misc_image.seek(self.AB_MISC_METADATA_OFFSET)
misc_image.write(ab_data)
- def info_image(self, image_filename, output):
+ def info_image(self, image_filename, output, atx):
"""Implements the 'info_image' command.
Arguments:
image_filename: Image file to get information from (file object).
output: Output file to write human-readable information to (file object).
+ atx: If True, show information about Android Things eXtension (ATX).
"""
image = ImageHandler(image_filename, read_only=True)
o = output
@@ -2443,6 +2444,31 @@ class Avb(object):
if num_printed == 0:
o.write(' (none)\n')
+ if atx and header.public_key_metadata_size:
+ o.write('Android Things eXtension (ATX):\n')
+ key_metadata_offset = (header.SIZE +
+ header.authentication_data_block_size +
+ header.public_key_metadata_offset)
+ key_metadata_blob = vbmeta_blob[key_metadata_offset: key_metadata_offset
+ + header.public_key_metadata_size]
+ version, pik, psk = struct.unpack('.
+*/
+
+#define PROGRAM_VERSION_STRING "1"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include
+#include
+#include
+#include
+#include
+
+#define CPIO_END "TRAILER!!!"
+#define CPIO_ENDLEN (sizeof(CPIO_END)-1)
+
+static char buf[CPIO_ENDLEN * 2 + 1];
+
+int main(int argc, char **argv)
+{
+ FILE *f;
+ size_t s;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s \n", argv[0]);
+ exit(1);
+ }
+
+ f = fopen(argv[1], "r");
+
+ if (f == NULL) {
+ fprintf(stderr, "Cannot open file '%s'\n", argv[1]);
+ exit(1);
+ }
+
+ s = fread(buf, 6, 1, f);
+ if (s <= 0) {
+ fprintf(stderr, "Read error from file '%s'\n", argv[1]);
+ fclose(f);
+ exit(1);
+ }
+ fseek(f, 0, SEEK_SET);
+
+ /* check, if this is a cpio archive */
+ if (buf[0] == '0' && buf[1] == '7' && buf[2] == '0' && buf[3] == '7' && buf[4] == '0' && buf[5] == '1') {
+ long pos = 0;
+
+ /* Search for CPIO_END */
+ do {
+ char *h;
+ fseek(f, pos, SEEK_SET);
+ buf[sizeof(buf) - 1] = 0;
+ s = fread(buf, CPIO_ENDLEN, 2, f);
+ if (s <= 0)
+ break;
+
+ h = strstr(buf, CPIO_END);
+ if (h) {
+ pos = (h - buf) + pos + CPIO_ENDLEN;
+ fseek(f, pos, SEEK_SET);
+ break;
+ }
+ pos += CPIO_ENDLEN;
+ } while (!feof(f));
+
+ if (feof(f)) {
+ /* CPIO_END not found, just cat the whole file */
+ fseek(f, 0, SEEK_SET);
+ } else {
+ /* skip zeros */
+ while (!feof(f)) {
+ size_t i;
+
+ buf[sizeof(buf) - 1] = 0;
+ s = fread(buf, 1, sizeof(buf) - 1, f);
+ if (s <= 0)
+ break;
+
+ for (i = 0; (i < s) && (buf[i] == 0); i++) ;
+
+ if (buf[i] != 0) {
+ pos += i;
+ fseek(f, pos, SEEK_SET);
+ break;
+ }
+
+ pos += s;
+ }
+ }
+ }
+ /* cat out the rest */
+ while (!feof(f)) {
+ s = fread(buf, 1, sizeof(buf), f);
+ if (s <= 0)
+ break;
+
+ s = fwrite(buf, 1, s, stdout);
+ if (s <= 0)
+ break;
+ }
+ fclose(f);
+
+ return EXIT_SUCCESS;
+}
diff --git a/aosp/system/libufdt/utils/src/mkdtboimg.py b/aosp/system/libufdt/utils/src/mkdtboimg.py
index 777f5cb..03f0fd1 100755
--- a/aosp/system/libufdt/utils/src/mkdtboimg.py
+++ b/aosp/system/libufdt/utils/src/mkdtboimg.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python2.7
+#! /usr/bin/env python
# Copyright 2017, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/bbootimg/build.gradle.kts b/bbootimg/build.gradle.kts
index 653f03d..996807c 100644
--- a/bbootimg/build.gradle.kts
+++ b/bbootimg/build.gradle.kts
@@ -1,8 +1,8 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
- kotlin("jvm") version "1.4.0"
- kotlin("plugin.serialization") version "1.4.0"
+ kotlin("jvm") version "1.4.10"
+ kotlin("plugin.serialization") version "1.4.10"
application
}
@@ -16,14 +16,13 @@ dependencies {
implementation("org.slf4j:slf4j-simple:1.7.30")
implementation("org.slf4j:slf4j-api:1.7.30")
- implementation("com.fasterxml.jackson.core:jackson-annotations:2.11.2")
- implementation("com.fasterxml.jackson.core:jackson-databind:2.11.2")
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC")
+ implementation("com.fasterxml.jackson.core:jackson-annotations:2.11.3")
+ implementation("com.fasterxml.jackson.core:jackson-databind:2.11.3")
implementation("com.google.guava:guava:18.0")
implementation("org.apache.commons:commons-exec:1.3")
- implementation("org.apache.commons:commons-compress:1.16.1")
+ implementation("org.apache.commons:commons-compress:1.20")
implementation("org.tukaani:xz:1.8")
- implementation("commons-codec:commons-codec:1.11")
+ implementation("commons-codec:commons-codec:1.15")
implementation("junit:junit:4.12")
implementation("org.bouncycastle:bcprov-jdk15on:1.57")
implementation("de.vandermeer:asciitable:0.3.2")
diff --git a/bbootimg/src/main/kotlin/avb/Avb.kt b/bbootimg/src/main/kotlin/avb/Avb.kt
index c1b92be..f36ed29 100644
--- a/bbootimg/src/main/kotlin/avb/Avb.kt
+++ b/bbootimg/src/main/kotlin/avb/Avb.kt
@@ -7,7 +7,8 @@ import avb.blob.AuxBlob
import avb.blob.Footer
import avb.blob.Header
import avb.desc.*
-import cfig.Helper.Companion.paddingWith
+import cfig.helper.Helper
+import cfig.helper.Helper.Companion.paddingWith
import cfig.io.Struct3
import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.codec.binary.Hex
@@ -26,6 +27,7 @@ class Avb {
private val MAX_VBMETA_SIZE = 64 * 1024
private val MAX_FOOTER_SIZE = 4096
private val BLOCK_SIZE = 4096
+ private val DEBUG = false
//migrated from: avbtool::Avb::addHashFooter
fun addHashFooter(image_file: String,
@@ -59,7 +61,9 @@ class Avb {
val vbmetaBlob = packVbMeta(newAvbInfo)
log.debug("vbmeta_blob: " + Helper.toHexString(vbmetaBlob))
- Helper.dumpToFile("hashDescriptor.vbmeta.blob", vbmetaBlob)
+ if (DEBUG) {
+ Helper.dumpToFile("hashDescriptor.vbmeta.blob", vbmetaBlob)
+ }
// image + padding
val imgPaddingNeeded = Helper.round_to_multiple(newImageSize, BLOCK_SIZE) - newImageSize
@@ -196,7 +200,7 @@ class Avb {
}
// aux - desc
- var descriptors = listOf()
+ var descriptors: List
if (vbMetaHeader.descriptors_size > 0) {
FileInputStream(image_file).use { fis ->
fis.skip(descStartOffset)
diff --git a/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt b/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt
index 7ef719c..17bd249 100644
--- a/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt
+++ b/bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt
@@ -1,7 +1,7 @@
package avb.blob
import avb.alg.Algorithms
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import org.slf4j.LoggerFactory
import java.security.MessageDigest
diff --git a/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt b/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt
index bc39193..cecf70e 100644
--- a/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt
+++ b/bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt
@@ -2,9 +2,8 @@ package avb.blob
import avb.alg.Algorithm
import avb.desc.*
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
-import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import org.slf4j.LoggerFactory
import java.nio.file.Files
diff --git a/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt
index a86e61e..a527a82 100644
--- a/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt
+++ b/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt
@@ -1,6 +1,6 @@
package avb.desc
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import java.io.InputStream
import java.security.MessageDigest
diff --git a/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt
index 2d6ae82..5db3130 100644
--- a/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt
+++ b/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt
@@ -1,7 +1,7 @@
package avb.desc
import avb.blob.Header
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import org.apache.commons.codec.binary.Hex
import org.slf4j.LoggerFactory
@@ -76,6 +76,7 @@ class HashDescriptor(var flags: Int = 0,
hasher.update(this.salt)
hasher.update(File(image_file).readBytes())
val digest = hasher.digest()
+ log.info("digest:" + Helper.toHexString(digest))
}
fun update(image_file: String, use_persistent_digest: Boolean = false): HashDescriptor {
diff --git a/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt
index 446d3a0..1369bd3 100644
--- a/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt
+++ b/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt
@@ -1,7 +1,7 @@
package avb.desc
import avb.blob.Header
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import java.io.InputStream
import java.util.*
diff --git a/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt
index 8ee79af..bf8c995 100644
--- a/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt
+++ b/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt
@@ -1,6 +1,6 @@
package avb.desc
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import java.io.InputStream
diff --git a/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt
index 1b92b79..c34da6d 100644
--- a/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt
+++ b/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt
@@ -1,6 +1,6 @@
package avb.desc
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import java.io.InputStream
diff --git a/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt
index 9d1af66..796ddfa 100644
--- a/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt
+++ b/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt
@@ -1,6 +1,6 @@
package avb.desc
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import org.apache.commons.codec.binary.Hex
import org.slf4j.LoggerFactory
diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt
index 1cfd101..a2232b6 100644
--- a/bbootimg/src/main/kotlin/bootimg/Common.kt
+++ b/bbootimg/src/main/kotlin/bootimg/Common.kt
@@ -1,8 +1,10 @@
package cfig.bootimg
import cfig.EnvironmentVerifier
-import cfig.Helper
import cfig.dtb_util.DTC
+import cfig.helper.Helper
+import cfig.helper.Helper.Companion.check_call
+import cfig.helper.ZipHelper
import cfig.io.Struct3.InputStreamExt.Companion.getInt
import cfig.kernel_util.KernelExtractor
import org.apache.commons.exec.CommandLine
@@ -110,14 +112,14 @@ class Common {
var ret = "gz"
Helper.extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length)
when {
- Helper.isGZ(s.dumpFile) -> {
+ ZipHelper.isGZ(s.dumpFile) -> {
File(s.dumpFile).renameTo(File(s.dumpFile + ".gz"))
- Helper.unGnuzipFile(s.dumpFile + ".gz", s.dumpFile)
+ ZipHelper.unGnuzipFile(s.dumpFile + ".gz", s.dumpFile)
}
- Helper.isLZ4(s.dumpFile) -> {
+ ZipHelper.isLZ4(s.dumpFile) -> {
log.info("ramdisk is compressed lz4")
File(s.dumpFile).renameTo(File(s.dumpFile + ".lz4"))
- Helper.decompressLZ4(s.dumpFile + ".lz4", s.dumpFile)
+ ZipHelper.decompressLZ4Ext(s.dumpFile + ".lz4", s.dumpFile)
ret = "lz4"
}
else -> {
@@ -201,10 +203,10 @@ class Common {
}
when {
ramdiskGz.endsWith(".gz") -> {
- Helper.gnuZipFile2(ramdiskGz, ByteArrayInputStream(outputStream.toByteArray()))
+ ZipHelper.gnuZipFile2(ramdiskGz, ByteArrayInputStream(outputStream.toByteArray()))
}
ramdiskGz.endsWith(".lz4") -> {
- Helper.compressLZ4(ramdiskGz, ByteArrayInputStream(outputStream.toByteArray()))
+ ZipHelper.compressLZ4(ramdiskGz, ByteArrayInputStream(outputStream.toByteArray()))
}
else -> {
throw IllegalArgumentException("$ramdiskGz is not supported")
@@ -249,7 +251,7 @@ class Common {
}
}
- fun unpackRamdisk(ramdisk: String, root: String) {
+ private fun unpackRamdisk(ramdisk: String, root: String) {
val rootFile = File(root).apply {
if (exists()) {
log.info("Cleaning [$root] before ramdisk unpacking")
@@ -258,11 +260,9 @@ class Common {
mkdirs()
}
- DefaultExecutor().let { exe ->
- exe.workingDirectory = rootFile
- exe.execute(CommandLine.parse("cpio -i -m -F " + File(ramdisk).canonicalPath))
- log.info(" ramdisk extracted : $ramdisk -> ${rootFile}")
- }
+ //("cpio -idmv -F " + File(ramdisk).canonicalPath).check_call(rootFile.canonicalPath)
+ ZipHelper.decompressCPIO(File(ramdisk).canonicalPath, rootFile.canonicalPath, File(ramdisk).canonicalPath + ".filelist")
+ log.info(" ramdisk extracted : $ramdisk -> $rootFile")
}
fun probeHeaderVersion(fileName: String): Int {
diff --git a/bbootimg/src/main/kotlin/bootimg/Signer.kt b/bbootimg/src/main/kotlin/bootimg/Signer.kt
index 7f8cb78..ad9b068 100644
--- a/bbootimg/src/main/kotlin/bootimg/Signer.kt
+++ b/bbootimg/src/main/kotlin/bootimg/Signer.kt
@@ -4,7 +4,7 @@ import avb.AVBInfo
import avb.alg.Algorithms
import cfig.Avb
import cfig.Avb.Companion.getJsonFileName
-import cfig.Helper
+import cfig.helper.Helper
import com.fasterxml.jackson.databind.ObjectMapper
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootHeaderV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootHeaderV2.kt
index 7be9283..b194c38 100644
--- a/bbootimg/src/main/kotlin/bootimg/v2/BootHeaderV2.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v2/BootHeaderV2.kt
@@ -1,6 +1,6 @@
package cfig.bootimg.v2
-import cfig.Helper
+import cfig.helper.Helper
import cfig.bootimg.Common
import cfig.io.Struct3
import org.slf4j.LoggerFactory
diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt
index 8ba8814..0b2e684 100644
--- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt
@@ -1,12 +1,11 @@
package cfig.bootimg.v2
import cfig.Avb
-import cfig.Helper
+import cfig.helper.Helper
import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Slice
import cfig.bootimg.Signer
-import cfig.bootimg.v3.BootV3
import cfig.packable.VBMetaParser
import com.fasterxml.jackson.databind.ObjectMapper
import de.vandermeer.asciitable.AsciiTable
diff --git a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt
index cfda15d..4de7093 100644
--- a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt
@@ -1,7 +1,7 @@
package cfig.bootimg.v3
import cfig.Avb
-import cfig.Helper
+import cfig.helper.Helper
import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Companion.getPaddingSize
import cfig.bootimg.Signer
diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt
index bf2a12e..1a80704 100644
--- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt
+++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt
@@ -1,9 +1,9 @@
package cfig.bootimg.v3
import cfig.Avb
-import cfig.Helper
import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Signer
+import cfig.helper.Helper
import cfig.packable.VBMetaParser
import com.fasterxml.jackson.databind.ObjectMapper
import de.vandermeer.asciitable.AsciiTable
@@ -101,10 +101,13 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
}
//data
log.info("Writing data ...")
- val bf = ByteBuffer.allocate(1024 * 1024 * 128)//assume total SIZE small than 64MB
- bf.order(ByteOrder.LITTLE_ENDIAN)
- C.writePaddedFile(bf, this.ramdisk.file, this.info.pageSize)
- C.writePaddedFile(bf, this.dtb.file, this.info.pageSize)
+ //assume total SIZE is smaller than 64MB
+ val bf = ByteBuffer.allocate(1024 * 1024 * 128).let {
+ it.order(ByteOrder.LITTLE_ENDIAN)
+ C.writePaddedFile(it, this.ramdisk.file, this.info.pageSize)
+ C.writePaddedFile(it, this.dtb.file, this.info.pageSize)
+ it
+ }
//write
FileOutputStream("${this.info.output}.clear", true).use { fos ->
fos.write(bf.array(), 0, bf.position())
@@ -210,8 +213,8 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
private fun toCommandLine(): CommandLine {
return CommandLine(Helper.prop("mkbootimg"))
- .addArgument("--vendor_ramdisk").addArgument(this.ramdisk.file)
- .addArgument("--dtb").addArgument(this.dtb.file)
+ .addArgument("--vendor_ramdisk").addArgument(ramdisk.file)
+ .addArgument("--dtb").addArgument(dtb.file)
.addArgument("--vendor_cmdline").addArgument(info.cmdline, false)
.addArgument("--header_version").addArgument(info.headerVersion.toString())
.addArgument("--base").addArgument("0")
@@ -219,6 +222,7 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
.addArgument("--kernel_offset").addArgument(info.kernelLoadAddr.toString())
.addArgument("--ramdisk_offset").addArgument(ramdisk.loadAddr.toString())
.addArgument("--dtb_offset").addArgument(dtb.loadAddr.toString())
+ .addArgument("--pagesize").addArgument(info.pageSize.toString())
.addArgument("--vendor_boot")
}
}
diff --git a/bbootimg/src/main/kotlin/Helper.kt b/bbootimg/src/main/kotlin/helper/Helper.kt
similarity index 62%
rename from bbootimg/src/main/kotlin/Helper.kt
rename to bbootimg/src/main/kotlin/helper/Helper.kt
index cc2187d..8f7ed06 100644
--- a/bbootimg/src/main/kotlin/Helper.kt
+++ b/bbootimg/src/main/kotlin/helper/Helper.kt
@@ -1,5 +1,6 @@
-package cfig
+package cfig.helper
+import cfig.KeyUtil
import cfig.io.Struct3
import com.google.common.math.BigIntegerMath
import org.apache.commons.codec.binary.Hex
@@ -14,8 +15,12 @@ import org.slf4j.LoggerFactory
import java.io.*
import java.math.BigInteger
import java.math.RoundingMode
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
import java.nio.file.Files
import java.nio.file.Paths
+import java.nio.file.attribute.PosixFilePermission
+import java.security.MessageDigest
import java.util.*
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
@@ -91,138 +96,6 @@ class Helper {
return data
}
- fun isGZ(compressedFile: String): Boolean {
- return try {
- GZIPInputStream(FileInputStream(compressedFile)).use { }
- true
- } catch (e: ZipException) {
- false
- }
- }
-
- fun isXZ(compressedFile: String): Boolean {
- return try {
- XZCompressorInputStream(FileInputStream(compressedFile)).use { }
- true
- } catch (e: ZipException) {
- false
- }
- }
-
- fun isLZ4(compressedFile: String): Boolean {
- return try {
- "lz4 -t $compressedFile".check_call()
- true
- } catch (e: Exception) {
- false
- }
- }
-
- fun decompressLZ4(lz4File: String, outFile: String) {
- "lz4 -d -fv $lz4File $outFile".check_call()
- }
-
- fun compressLZ4(lz4File: String, inputStream: InputStream) {
- val fos = FileOutputStream(File(lz4File))
- val baosE = ByteArrayOutputStream()
- DefaultExecutor().let { exec ->
- exec.streamHandler = PumpStreamHandler(fos, baosE, inputStream)
- val cmd = CommandLine.parse("lz4 -l -12 --favor-decSpeed")
- log.info(cmd.toString())
- exec.execute(cmd)
- }
- baosE.toByteArray().let {
- if (it.isNotEmpty()) {
- log.warn(String(it))
- }
- }
- fos.close()
- }
-
- @Throws(IOException::class)
- fun gnuZipFile(compressedFile: String, decompressedFile: String) {
- val buffer = ByteArray(1024)
- FileOutputStream(compressedFile).use { fos ->
- GZIPOutputStream(fos).use { gos ->
- FileInputStream(decompressedFile).use { fis ->
- var bytesRead: Int
- while (true) {
- bytesRead = fis.read(buffer)
- if (bytesRead <= 0) break
- gos.write(buffer, 0, bytesRead)
- }
- gos.finish()
- log.info("gzip done: $decompressedFile -> $compressedFile")
- }//file-input-stream
- }//gzip-output-stream
- }//file-output-stream
- }
-
- @Throws(IOException::class)
- fun unGnuzipFile(compressedFile: String, decompressedFile: String) {
- val buffer = ByteArray(1024)
- FileInputStream(compressedFile).use { fileIn ->
- //src
- GZIPInputStream(fileIn).use { gZIPInputStream ->
- //src
- FileOutputStream(decompressedFile).use { fileOutputStream ->
- var bytesRead: Int
- while (true) {
- bytesRead = gZIPInputStream.read(buffer)
- if (bytesRead <= 0) break
- fileOutputStream.write(buffer, 0, bytesRead)
- }
- log.info("decompress(gz) done: $compressedFile -> $decompressedFile")
- }
- }
- }
- }
-
- /*
- caution: about gzip header - OS (Operating System)
-
- According to https://docs.oracle.com/javase/8/docs/api/java/util/zip/package-summary.html and
- GZIP spec RFC-1952(http://www.ietf.org/rfc/rfc1952.txt), gzip files created from java.util.zip.GZIPOutputStream
- will mark the OS field with
- 0 - FAT filesystem (MS-DOS, OS/2, NT/Win32)
- But default image built from Android source code has the OS field:
- 3 - Unix
- This MAY not be a problem, at least we didn't find it till now.
- */
- @Throws(IOException::class)
- @Deprecated("this function misses features")
- fun gnuZipFile(compressedFile: String, fis: InputStream) {
- val buffer = ByteArray(1024)
- FileOutputStream(compressedFile).use { fos ->
- GZIPOutputStream(fos).use { gos ->
- var bytesRead: Int
- while (true) {
- bytesRead = fis.read(buffer)
- if (bytesRead <= 0) break
- gos.write(buffer, 0, bytesRead)
- }
- log.info("compress(gz) done: $compressedFile")
- }
- }
- }
-
- fun gnuZipFile2(compressedFile: String, fis: InputStream) {
- val buffer = ByteArray(1024)
- val p = GzipParameters()
- p.operatingSystem = 3
- FileOutputStream(compressedFile).use { fos ->
- GzipCompressorOutputStream(fos, p).use { gos ->
- var bytesRead: Int
- while (true) {
- bytesRead = fis.read(buffer)
- if (bytesRead <= 0) break
- gos.write(buffer, 0, bytesRead)
- }
- log.info("compress(gz) done: $compressedFile")
- }
- }
- }
-
fun extractFile(fileName: String, outImgName: String, offset: Long, length: Int) {
if (0 == length) {
return
@@ -354,12 +227,23 @@ class Helper {
log.info("Dumping data to $dumpFile done")
}
- fun String.check_call(): Boolean {
+ fun String.deleteIfExists() {
+ if (File(this).exists()) {
+ log.info("deleting $this")
+ File(this).delete()
+ }
+ }
+
+ fun String.check_call(inWorkdir: String? = null): Boolean {
val ret: Boolean
try {
val cmd = CommandLine.parse(this)
- log.info(cmd.toString())
- DefaultExecutor().execute(cmd)
+ log.run {
+ info("CMD: $cmd, workDir: $inWorkdir")
+ }
+ val exec = DefaultExecutor()
+ inWorkdir?.let { exec.workingDirectory = File(it) }
+ exec.execute(cmd)
ret = true
} catch (e: java.lang.IllegalArgumentException) {
log.error("$e: can not parse command: [$this]")
@@ -381,10 +265,143 @@ class Helper {
it.streamHandler = PumpStreamHandler(outputStream)
it.execute(CommandLine.parse(this))
}
- log.info(outputStream.toString())
+ log.info(outputStream.toString().trim())
return outputStream.toString().trim()
}
+ fun String.pumpRun(): Array {
+ val outStream = ByteArrayOutputStream()
+ val errStream = ByteArrayOutputStream()
+ log.info("CMD: $this")
+ DefaultExecutor().let {
+ it.streamHandler = PumpStreamHandler(outStream, errStream)
+ it.execute(CommandLine.parse(this))
+ }
+ log.info("stdout [$outStream]")
+ log.info("stderr [$errStream]")
+ return arrayOf(outStream, errStream)
+ }
+
+ fun powerRun3(cmdline: CommandLine, inputStream: InputStream?): Array {
+ var ret = true
+ val outStream = ByteArrayOutputStream()
+ val errStream = ByteArrayOutputStream()
+ log.info("CMD: $cmdline")
+ try {
+ DefaultExecutor().let {
+ it.streamHandler = PumpStreamHandler(outStream, errStream, inputStream)
+ it.execute(cmdline)
+ }
+ } catch (e: ExecuteException) {
+ log.error("fail to execute [${cmdline}]")
+ ret = false
+ }
+ log.debug("stdout [$outStream]")
+ log.debug("stderr [$errStream]")
+ return arrayOf(ret, outStream.toByteArray(), errStream.toByteArray())
+ }
+
+ fun powerRun2(cmd: String, inputStream: InputStream?): Array {
+ var ret = true
+ val outStream = ByteArrayOutputStream()
+ val errStream = ByteArrayOutputStream()
+ log.info("CMD: $cmd")
+ try {
+ DefaultExecutor().let {
+ it.streamHandler = PumpStreamHandler(outStream, errStream, inputStream)
+ it.execute(CommandLine.parse(cmd))
+ }
+ } catch (e: ExecuteException) {
+ log.error("fail to execute [$cmd]")
+ ret = false
+ }
+ log.debug("stdout [$outStream]")
+ log.debug("stderr [$errStream]")
+ return arrayOf(ret, outStream.toByteArray(), errStream.toByteArray())
+ }
+
+ fun powerRun(cmd: String, inputStream: InputStream?): Array {
+ val outStream = ByteArrayOutputStream()
+ val errStream = ByteArrayOutputStream()
+ log.info("CMD: $cmd")
+ try {
+ DefaultExecutor().let {
+ it.streamHandler = PumpStreamHandler(outStream, errStream, inputStream)
+ it.execute(CommandLine.parse(cmd))
+ }
+ } catch (e: ExecuteException) {
+ log.error("fail to execute [$cmd]")
+ }
+ log.debug("stdout [$outStream]")
+ log.debug("stderr [$errStream]")
+ return arrayOf(outStream.toByteArray(), errStream.toByteArray())
+ }
+
+ fun hashFileAndSize(vararg inFiles: String?): ByteArray {
+ val md = MessageDigest.getInstance("SHA1")
+ for (item in inFiles) {
+ if (null == item) {
+ md.update(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
+ .putInt(0)
+ .array())
+ log.debug("update null $item: " + toHexString((md.clone() as MessageDigest).digest()))
+ } else {
+ val currentFile = File(item)
+ FileInputStream(currentFile).use { iS ->
+ var byteRead: Int
+ val dataRead = ByteArray(1024)
+ while (true) {
+ byteRead = iS.read(dataRead)
+ if (-1 == byteRead) {
+ break
+ }
+ md.update(dataRead, 0, byteRead)
+ }
+ log.debug("update file $item: " + toHexString((md.clone() as MessageDigest).digest()))
+ md.update(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
+ .putInt(currentFile.length().toInt())
+ .array())
+ log.debug("update SIZE $item: " + toHexString((md.clone() as MessageDigest).digest()))
+ }
+ }
+ }
+
+ return md.digest()
+ }
+
+ fun assertFileEquals(file1: String, file2: String) {
+ val hash1 = hashFileAndSize(file1)
+ val hash2 = hashFileAndSize(file2)
+ log.info("$file1 hash ${toHexString(hash1)}, $file2 hash ${toHexString(hash2)}")
+ if (hash1.contentEquals(hash2)) {
+ log.info("Hash verification passed: ${toHexString(hash1)}")
+ } else {
+ log.error("Hash verification failed")
+ throw UnknownError("Do not know why hash verification fails, maybe a bug")
+ }
+ }
+
+ fun modeToPermissions(inMode: Int): Set {
+ var mode = inMode
+ val PERMISSIONS_MASK = 4095
+ // setgid/setuid/sticky are not supported.
+ val MAX_SUPPORTED_MODE = 511
+ mode = mode and PERMISSIONS_MASK
+ if (mode and MAX_SUPPORTED_MODE != mode) {
+ throw IOException("Invalid mode: $mode")
+ }
+ val allPermissions = PosixFilePermission.values()
+ val result: MutableSet = EnumSet.noneOf(PosixFilePermission::class.java)
+ for (i in allPermissions.indices) {
+ if (mode and 1 == 1) {
+ result.add(allPermissions[allPermissions.size - i - 1])
+ }
+ mode = mode shr 1
+ }
+ return result
+ }
+
+
private val log = LoggerFactory.getLogger("Helper")
}
}
diff --git a/bbootimg/src/main/kotlin/helper/ZipHelper.kt b/bbootimg/src/main/kotlin/helper/ZipHelper.kt
new file mode 100644
index 0000000..127e137
--- /dev/null
+++ b/bbootimg/src/main/kotlin/helper/ZipHelper.kt
@@ -0,0 +1,416 @@
+package cfig.helper
+
+import cfig.helper.Helper.Companion.check_call
+import cfig.helper.Helper.Companion.check_output
+import cfig.io.Struct3
+import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream
+import org.apache.commons.compress.archivers.zip.*
+import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
+import org.apache.commons.compress.compressors.gzip.GzipParameters
+import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream
+import org.apache.commons.compress.compressors.xz.XZCompressorInputStream
+import org.apache.commons.compress.utils.IOUtils
+import org.apache.commons.exec.CommandLine
+import org.apache.commons.exec.DefaultExecutor
+import org.apache.commons.exec.PumpStreamHandler
+import org.slf4j.LoggerFactory
+import java.io.*
+import java.lang.IllegalArgumentException
+import java.net.URI
+import java.nio.file.FileSystems
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.nio.file.StandardCopyOption
+import java.util.zip.GZIPInputStream
+import java.util.zip.GZIPOutputStream
+import java.util.zip.ZipException
+import kotlin.reflect.full.declaredFunctions
+import kotlin.reflect.jvm.isAccessible
+
+class ZipHelper {
+ class ZipEntryRecipe(val data: ByteArray, val name: String, val method: ZipMethod)
+
+ companion object {
+ private val log = LoggerFactory.getLogger("ZipHelper")
+
+ fun unZipFile2(fileName: String, outDir: String) {
+ val zis = ZipArchiveInputStream(BufferedInputStream(FileInputStream(fileName)))
+ while (true) {
+ val entry = zis.nextZipEntry ?: break
+ val entryOut = File(outDir + "/" + entry.name)
+ when {
+ entry.isDirectory -> {
+ log.error("Found dir : " + entry.name)
+ throw IllegalArgumentException("this should not happen")
+ }
+ entry.isUnixSymlink -> {
+ log.error("Found link: " + entry.name)
+ throw IllegalArgumentException("this should not happen")
+ }
+ else -> {
+ if (entry.name.contains("/")) {
+ log.debug("Createing dir: " + entryOut.parentFile.canonicalPath)
+ entryOut.parentFile.mkdirs()
+ }
+ log.info("Unzipping " + entry.name)
+ IOUtils.copy(zis, FileOutputStream(entryOut))
+ }
+ }
+ }
+ }
+
+ /*
+ https://github.com/python/cpython/blob/3.8/Lib/zipfile.py
+ The "local file header" structure, magic number, size, and indices
+ (section V.A in the format document)
+ structFileHeader = "<4s2B4HL2L2H"
+ stringFileHeader = b"PK\003\004"
+ sizeFileHeader = struct.calcsize(structFileHeader)
+ */
+ fun ZipArchiveEntry.getEntryOffset(): Long {
+ val zipFileHeaderSize = Struct3("<4s2B4HL2L2H").calcSize()
+ val funGetLocalHeaderOffset = ZipArchiveEntry::class.declaredFunctions.filter { funcItem ->
+ funcItem.name == "getLocalHeaderOffset"
+ }[0]
+ funGetLocalHeaderOffset.isAccessible = true
+ val headerOffset = funGetLocalHeaderOffset.call(this) as Long
+ val offset: Long = headerOffset + zipFileHeaderSize + this.localFileDataExtra.size + this.name.length
+ log.debug("headerOffset = $headerOffset")
+ log.debug("calcSize: $zipFileHeaderSize")
+ return offset
+ }
+
+ fun dumpZipEntry(inFile: String, entryName: String, outFile: String) {
+ log.info("dumping: $inFile#$entryName -> $outFile")
+ val zf = ZipFile(inFile)
+ val entry = zf.getEntry(entryName)
+ FileOutputStream(outFile).use { outStream ->
+ zf.getInputStream(entry).copyTo(outStream)
+ }
+ zf.close()
+ }
+
+ fun getEntryStream(zipFile: ZipFile, entryName: String): InputStream {
+ return zipFile.getInputStream(zipFile.getEntry(entryName))
+ }
+
+ fun ZipFile.dumpEntryIfExists(entryName: String, outFile: File) {
+ val entry = this.getEntry(entryName)
+ if (entry != null) {
+ log.info("dumping entry: $entryName -> $outFile")
+ FileOutputStream(outFile).use { outStream ->
+ this.getInputStream(entry).copyTo(outStream)
+ }
+ } else {
+ log.info("dumping entry: $entryName : entry not found, skip")
+ }
+ }
+
+ fun ZipFile.dumpEntry(entryName: String, outFile: File) {
+ log.info("dumping entry: $entryName -> $outFile")
+ val entry = this.getEntry(entryName)
+ FileOutputStream(outFile).use { outStream ->
+ this.getInputStream(entry).copyTo(outStream)
+ }
+ }
+
+ fun ZipFile.dumpEntry(entryName: String, outFile: String) {
+ log.info("dumping entry: $entryName -> $outFile")
+ val entry = this.getEntry(entryName)
+ FileOutputStream(outFile).use { outStream ->
+ this.getInputStream(entry).copyTo(outStream)
+ }
+ }
+
+ fun ZipArchiveOutputStream.packFile(inFile: File, entryName: String, zipMethod: ZipMethod = ZipMethod.DEFLATED) {
+ log.info("packing $entryName($zipMethod) from file $inFile (size=${inFile.length()} ...")
+ val entry = ZipArchiveEntry(inFile, entryName)
+ entry.method = zipMethod.ordinal
+ this.putArchiveEntry(entry)
+ IOUtils.copy(Files.newInputStream(inFile.toPath()), this)
+ this.closeArchiveEntry()
+ }
+
+ fun ZipArchiveOutputStream.packEntry(inBuf: ByteArray, entryName: String, zipMethod: ZipMethod = ZipMethod.DEFLATED) {
+ log.info("packing $entryName($zipMethod) from memory data (size=${inBuf.size}...")
+ val entry = ZipArchiveEntry(entryName)
+ entry.method = zipMethod.ordinal
+ this.putArchiveEntry(entry)
+ IOUtils.copy(ByteArrayInputStream(inBuf), this)
+ this.closeArchiveEntry()
+ }
+
+ fun ZipArchiveOutputStream.packStream(inStream: InputStream, entryName: String, zipMethod: ZipMethod = ZipMethod.DEFLATED) {
+ log.info("packing $entryName($zipMethod) from input stream (size=unknown...")
+ val entry = ZipArchiveEntry(entryName)
+ entry.method = zipMethod.ordinal
+ this.putArchiveEntry(entry)
+ IOUtils.copy(inStream, this)
+ this.closeArchiveEntry()
+ }
+
+ fun zipDelete(zipFile: File, entryName: String) {
+ val zipProperties = mutableMapOf("create" to "false")
+ val zipURI = URI.create("jar:file:" + zipFile.canonicalPath)
+ FileSystems.newFileSystem(zipURI, zipProperties).use { zipfs ->
+ val entryPath = zipfs.getPath(entryName)
+ log.info("deleting " + entryPath.toUri() + " from ZIP File ${zipFile.name}")
+ Files.delete(entryPath)
+ }
+ }
+
+ fun zipClone(inFile: String, outFile: String) {
+ ZipFile(inFile).use { zf ->
+ val zaos = ZipArchiveOutputStream(FileOutputStream(outFile))
+ val e = zf.entries
+ while (e.hasMoreElements()) {
+ val entry = e.nextElement()
+ zaos.putArchiveEntry(entry)
+ IOUtils.copy(zf.getInputStream(entry), zaos)
+ zaos.closeArchiveEntry()
+ }
+ zaos.finish()
+ zaos.close()
+ }
+ }
+
+ fun zipEdit(inFile: String, entryRecipe: ZipEntryRecipe) {
+ val tmpFile = File.createTempFile("edit.", ".zip")
+ log.info("transforming $inFile --> $tmpFile ...")
+ ZipFile(inFile).use { zf ->
+ val zaos = ZipArchiveOutputStream(tmpFile)
+ val e = zf.entries
+ if (zf.getEntry(entryRecipe.name) == null) {
+ log.info("adding new entry [${entryRecipe.name}(${entryRecipe.method})] into [${tmpFile.canonicalPath}]")
+ val entry = ZipArchiveEntry(entryRecipe.name)
+ entry.method = entryRecipe.method.ordinal
+ zaos.putArchiveEntry(entry)
+ IOUtils.copy(ByteArrayInputStream(entryRecipe.data), zaos)
+ }
+
+ while (e.hasMoreElements()) {
+ val entry = e.nextElement()
+ zaos.putArchiveEntry(entry)
+ if (entry.name == entryRecipe.name) {
+ log.info("modifying existent entry [${entryRecipe.name}(${entryRecipe.method})] into [${tmpFile.canonicalPath}]")
+ IOUtils.copy(ByteArrayInputStream(entryRecipe.data), zaos)
+ } else {
+ log.debug("cloning entry ${entry.name} ...")
+ IOUtils.copy(zf.getInputStream(entry), zaos)
+ }
+ zaos.closeArchiveEntry()
+ }
+
+ zaos.finish()
+ zaos.close()
+ }
+ log.info("transforming $inFile --> ${tmpFile.name} done")
+ Files.move(tmpFile.toPath(), File(inFile).toPath(), StandardCopyOption.REPLACE_EXISTING)
+ log.info("renaming ${tmpFile.canonicalPath} --> $inFile done")
+ }
+
+ fun isGZ(compressedFile: String): Boolean {
+ return try {
+ GZIPInputStream(FileInputStream(compressedFile)).use { }
+ true
+ } catch (e: ZipException) {
+ false
+ }
+ }
+
+ fun isXZ(compressedFile: String): Boolean {
+ return try {
+ XZCompressorInputStream(FileInputStream(compressedFile)).use { }
+ true
+ } catch (e: ZipException) {
+ false
+ }
+ }
+
+ fun isLZ4(compressedFile: String): Boolean {
+ return try {
+ "lz4 -t $compressedFile".check_call()
+ true
+ } catch (e: Exception) {
+ false
+ }
+ }
+
+ fun decompressLZ4Ext(lz4File: String, outFile: String) {
+ "lz4 -d -fv $lz4File $outFile".check_call()
+ }
+
+ fun compressLZ4(lz4File: String, inputStream: InputStream) {
+ FileOutputStream(File(lz4File)).use { fos ->
+ val baosE = ByteArrayOutputStream()
+ DefaultExecutor().let { exec ->
+ exec.streamHandler = PumpStreamHandler(fos, baosE, inputStream)
+ val cmd = CommandLine.parse("lz4 -l -12")
+ if ("lz4 --version".check_output().contains("r\\d+,".toRegex())) {
+ log.warn("lz4 version obsolete, needs update")
+ } else {
+ cmd.addArgument("--favor-decSpeed")
+ }
+ log.info(cmd.toString())
+ exec.execute(cmd)
+ }
+ baosE.toByteArray().let {
+ if (it.isNotEmpty()) {
+ log.warn(String(it))
+ }
+ }
+ }
+ }
+
+ fun decompressLZ4(framedLz4: String, outFile: String) {
+ FramedLZ4CompressorInputStream(
+ Files.newInputStream(Paths.get(framedLz4))).use { zIn ->
+ Files.newOutputStream(Paths.get(outFile)).use { out ->
+ log.info("decompress lz4: $framedLz4 -> $outFile")
+ val buffer = ByteArray(8192)
+ var n: Int
+ while (-1 != zIn.read(buffer).also { n = it }) {
+ out.write(buffer, 0, n)
+ }
+ }
+ }
+ }
+
+ @Throws(IOException::class)
+ fun gnuZipFile(compressedFile: String, decompressedFile: String) {
+ val buffer = ByteArray(1024)
+ FileOutputStream(compressedFile).use { fos ->
+ GZIPOutputStream(fos).use { gos ->
+ FileInputStream(decompressedFile).use { fis ->
+ var bytesRead: Int
+ while (true) {
+ bytesRead = fis.read(buffer)
+ if (bytesRead <= 0) break
+ gos.write(buffer, 0, bytesRead)
+ }
+ gos.finish()
+ log.info("gzip done: $decompressedFile -> $compressedFile")
+ }//file-input-stream
+ }//gzip-output-stream
+ }//file-output-stream
+ }
+
+ @Throws(IOException::class)
+ fun unGnuzipFile(compressedFile: String, decompressedFile: String) {
+ val buffer = ByteArray(1024)
+ FileInputStream(compressedFile).use { fileIn ->
+ //src
+ GZIPInputStream(fileIn).use { gZIPInputStream ->
+ //src
+ FileOutputStream(decompressedFile).use { fileOutputStream ->
+ var bytesRead: Int
+ while (true) {
+ bytesRead = gZIPInputStream.read(buffer)
+ if (bytesRead <= 0) break
+ fileOutputStream.write(buffer, 0, bytesRead)
+ }
+ log.info("decompress(gz) done: $compressedFile -> $decompressedFile")
+ }
+ }
+ }
+ }
+
+ /*
+ caution: about gzip header - OS (Operating System)
+
+ According to https://docs.oracle.com/javase/8/docs/api/java/util/zip/package-summary.html and
+ GZIP spec RFC-1952(http://www.ietf.org/rfc/rfc1952.txt), gzip files created from java.util.zip.GZIPOutputStream
+ will mark the OS field with
+ 0 - FAT filesystem (MS-DOS, OS/2, NT/Win32)
+ But default image built from Android source code has the OS field:
+ 3 - Unix
+ This MAY not be a problem, at least we didn't find it till now.
+ */
+ @Throws(IOException::class)
+ @Deprecated("this function misses features")
+ fun gnuZipFile(compressedFile: String, fis: InputStream) {
+ val buffer = ByteArray(1024)
+ FileOutputStream(compressedFile).use { fos ->
+ GZIPOutputStream(fos).use { gos ->
+ var bytesRead: Int
+ while (true) {
+ bytesRead = fis.read(buffer)
+ if (bytesRead <= 0) break
+ gos.write(buffer, 0, bytesRead)
+ }
+ log.info("compress(gz) done: $compressedFile")
+ }
+ }
+ }
+
+ fun gnuZipFile2(compressedFile: String, fis: InputStream) {
+ val buffer = ByteArray(1024)
+ val p = GzipParameters()
+ p.operatingSystem = 3
+ FileOutputStream(compressedFile).use { fos ->
+ GzipCompressorOutputStream(fos, p).use { gos ->
+ var bytesRead: Int
+ while (true) {
+ bytesRead = fis.read(buffer)
+ if (bytesRead <= 0) break
+ gos.write(buffer, 0, bytesRead)
+ }
+ log.info("compress(gz) done: $compressedFile")
+ }
+ }
+ }
+
+ fun decompressCPIO(cpioFile: String, outDir: String, fileList: String? = null) {
+ run { //clean up
+ if (File(outDir).exists()) {
+ log.info("Cleaning $outDir ...")
+ File(outDir).deleteRecursively()
+ }
+ File(outDir).mkdir()
+ }
+ val cis = CpioArchiveInputStream(FileInputStream(cpioFile))
+ val fileListDump = if (fileList != null) FileOutputStream(fileList) else null
+
+ data class CpioEntryInfo(var type: String = "", var mode: String = "",
+ var uid_gid: String = "", var name: String = "",
+ var size: Long = 0, var linkTarget: String = "")
+ while (true) {
+ val entry = cis.nextCPIOEntry ?: break
+ val entryInfo = CpioEntryInfo(name = entry.name,
+ size = entry.size,
+ mode = String.format("%6s", java.lang.Long.toOctalString(entry.mode)),
+ uid_gid = "${entry.uid}/${entry.gid}")
+ if (!cis.canReadEntryData(entry)) {
+ throw RuntimeException("can not read entry ??")
+ }
+ val buffer = ByteArray(entry.size.toInt())
+ cis.read(buffer)
+ val outEntryName = File(outDir + "/" + entry.name).path
+ when {
+ entry.isRegularFile -> {
+ entryInfo.type = "REG"
+ File(outEntryName).writeBytes(buffer)
+ Files.setPosixFilePermissions(Paths.get(outEntryName),
+ Helper.modeToPermissions((entry.mode and 0xfff).toInt()))
+ }
+ entry.isSymbolicLink -> {
+ entryInfo.type = "LNK"
+ entryInfo.linkTarget = String(buffer)
+ Files.createSymbolicLink(Paths.get(outEntryName), Paths.get(String(buffer)))
+ }
+ entry.isDirectory -> {
+ entryInfo.type = "DIR"
+ File(outEntryName).mkdir()
+ Files.setPosixFilePermissions(Paths.get(outEntryName),
+ Helper.modeToPermissions((entry.mode and 0xfff).toInt()))
+ }
+ else -> throw IllegalArgumentException("??? type unknown")
+ }
+ File(outEntryName).setLastModified(entry.time)
+ log.debug(entryInfo.toString() + (", read " + cis.bytesRead))
+ fileListDump?.write((entryInfo.toString() + ", read " + cis.bytesRead + "\n").toByteArray())
+ }
+ fileListDump?.close()
+ }
+ }
+}
diff --git a/bbootimg/src/main/kotlin/init/Reboot.kt b/bbootimg/src/main/kotlin/init/Reboot.kt
index bfa401e..f21b7c4 100644
--- a/bbootimg/src/main/kotlin/init/Reboot.kt
+++ b/bbootimg/src/main/kotlin/init/Reboot.kt
@@ -19,6 +19,7 @@ class Reboot {
const val lastRebootReasonKey = "persist.sys.boot.reason"
private fun doReboot(cmd: RB_TYPE, reason: String, rebootTarget: String) {
+ log.info("DoReboot: cmd=$cmd, reason=$reason, tgt=$rebootTarget")
val reasons = reason.split(",").toTypedArray()
val props = Properties()
props.setProperty(lastRebootReasonKey, reason)
diff --git a/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt b/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt
index 5eadcb6..57ca1f7 100644
--- a/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt
+++ b/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt
@@ -1,7 +1,7 @@
package cfig.kernel_util
import cfig.EnvironmentVerifier
-import cfig.Helper
+import cfig.helper.Helper
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.slf4j.Logger
diff --git a/bbootimg/src/main/kotlin/packable/BootImgParser.kt b/bbootimg/src/main/kotlin/packable/BootImgParser.kt
index 825378f..1cd8385 100644
--- a/bbootimg/src/main/kotlin/packable/BootImgParser.kt
+++ b/bbootimg/src/main/kotlin/packable/BootImgParser.kt
@@ -3,7 +3,7 @@ package cfig.packable
import avb.AVBInfo
import avb.blob.Footer
import cfig.Avb
-import cfig.Helper
+import cfig.helper.Helper
import cfig.bootimg.Common.Companion.probeHeaderVersion
import cfig.bootimg.v2.BootV2
import cfig.bootimg.v3.BootV3
@@ -95,8 +95,8 @@ class BootImgParser() : IPackable {
private val log = LoggerFactory.getLogger(BootImgParser::class.java)
fun updateVbmeta(fileName: String) {
- log.info("Updating vbmeta.img side by side ...")
if (File("vbmeta.img").exists()) {
+ log.info("Updating vbmeta.img side by side ...")
val partitionName = ObjectMapper().readValue(File(Avb.getJsonFileName(fileName)), AVBInfo::class.java).let {
it.auxBlob!!.hashDescriptors.get(0).partition_name
}
@@ -118,6 +118,8 @@ class BootImgParser() : IPackable {
this.auxBlob!!.hashDescriptors.add(hd)
}
Avb().packVbMetaWithPadding("vbmeta.img", mainVBMeta)
+ } else {
+ log.info("no companion vbmeta.img")
}
}
}
diff --git a/bbootimg/src/main/kotlin/packable/DtboParser.kt b/bbootimg/src/main/kotlin/packable/DtboParser.kt
index d71b92e..e6c3a56 100644
--- a/bbootimg/src/main/kotlin/packable/DtboParser.kt
+++ b/bbootimg/src/main/kotlin/packable/DtboParser.kt
@@ -1,8 +1,8 @@
package cfig.packable
import cfig.EnvironmentVerifier
-import cfig.Helper
import cfig.dtb_util.DTC
+import cfig.helper.Helper
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.slf4j.LoggerFactory
diff --git a/bbootimg/src/main/kotlin/packable/IPackable.kt b/bbootimg/src/main/kotlin/packable/IPackable.kt
index 73edf67..55f8b64 100644
--- a/bbootimg/src/main/kotlin/packable/IPackable.kt
+++ b/bbootimg/src/main/kotlin/packable/IPackable.kt
@@ -1,8 +1,8 @@
package cfig.packable
-import cfig.Helper
-import cfig.Helper.Companion.check_call
-import cfig.Helper.Companion.check_output
+import cfig.helper.Helper
+import cfig.helper.Helper.Companion.check_call
+import cfig.helper.Helper.Companion.check_output
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
diff --git a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt
index 87d02e2..5c61e7e 100644
--- a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt
+++ b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt
@@ -1,7 +1,7 @@
package cfig.packable
import cfig.Avb
-import cfig.Helper
+import cfig.helper.Helper
import java.io.File
@OptIn(ExperimentalUnsignedTypes::class)
diff --git a/bbootimg/src/main/kotlin/packable/VendorBootParser.kt b/bbootimg/src/main/kotlin/packable/VendorBootParser.kt
index ee7930c..2f50387 100644
--- a/bbootimg/src/main/kotlin/packable/VendorBootParser.kt
+++ b/bbootimg/src/main/kotlin/packable/VendorBootParser.kt
@@ -1,6 +1,6 @@
package cfig.packable
-import cfig.Helper
+import cfig.helper.Helper
import cfig.bootimg.v3.VendorBoot
import cfig.packable.BootImgParser.Companion.updateVbmeta
import com.fasterxml.jackson.databind.ObjectMapper
diff --git a/bbootimg/src/main/kotlin/sparse_util/SparseImgParser.kt b/bbootimg/src/main/kotlin/sparse_util/SparseImgParser.kt
index 277ddb3..cf586cd 100644
--- a/bbootimg/src/main/kotlin/sparse_util/SparseImgParser.kt
+++ b/bbootimg/src/main/kotlin/sparse_util/SparseImgParser.kt
@@ -3,7 +3,7 @@ package cfig.sparse_util
import cfig.EnvironmentVerifier
import cfig.packable.IPackable
import org.slf4j.LoggerFactory
-import cfig.Helper.Companion.check_call
+import cfig.helper.Helper.Companion.check_call
@OptIn(ExperimentalUnsignedTypes::class)
class SparseImgParser : IPackable {
diff --git a/bbootimg/src/test/kotlin/HelperTest.kt b/bbootimg/src/test/kotlin/HelperTest.kt
index 31fe31c..549fe60 100644
--- a/bbootimg/src/test/kotlin/HelperTest.kt
+++ b/bbootimg/src/test/kotlin/HelperTest.kt
@@ -1,11 +1,14 @@
import avb.alg.Algorithms
-import cfig.Helper
import cfig.KeyUtil
+import cfig.helper.Helper
import com.google.common.math.BigIntegerMath
import org.apache.commons.codec.binary.Hex
+import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream
import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.junit.Assert.*
+import org.junit.Assert.assertEquals
import org.junit.Test
+import org.slf4j.LoggerFactory
+import java.io.*
import java.math.BigInteger
import java.math.RoundingMode
import java.nio.file.Files
@@ -17,14 +20,12 @@ import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher
-import java.security.spec.RSAPublicKeySpec
-import java.security.PublicKey
-import java.security.spec.RSAPrivateKeySpec
-import java.security.PrivateKey
@OptIn(ExperimentalUnsignedTypes::class)
class HelperTest {
+ private val log = LoggerFactory.getLogger(HelperTest::class.java)
+
@Test
fun rawSignTest() {
val data = Hex.decodeHex("0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004206317a4c8d86accc8258c1ac23ef0ebd18bc33010d7afb43b241802646360b4ab")
diff --git a/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt b/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt
index c93952d..2fcf2b2 100644
--- a/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt
+++ b/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt
@@ -1,7 +1,6 @@
package avb.alg
-import avb.alg.Algorithms
-import cfig.Helper
+import cfig.helper.Helper
import org.junit.Assert
import org.junit.Test
diff --git a/bbootimg/src/test/kotlin/cfig/io/Struct3Test.kt b/bbootimg/src/test/kotlin/cfig/io/Struct3Test.kt
index 1a0fc76..c69934f 100644
--- a/bbootimg/src/test/kotlin/cfig/io/Struct3Test.kt
+++ b/bbootimg/src/test/kotlin/cfig/io/Struct3Test.kt
@@ -1,4 +1,4 @@
-import cfig.Helper
+import cfig.helper.Helper
import cfig.io.Struct3
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Assert
diff --git a/build.gradle.kts b/build.gradle.kts
index bf076be..ba78fca 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -5,8 +5,8 @@ import org.apache.commons.exec.DefaultExecutor
import org.apache.commons.exec.PumpStreamHandler
val GROUP_ANDROID = "android"
-if (parseGradleVersion(gradle.gradleVersion) < 5) {
- logger.error("ERROR: Gradle Version MUST >= 5.0, current is {}", gradle.gradleVersion)
+if (parseGradleVersion(gradle.gradleVersion) < 6) {
+ logger.error("ERROR: Gradle Version MUST >= 6.0, current is {}", gradle.gradleVersion)
throw RuntimeException("ERROR: Gradle Version")
} else {
logger.info("Gradle Version {}", gradle.gradleVersion)
diff --git a/integrationTest.py b/integrationTest.py
index b488cf9..8249078 100755
--- a/integrationTest.py
+++ b/integrationTest.py
@@ -83,6 +83,7 @@ def verifySingleDir(inResourceDir, inImageDir):
log.warning("calling %s" % pyFile)
subprocess.check_call(pyFile, shell = True)
cleanUp()
+ log.info("Leave %s" % os.path.join(resDir, imgDir))
def decompressXZ(inFile, outFile):
with lzma.open(inFile) as f:
@@ -114,6 +115,8 @@ def main():
verifySingleDir(resDir, "Q_preview_blueline_qpp2.190228.023")
# 10
verifySingleDir(resDir, "10.0.0_coral-qq1d.200205.002")
+ # 11
+ verifySingleDir(resDir, "11.0.0_redfin.rd1a.200810.021.a1")
log.info(successLogo)
diff --git a/src/integrationTest/resources b/src/integrationTest/resources
index 9aa5996..de9ef14 160000
--- a/src/integrationTest/resources
+++ b/src/integrationTest/resources
@@ -1 +1 @@
-Subproject commit 9aa59964ee1d0c828c8655ba7916f162c7721703
+Subproject commit de9ef14f57d6c39031710d015a4b3d13132bf5d7