From ddd1ad5ef2db2d238a4148943a9ec5a70e7b880d Mon Sep 17 00:00:00 2001 From: cfig Date: Mon, 12 Oct 2020 17:04:45 +0800 Subject: [PATCH] mass update dependent modules: aosp update kotlin: 1.4.0 -> 1.4.10 unify helpers between projects commons.compress 1.20 bbootimage: fix page size error in vendor_boot supress compile warnings lz4: change lz4 compress parameter if lz4 is not up-to-date use external lz4 to decompress kernel for Ubuntu <=18.04 cpio: use java cpio to decompress ramdisk add skipcpio to read contatenate cpio --- README.md | 4 +- aosp/avb/avbtool.v1.2.py | 34 +- aosp/build/tools/extract_kernel.py | 36 +- aosp/dracut/README.md | 1 + aosp/dracut/skipcpio.c | 122 +++++ aosp/system/libufdt/utils/src/mkdtboimg.py | 2 +- bbootimg/build.gradle.kts | 13 +- bbootimg/src/main/kotlin/avb/Avb.kt | 10 +- bbootimg/src/main/kotlin/avb/blob/AuthBlob.kt | 2 +- bbootimg/src/main/kotlin/avb/blob/AuxBlob.kt | 3 +- .../avb/desc/ChainPartitionDescriptor.kt | 2 +- .../main/kotlin/avb/desc/HashDescriptor.kt | 3 +- .../kotlin/avb/desc/HashTreeDescriptor.kt | 2 +- .../avb/desc/KernelCmdlineDescriptor.kt | 2 +- .../kotlin/avb/desc/PropertyDescriptor.kt | 2 +- .../main/kotlin/avb/desc/UnknownDescriptor.kt | 2 +- bbootimg/src/main/kotlin/bootimg/Common.kt | 26 +- bbootimg/src/main/kotlin/bootimg/Signer.kt | 2 +- .../main/kotlin/bootimg/v2/BootHeaderV2.kt | 2 +- bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt | 3 +- bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt | 2 +- .../src/main/kotlin/bootimg/v3/VendorBoot.kt | 18 +- .../src/main/kotlin/{ => helper}/Helper.kt | 291 ++++++------ bbootimg/src/main/kotlin/helper/ZipHelper.kt | 416 ++++++++++++++++++ bbootimg/src/main/kotlin/init/Reboot.kt | 1 + .../kotlin/kernel_util/KernelExtractor.kt | 2 +- .../src/main/kotlin/packable/BootImgParser.kt | 6 +- .../src/main/kotlin/packable/DtboParser.kt | 2 +- .../src/main/kotlin/packable/IPackable.kt | 6 +- .../src/main/kotlin/packable/VBMetaParser.kt | 2 +- .../main/kotlin/packable/VendorBootParser.kt | 2 +- .../kotlin/sparse_util/SparseImgParser.kt | 2 +- bbootimg/src/test/kotlin/HelperTest.kt | 13 +- .../src/test/kotlin/avb/alg/AlgorithmsTest.kt | 3 +- .../src/test/kotlin/cfig/io/Struct3Test.kt | 2 +- build.gradle.kts | 4 +- integrationTest.py | 3 + src/integrationTest/resources | 2 +- 38 files changed, 821 insertions(+), 229 deletions(-) create mode 100644 aosp/dracut/README.md create mode 100644 aosp/dracut/skipcpio.c rename bbootimg/src/main/kotlin/{ => helper}/Helper.kt (62%) create mode 100644 bbootimg/src/main/kotlin/helper/ZipHelper.kt 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 -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/fa6a49bb22b84307b12e7a8878867c1e)](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) [![Build Status](https://travis-ci.org/cfig/Android_boot_image_editor.svg?branch=master)](https://travis-ci.org/cfig/Android_boot_image_editor) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/fa6a49bb22b84307b12e7a8878867c1e)](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) [![License](http://img.shields.io/:license-apache-blue.svg?style=flat-square)](http://www.apache.org/licenses/LICENSE-2.0.html) -A tool for reverse engineering Android ROM images. (working on ![Linux](doc/linux24.png) and ![Mac](doc/apple24.png)) +A tool for reverse engineering Android ROM images. (working on ![Linux](doc/linux24.png)(Ubuntu 18.04+) and ![Mac](doc/apple24.png)) ## 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