From 43078016d4bb78c6ccf430d5cfb0ec4c8d7d2bcb Mon Sep 17 00:00:00 2001 From: cfig Date: Fri, 16 May 2025 15:29:22 +0800 Subject: [PATCH] staging --- bbootimg/build.gradle.kts | 14 +-- bbootimg/src/main/kotlin/bootimg/Common.kt | 70 +++++++++++-- bbootimg/src/main/kotlin/bootimg/Signer.kt | 52 +++++++++- .../main/kotlin/bootimg/cpio/AndroidCpio.kt | 1 + bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt | 74 ++++++++------ bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt | 48 +++++---- .../src/main/kotlin/bootimg/v3/VendorBoot.kt | 68 ++++++++----- .../src/main/kotlin/packable/BootImgParser.kt | 79 +++++++++++++-- .../main/kotlin/packable/PackableLauncher.kt | 98 ++++++++++--------- .../src/main/kotlin/packable/VBMetaParser.kt | 31 +++++- .../main/kotlin/packable/VendorBootParser.kt | 38 +++++-- doc/feature_list.md | 8 +- helper/build.gradle.kts | 14 +-- lazybox/build.gradle.kts | 15 +-- settings.gradle.kts | 5 + tools/factory_image_parser.py | 6 ++ tools/local/bin/{abe => be} | 28 ++++-- 17 files changed, 468 insertions(+), 181 deletions(-) rename tools/local/bin/{abe => be} (63%) diff --git a/bbootimg/build.gradle.kts b/bbootimg/build.gradle.kts index c47ee63..67ceee8 100644 --- a/bbootimg/build.gradle.kts +++ b/bbootimg/build.gradle.kts @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "2.1.0" + kotlin("jvm") version "2.1.20" application } @@ -62,11 +62,13 @@ java { targetCompatibility = JavaVersion.VERSION_11 } -tasks.withType().all { - kotlinOptions { - freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" - freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes" - jvmTarget = "11" +tasks.withType().configureEach { + compilerOptions { + freeCompilerArgs.addAll( + "-opt-in=kotlin.RequiresOptIn", + "-opt-in=kotlin.ExperimentalUnsignedTypes" + ) + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) } } diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt index ed99534..aa1b0c7 100644 --- a/bbootimg/src/main/kotlin/bootimg/Common.kt +++ b/bbootimg/src/main/kotlin/bootimg/Common.kt @@ -20,6 +20,7 @@ import cfig.bootimg.cpio.AndroidCpio import rom.fdt.DTC import cfig.helper.Helper import cfig.helper.ZipHelper +import cfig.packable.BootImgParser import cfig.utils.KernelExtractor import com.github.freva.asciitable.HorizontalAlign import org.apache.commons.exec.CommandLine @@ -32,6 +33,8 @@ import java.io.File import java.nio.file.Files import java.nio.file.Paths import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException import java.lang.NumberFormatException import java.nio.ByteBuffer import java.nio.ByteOrder @@ -52,6 +55,12 @@ class Common { private val log = LoggerFactory.getLogger(Common::class.java) private const val MAX_ANDROID_VER = 11 + val loadProperties: (String) -> Properties = { fileName -> + Properties().apply { + File(fileName).inputStream().use { load(it) } + } + } + @Throws(IllegalArgumentException::class) fun packOsVersion(x: String?): Int { if (x.isNullOrBlank()) return 0 @@ -423,19 +432,24 @@ class Common { ) } - fun printPackSummary(imageName: String) { + fun printPackSummary(imageName: String, outFile: String? = null) { val prints: MutableList> = mutableListOf() val tableHeader = de.vandermeer.asciitable.AsciiTable().apply { addRule(); addRow("What", "Where"); addRule() } val tab = de.vandermeer.asciitable.AsciiTable().let { it.addRule() - if (File("$imageName.signed").exists()) { - it.addRow("re-packed $imageName", "$imageName.signed") - prints.add(Pair("re-packed $imageName", "$imageName.signed")) + if (outFile != null) { + it.addRow("re-packed $imageName", outFile) + prints.add(Pair("re-packed $imageName", outFile)) } else { - it.addRow("re-packed $imageName", "$imageName.clear") - prints.add(Pair("re-packed $imageName", "$imageName.clear")) + if (File("$imageName.signed").exists()) { + it.addRow("re-packed $imageName", "$imageName.signed") + prints.add(Pair("re-packed $imageName", "$imageName.signed")) + } else { + it.addRow("re-packed $imageName", "$imageName.clear") + prints.add(Pair("re-packed $imageName", "$imageName.clear")) + } } it.addRule() it @@ -457,6 +471,31 @@ class Common { } } + /* + be_caller_dir: set in "be" script, to support out of tree invocation + */ + fun shortenPath(fullPath: String, inCurrentPath: String = System.getProperty("user.dir")): String { + val currentPath = System.getenv("be_caller_dir") ?: inCurrentPath + val full = Paths.get(fullPath).normalize().toAbsolutePath() + val base = Paths.get(currentPath).normalize().toAbsolutePath() + return try { + base.relativize(full).toString() + } catch (e: IllegalArgumentException) { + full.toString() + } + } + + fun String.toShortenPath(inCurrentPath: String = System.getProperty("user.dir")): String { + val currentPath = System.getenv("be_caller_dir") ?: inCurrentPath + val full = Paths.get(this).normalize().toAbsolutePath() + val base = Paths.get(currentPath).normalize().toAbsolutePath() + return try { + base.relativize(full).toString() + } catch (e: IllegalArgumentException) { + full.toString() + } + } + fun printPackSummaryInternal(imageName: String) { val prints: MutableList> = mutableListOf() val tableHeader = de.vandermeer.asciitable.AsciiTable().apply { @@ -485,5 +524,24 @@ class Common { log.info("\n\t\t\tPack Summary of ${imageName}\n{}\n{}", tableHeader.render(), tab.render()) } } + + fun createWorkspaceIni(fileName: String) { + //create workspace file + val workspaceFile = File(Helper.prop("workDir"), "workspace.ini").canonicalPath + try { + FileOutputStream(workspaceFile).use { output -> + Properties().also { + it.setProperty("file", fileName) + it.setProperty("workDir", Helper.prop("workDir")) + it.setProperty("role", File(fileName).name) + it.store(output, "unpackInternal configuration") + } + log.info("workspace file created: $workspaceFile") + } + } catch (e: IOException) { + log.error("error writing workspace file: ${e.message}") + } + //create workspace file done + } } } diff --git a/bbootimg/src/main/kotlin/bootimg/Signer.kt b/bbootimg/src/main/kotlin/bootimg/Signer.kt index 7609967..600a29a 100644 --- a/bbootimg/src/main/kotlin/bootimg/Signer.kt +++ b/bbootimg/src/main/kotlin/bootimg/Signer.kt @@ -30,8 +30,58 @@ class Signer { companion object { private val log = LoggerFactory.getLogger(Signer::class.java) + fun signAVB2(inFile: String, //"$output.clear" + outFile: String, //"$output.signed" + aiFile: String, //AVBInfo + imageSize: Long, + avbtool: String) { + log.info("Adding hash_footer with verified-boot 2.0 style: $inFile -> $outFile") + val ai = ObjectMapper().readValue(File(aiFile), AVBInfo::class.java) + val alg = Algorithms.get(ai.header!!.algorithm_type) + val bootDesc = ai.auxBlob!!.hashDescriptors[0] + val newAvbInfo = ObjectMapper().readValue(File(aiFile), AVBInfo::class.java) + + //our signer + File(inFile).copyTo(File(outFile), overwrite = true) + Avb().addHashFooter(outFile, + imageSize, + partition_name = bootDesc.partition_name, + newAvbInfo = newAvbInfo) + //original signer + val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else "" + CommandLine.parse("$cmdPrefix$avbtool add_hash_footer").apply { + addArguments("--image ${outFile}2") //boot.img.signed2 + addArguments("--flags ${ai.header!!.flags}") + addArguments("--partition_size ${imageSize}") + addArguments("--salt ${Helper.toHexString(bootDesc.salt)}") + addArguments("--partition_name ${bootDesc.partition_name}") + addArguments("--hash_algorithm ${bootDesc.hash_algorithm}") + addArguments("--algorithm ${alg!!.name}") + addArguments("--rollback_index ${ai.header!!.rollback_index}") + if (alg.defaultKey.isNotBlank()) { + addArguments("--key ${alg.defaultKey}") + } + newAvbInfo.auxBlob?.let { newAuxblob -> + newAuxblob.propertyDescriptors.forEach { newProp -> + addArguments(arrayOf("--prop", "${newProp.key}:${newProp.value}")) + } + } + addArgument("--internal_release_string") + addArgument(ai.header!!.release_string, false) + log.info(this.toString()) + + File(inFile).copyTo(File("${outFile}2"), overwrite = true) + DefaultExecutor().execute(this) + } + Helper.assertFileEquals(outFile, "${outFile}2") + File("${outFile}2").delete() + //TODO: decide what to verify + //Parser.verifyAVBIntegrity(cfg.info.output + ".signed", avbtool) + //Parser.verifyAVBIntegrity(cfg.info.output + ".signed2", avbtool) + } + fun signAVB(output: String, imageSize: Long, avbtool: String) { - log.info("Adding hash_footer with verified-boot 2.0 style") + log.info("Adding hash_footer with verified-boot 2.0 style: $output") val ai = ObjectMapper().readValue(File(getJsonFileName(output)), AVBInfo::class.java) val alg = Algorithms.get(ai.header!!.algorithm_type) val bootDesc = ai.auxBlob!!.hashDescriptors[0] diff --git a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt index dd40d46..59e5925 100644 --- a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt +++ b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt @@ -194,6 +194,7 @@ class AndroidCpio { val rounded = Helper.round_to_multiple(len, 256) //file in page 256 if (len != rounded) { FileOutputStream(outFile, true).use { fos -> + log.info("cpio padding size: " + (rounded - len) + " bytes") fos.write(ByteArray((rounded - len).toInt())) } } diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt index 4d9934a..7345f2f 100644 --- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt +++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt @@ -16,15 +16,14 @@ package cfig.bootimg.v2 import avb.AVBInfo import cfig.Avb -import cfig.bootimg.Common as C import cfig.bootimg.Common import cfig.bootimg.Common.Companion.deleleIfExists +import cfig.bootimg.Common.Companion.shortenPath import cfig.bootimg.Signer import cfig.helper.Dumpling import cfig.helper.Helper import cfig.helper.ZipHelper import cfig.packable.VBMetaParser -import rom.fdt.DTC import cfig.utils.EnvironmentVerifier import com.fasterxml.jackson.databind.ObjectMapper import com.github.freva.asciitable.HorizontalAlign @@ -32,11 +31,13 @@ import de.vandermeer.asciitable.AsciiTable import org.apache.commons.exec.CommandLine import org.apache.commons.exec.DefaultExecutor import org.slf4j.LoggerFactory +import rom.fdt.DTC import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.nio.ByteBuffer import java.nio.ByteOrder +import cfig.bootimg.Common as C data class BootV2( var info: MiscInfo = MiscInfo(), @@ -95,7 +96,9 @@ data class BootV2( companion object { private val log = LoggerFactory.getLogger(BootV2::class.java) - private val workDir = Helper.prop("workDir") + private val workDir: () -> String = { + Helper.prop("workDir")!! + } private val mapper = ObjectMapper() private val dtsSuffix = Helper.prop("config.dts_suffix") @@ -128,7 +131,7 @@ data class BootV2( } } ret.kernel.let { theKernel -> - theKernel.file = File(workDir, "kernel").path + theKernel.file = File(workDir(), "kernel").path theKernel.size = bh2.kernelLength theKernel.loadOffset = bh2.kernelOffset theKernel.position = ret.getKernelPosition() @@ -138,28 +141,28 @@ data class BootV2( theRamdisk.loadOffset = bh2.ramdiskOffset theRamdisk.position = ret.getRamdiskPosition() if (bh2.ramdiskLength > 0) { - theRamdisk.file = File(workDir, "ramdisk.img").path + theRamdisk.file = File(workDir(), "ramdisk.img").path } } if (bh2.secondBootloaderLength > 0) { ret.secondBootloader = CommArgs() ret.secondBootloader!!.size = bh2.secondBootloaderLength ret.secondBootloader!!.loadOffset = bh2.secondBootloaderOffset - ret.secondBootloader!!.file = File(workDir, "second").path + ret.secondBootloader!!.file = File(workDir(), "second").path ret.secondBootloader!!.position = ret.getSecondBootloaderPosition() } if (bh2.recoveryDtboLength > 0) { ret.recoveryDtbo = CommArgsLong() ret.recoveryDtbo!!.size = bh2.recoveryDtboLength ret.recoveryDtbo!!.loadOffset = bh2.recoveryDtboOffset //Q - ret.recoveryDtbo!!.file = File(workDir, "recoveryDtbo").path + ret.recoveryDtbo!!.file = File(workDir(), "recoveryDtbo").path ret.recoveryDtbo!!.position = ret.getRecoveryDtboPosition() } if (bh2.dtbLength > 0) { ret.dtb = DtbArgsLong() ret.dtb!!.size = bh2.dtbLength ret.dtb!!.loadOffset = bh2.dtbOffset //Q - ret.dtb!!.file = File(workDir, "dtb").path + ret.dtb!!.file = File(workDir(), "dtb").path ret.dtb!!.position = ret.getDtbPosition() } } @@ -206,7 +209,7 @@ data class BootV2( fun extractImages(): BootV2 { //info - mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this) + mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir(), info.json), this) //kernel if (kernel.size > 0) { Common.dumpKernel(Helper.Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!)) @@ -220,15 +223,17 @@ data class BootV2( //ramdisk if (this.ramdisk.size > 0) { val fmt = C.dumpRamdisk( - Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), File(workDir, "root").path + Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), + File(workDir(), "root").path ) this.ramdisk.file = this.ramdisk.file!! + ".$fmt" if (fmt == "xz") { - val checkType = ZipHelper.xzStreamFlagCheckTypeToString(ZipHelper.parseStreamFlagCheckType(this.ramdisk.file!!)) + val checkType = + ZipHelper.xzStreamFlagCheckTypeToString(ZipHelper.parseStreamFlagCheckType(this.ramdisk.file!!)) this.ramdisk.xzFlags = checkType } //dump info again - mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this) + mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir(), this.info.json), this) } //second bootloader secondBootloader?.let { @@ -254,7 +259,7 @@ data class BootV2( this.dtb!!.dtbList = DTC.parseMultiple(dtb!!.file!!) log.info("dtb sz = " + this.dtb!!.dtbList.size) //dump info again - mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this) + mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir(), info.json), this) //dump dtb items DTC.extractMultiple(dtb!!.file!!, this.dtb!!.dtbList) @@ -285,8 +290,8 @@ data class BootV2( } val tab = AsciiTable().let { it.addRule() - it.addRow("image info", workDir + info.output.removeSuffix(".img") + ".json") - prints.add(Pair("image info", workDir + info.output.removeSuffix(".img") + ".json")) + it.addRow("image info", shortenPath( File(Helper.prop("workDir"), info.output.removeSuffix(".img") + ".json").path)) + prints.add(Pair("image info", shortenPath(File(workDir(), info.output.removeSuffix(".img") + ".json").path))) if (this.info.verify.startsWith("VB2.0")) { it.addRule() val verifyStatus = if (this.info.verify.contains("PASS")) { @@ -311,11 +316,11 @@ data class BootV2( //kernel it.addRule() if (this.kernel.size > 0) { - it.addRow("kernel", this.kernel.file) + it.addRow("kernel", shortenPath(this.kernel.file!!)) } else { //only for ramdisk.img, Issue #122 it.addRow("kernel", "NONE") } - prints.add(Pair("kernel", this.kernel.file.toString())) + prints.add(Pair("kernel", shortenPath(this.kernel.file.toString()))) File(Helper.prop("kernelVersionFile")).let { kernelVersionFile -> if (kernelVersionFile.exists()) { it.addRow("\\-- version " + kernelVersionFile.readLines().toString(), kernelVersionFile.path) @@ -331,10 +336,10 @@ data class BootV2( //ramdisk if (this.ramdisk.size > 0) { it.addRule() - it.addRow("ramdisk", this.ramdisk.file) - prints.add(Pair("ramdisk", this.ramdisk.file.toString())) - it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path) - prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path)) + it.addRow("ramdisk", shortenPath(this.ramdisk.file!!)) + prints.add(Pair("ramdisk", shortenPath(this.ramdisk.file.toString()))) + it.addRow("\\-- extracted ramdisk rootfs", shortenPath(File(workDir(), "root").path)) + prints.add(Pair("\\-- extracted ramdisk rootfs", shortenPath(File(workDir(), "root").path))) } //second this.secondBootloader?.let { theSecondBootloader -> @@ -382,15 +387,17 @@ data class BootV2( } if (EnvironmentVerifier().isWindows) { - log.info("\n" + - com.github.freva.asciitable.AsciiTable.getTable( - com.github.freva.asciitable.AsciiTable.BASIC_ASCII, - prints, mutableListOf( - com.github.freva.asciitable.Column().header("What") - .dataAlign(HorizontalAlign.LEFT) - .with { it.first }, - com.github.freva.asciitable.Column().header("Where").with { it.second }) - )) + log.info( + "\n" + + com.github.freva.asciitable.AsciiTable.getTable( + com.github.freva.asciitable.AsciiTable.BASIC_ASCII, + prints, mutableListOf( + com.github.freva.asciitable.Column().header("What") + .dataAlign(HorizontalAlign.LEFT) + .with { it.first }, + com.github.freva.asciitable.Column().header("Where").with { it.second }) + ) + ) } else { log.info( "\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}", @@ -433,14 +440,14 @@ data class BootV2( ramdisk.file = null ramdisk.loadOffset = 0 } else { - if (File(this.ramdisk.file!!).exists() && !File(workDir + "root").exists()) { + if (File(this.ramdisk.file!!).exists() && !File(workDir(), "root").exists()) { //do nothing if we have ramdisk.img.gz but no /root log.warn("Use prebuilt ramdisk file: ${this.ramdisk.file}") } else { File(this.ramdisk.file!!).deleleIfExists() File(this.ramdisk.file!!.removeSuffix(".gz")).deleleIfExists() //Common.packRootfs("${workDir}/root", this.ramdisk.file!!, Common.parseOsMajor(info.osVersion.toString())) - Common.packRootfs(File(workDir, "root").path, this.ramdisk.file!!, this.ramdisk.xzFlags) + Common.packRootfs(File(workDir(), "root").path, this.ramdisk.file!!, this.ramdisk.xzFlags) } this.ramdisk.size = File(this.ramdisk.file!!).length().toInt() } @@ -466,18 +473,21 @@ data class BootV2( 0 -> { Common.hashFileAndSize(kernel.file, ramdisk.file, secondBootloader?.file) } + 1 -> { Common.hashFileAndSize( kernel.file, ramdisk.file, secondBootloader?.file, recoveryDtbo?.file ) } + 2 -> { Common.hashFileAndSize( kernel.file, ramdisk.file, secondBootloader?.file, recoveryDtbo?.file, dtb?.file ) } + else -> { throw IllegalArgumentException("headerVersion ${info.headerVersion} illegal") } diff --git a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt index 3506d93..0f2dfd1 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt @@ -22,6 +22,7 @@ import cfig.bootimg.Common import cfig.utils.EnvironmentVerifier import cfig.bootimg.Common.Companion.deleleIfExists import cfig.bootimg.Common.Companion.getPaddingSize +import cfig.bootimg.Common.Companion.shortenPath import cfig.bootimg.Signer import cfig.helper.Helper import cfig.helper.Dumpling @@ -228,22 +229,26 @@ data class BootV3( } if (fileName != info.role) { - Helper.setProp("out.file", fileName) if (bSigningNeeded) { + log.info("x1") + Helper.setProp("out.file", "$fileName.signed") //@formatter:off File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".signed")) - .copyTo(File(fileName), true) + .copyTo(File(Helper.prop("out.file")!!), true) //@formatter:on - log.info("Signed image saved as $fileName") + log.info("Signed image saved as " + Helper.prop("out.file")) } else { + log.info("x2") + Helper.setProp("out.file", fileName) //@formatter:off File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".clear")) - .copyTo(File(fileName), true) + .copyTo(File(Helper.prop("out.file")!!), true) //@formatter:on - log.info("Unsigned image saved as $fileName") + log.info("Unsigned image saved as " + Helper.prop("out.file")) } } else { if (bSigningNeeded) { + log.info("x3") Helper.setProp("out.file", info.role + ".signed") //@formatter:off File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".signed")) @@ -251,6 +256,7 @@ data class BootV3( //@formatter:on log.info("Signed image saved as ${info.role}.signed") } else { + log.info("x4") Helper.setProp("out.file", info.role + ".clear") //@formatter:off File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".clear")) @@ -385,27 +391,27 @@ data class BootV3( } val tab = AsciiTable().let { it.addRule() - it.addRow("image info", Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json")) - prints.add(Pair("image info", Helper.joinPath(workDir, info.role.removeSuffix(".img") + ".json"))) + it.addRow("image info", shortenPath(Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json"))) + prints.add(Pair("image info", shortenPath(Helper.joinPath(workDir, info.role.removeSuffix(".img") + ".json")))) it.addRule() if (this.kernel.size > 0) { - it.addRow("kernel", this.kernel.file) - prints.add(Pair("kernel", this.kernel.file)) + it.addRow("kernel", shortenPath(this.kernel.file)) + prints.add(Pair("kernel", shortenPath(this.kernel.file))) File(Helper.joinPath(workDir, Helper.prop("kernelVersionStem")!!)).let { kernelVersionFile -> if (kernelVersionFile.exists()) { - it.addRow("\\-- version " + kernelVersionFile.readLines().toString(), kernelVersionFile.path) + it.addRow("\\-- version " + kernelVersionFile.readLines().toString(), shortenPath( kernelVersionFile.path)) prints.add( Pair( "\\-- version " + kernelVersionFile.readLines().toString(), - kernelVersionFile.path + shortenPath(kernelVersionFile.path) ) ) } } File(Helper.joinPath(workDir, Helper.prop("kernelConfigStem")!!)).let { kernelConfigFile -> if (kernelConfigFile.exists()) { - it.addRow("\\-- config", kernelConfigFile.path) - prints.add(Pair("\\-- config", kernelConfigFile.path)) + it.addRow("\\-- config", shortenPath(kernelConfigFile.path)) + prints.add(Pair("\\-- config", shortenPath(kernelConfigFile.path))) } } it.addRule() @@ -442,11 +448,11 @@ data class BootV3( File(Avb.getJsonFileName("sig.boot")).let { jsonFile -> if (jsonFile.exists()) { it.addRow("GKI signature 2.0", this.bootSignature.file) - it.addRow("\\-- boot", jsonFile.path) + it.addRow("\\-- boot", shortenPath(jsonFile.path)) it.addRow("\\------ signing key", Avb.inspectKey(mapper.readValue(jsonFile, AVBInfo::class.java))) //basic prints.add(Pair("GKI signature 2.0", this.bootSignature.file)) - prints.add(Pair("\\-- boot", jsonFile.path)) + prints.add(Pair("\\-- boot", shortenPath(jsonFile.path))) prints.add( Pair( "\\------ signing key", @@ -458,19 +464,19 @@ data class BootV3( File(Avb.getJsonFileName("sig.kernel")).let { jsonFile -> if (jsonFile.exists()) { val readBackAvb = mapper.readValue(jsonFile, AVBInfo::class.java) - it.addRow("\\-- kernel", jsonFile.path) + it.addRow("\\-- kernel", shortenPath(jsonFile.path)) it.addRow("\\------ signing key", Avb.inspectKey(readBackAvb)) it.addRule() //basic - prints.add(Pair("\\-- kernel", jsonFile.path)) + prints.add(Pair("\\-- kernel", shortenPath(jsonFile.path))) prints.add(Pair("\\------ signing key", Avb.inspectKey(readBackAvb))) } } //AVB info Avb.getJsonFileName(info.role).let { jsonFile -> - it.addRow("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE") - prints.add(Pair("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE")) + it.addRow("AVB info", if (File(jsonFile).exists()) shortenPath(jsonFile) else "NONE") + prints.add(Pair("AVB info", if (File(jsonFile).exists()) shortenPath(jsonFile) else "NONE")) if (File(jsonFile).exists()) { mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai -> it.addRow("\\------ signing key", Avb.inspectKey(ai)) @@ -487,10 +493,10 @@ data class BootV3( if (File(vbmetaCompanion).exists()) { log.warn("XXXX: Found vbmeta.img, parsing ...") //basic - prints.add(Pair("vbmeta.img", Avb.getJsonFileName("vbmeta.img"))) + prints.add(Pair("vbmeta.img", shortenPath(Avb.getJsonFileName("vbmeta.img")))) //table it.addRule() - it.addRow("vbmeta.img", Avb.getJsonFileName("vbmeta.img")) + it.addRow("vbmeta.img", shortenPath(Avb.getJsonFileName("vbmeta.img"))) it.addRule() "\n" + it.render() } else { diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt index 6a515e4..04ba1dc 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt @@ -19,18 +19,19 @@ import cc.cfig.io.Struct import cfig.Avb import cfig.bootimg.Common import cfig.bootimg.Common.Companion.deleleIfExists +import cfig.bootimg.Common.Companion.toShortenPath import cfig.bootimg.Signer import cfig.helper.Dumpling import cfig.helper.Helper import cfig.helper.ZipHelper import cfig.packable.VBMetaParser -import rom.fdt.DTC import cfig.utils.EnvironmentVerifier import com.fasterxml.jackson.databind.ObjectMapper import de.vandermeer.asciitable.AsciiTable import org.apache.commons.exec.CommandLine import org.apache.commons.exec.DefaultExecutor import org.slf4j.LoggerFactory +import rom.fdt.DTC import java.io.* import java.nio.ByteBuffer import java.nio.ByteOrder @@ -265,7 +266,9 @@ data class VendorBoot( } this.dtb.size = File(this.dtb.file).length().toInt() //header - FileOutputStream(this.info.role + ".clear", false).use { fos -> + val clearFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".clear") + val googleFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".google") + FileOutputStream(clearFile, false).use { fos -> val encodedHeader = this.toHeader().encode() fos.write(encodedHeader) fos.write(ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size)) @@ -303,17 +306,17 @@ data class VendorBoot( } } //write - FileOutputStream("${this.info.role}.clear", true).use { fos -> + FileOutputStream(clearFile, true).use { fos -> fos.write(bf.array(), 0, bf.position()) } //google way - this.toCommandLine().addArgument(this.info.role + ".google").let { + this.toCommandLine().addArgument(googleFile).let { log.info(it.toString()) DefaultExecutor().execute(it) } - Helper.assertFileEquals(this.info.role + ".clear", this.info.role + ".google") + Helper.assertFileEquals(clearFile, googleFile) return this } @@ -321,7 +324,15 @@ data class VendorBoot( val avbtool = String.format(Helper.prop("avbtool")!!, "v1.2") File(Avb.getJsonFileName(info.role)).let { if (it.exists()) { - Signer.signAVB(info.role, this.info.imageSize, avbtool) + //Signer.signAVB(info.role, this.info.imageSize, avbtool) + val clearFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".clear") + val signedFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".signed") + Signer.signAVB2(clearFile, + signedFile, + Avb.getJsonFileName(info.role), + this.info.imageSize, + avbtool + ) } else { log.warn("skip signing of ${info.role}") } @@ -329,6 +340,13 @@ data class VendorBoot( return this } + fun postCopy(outFile: String): VendorBoot { + val signedFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".signed") + log.info("COPY $signedFile -> $outFile") + File(signedFile).copyTo(File(outFile), overwrite = true) + return this + } + fun updateVbmeta(): VendorBoot { Avb.updateVbmeta(info.role) return this @@ -416,31 +434,31 @@ data class VendorBoot( } val tab = AsciiTable().let { it.addRule() - val imageInfoJsonFile = Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json") + val imageInfoJsonFile = Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json").toShortenPath() it.addRow("image info", imageInfoJsonFile) prints.add(Pair("image info", imageInfoJsonFile)) it.addRule() - it.addRow("ramdisk", this.ramdisk.file) - prints.add(Pair("ramdisk", this.ramdisk.file)) + it.addRow("ramdisk", this.ramdisk.file.toShortenPath()) + prints.add(Pair("ramdisk", this.ramdisk.file.toShortenPath())) if (this.ramdisk_table.size > 0) { this.ramdisk_table.ramdidks.forEachIndexed { index, entry -> //fancy ascii - it.addRow("-- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file) - it.addRow("------- extracted rootfs", File(workDir, "root.${index + 1}").path) + it.addRow("-- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file.toShortenPath()) + it.addRow("------- extracted rootfs", File(workDir, "root.${index + 1}").path.toShortenPath()) //basic ascii //@formatter:off - prints.add(Pair(" -- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file)) + prints.add(Pair(" -- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file.toShortenPath())) //@formatter:on - prints.add(Pair(" ------- extracted rootfs", File(workDir, "root.${index + 1}").path)) + prints.add(Pair(" ------- extracted rootfs", File(workDir, "root.${index + 1}").path.toShortenPath())) } } else { - it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path) - prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path)) + it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path.toShortenPath()) + prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path.toShortenPath())) } it.addRule() if (this.dtb.size > 0) { - it.addRow("dtb", this.dtb.file) - prints.add(Pair("dtb", this.dtb.file)) + it.addRow("dtb", this.dtb.file.toShortenPath()) + prints.add(Pair("dtb", this.dtb.file.toShortenPath())) if (File(this.dtb.file + ".0.${dtsSuffix}").exists()) { it.addRow("\\-- decompiled dts [${dtb.dtbList.size}]", dtb.file + "*.${dtsSuffix}") prints.add(Pair("\\-- decompiled dts [${dtb.dtbList.size}]", dtb.file + "*.${dtsSuffix}")) @@ -451,14 +469,14 @@ data class VendorBoot( } if (this.bootconfig.size > 0) { it.addRule() - it.addRow("bootconfig", this.bootconfig.file) - prints.add(Pair("bootconfig", this.bootconfig.file)) + it.addRow("bootconfig", this.bootconfig.file.toShortenPath()) + prints.add(Pair("bootconfig", this.bootconfig.file.toShortenPath())) } it.addRule() Avb.getJsonFileName(info.role).let { jsonFile -> if (File(jsonFile).exists()) { - it.addRow("AVB info", jsonFile) - prints.add(Pair("AVB info", jsonFile)) + it.addRow("AVB info", jsonFile.toShortenPath()) + prints.add(Pair("AVB info", jsonFile.toShortenPath())) mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai -> it.addRow("\\-- signing key", Avb.inspectKey(ai)) prints.add(Pair(" \\-- signing key", Avb.inspectKey(ai))) @@ -474,8 +492,8 @@ data class VendorBoot( val tabVBMeta = AsciiTable().let { if (File("vbmeta.img").exists()) { it.addRule() - it.addRow("vbmeta.img", Avb.getJsonFileName("vbmeta.img")) - prints.add(Pair("vbmeta.img", Avb.getJsonFileName("vbmeta.img"))) + it.addRow("vbmeta.img", Avb.getJsonFileName("vbmeta.img").toShortenPath()) + prints.add(Pair("vbmeta.img", Avb.getJsonFileName("vbmeta.img").toShortenPath())) it.addRule() "\n" + it.render() } else { @@ -492,8 +510,8 @@ data class VendorBoot( return this } - fun printPackSummary(): VendorBoot { - Common.printPackSummary(info.role) + fun printPackSummary(outFileName: String): VendorBoot { + Common.printPackSummary(info.role, outFileName) return this } diff --git a/bbootimg/src/main/kotlin/packable/BootImgParser.kt b/bbootimg/src/main/kotlin/packable/BootImgParser.kt index bedda87..4123292 100644 --- a/bbootimg/src/main/kotlin/packable/BootImgParser.kt +++ b/bbootimg/src/main/kotlin/packable/BootImgParser.kt @@ -15,6 +15,7 @@ package cfig.packable import avb.blob.Footer +import cfig.bootimg.Common import cfig.bootimg.Common.Companion.probeHeaderVersion import cfig.bootimg.v2.BootV2 import cfig.bootimg.v2.BootV2Dialects @@ -45,14 +46,20 @@ class BootImgParser : IPackable { } override fun unpack(fileName: String) { - unpackInternal(fileName, fileName, outDir) + unpackInternal(fileName, outDir) } - fun unpackInternal(targetFile: String, fileName: String, unpackDir: String) { - log.info("unpackInternal(fileName: $fileName, unpackDir: $unpackDir)") - Helper.setProp("workDir", unpackDir) + // called via reflection + fun unpackInternal(inFileName: String, unpackDir: String) { + log.info("unpackInternal(fileName: $inFileName, unpackDir: $unpackDir)") + val fileName = File(inFileName).canonicalPath + Helper.setProp("workDir", File(unpackDir).canonicalPath) + log.info("workspace set to $unpackDir") clear() - File("$outDir/role").writeText(File(File(targetFile).canonicalPath).name) + //create workspace file + Common.createWorkspaceIni(fileName) + //create workspace file done + val hv = probeHeaderVersion(fileName) log.info("header version $hv") when (hv) { @@ -85,7 +92,64 @@ class BootImgParser : IPackable { } } - fun packInternal(targetFile: String, workspace: String, fileName: String) { + // called via reflection + fun packInternal(workspace: String, outFileName: String) { + log.info("packInternal($workspace, $outFileName)") + Helper.setProp("workDir", workspace) + val targetFile = outFileName + val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("role") + val cfgFile = File(workspace, iniRole.removeSuffix(".img") + ".json").canonicalPath + log.info("Loading config from $cfgFile") + if (!File(cfgFile).exists()) { + val tab = AsciiTable().let { + it.addRule() + it.addRow("'$cfgFile' doesn't exist, did you forget to 'unpack' ?") + it.addRule() + it + } + log.info("\n{}", tab.render()) + return + } + + val worker = + try { + ObjectMapper().readValue(File(cfgFile), BootV2::class.java) + } catch (e: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException) { + try { + ObjectMapper().readValue(File(cfgFile), BootV3::class.java) + } catch (e: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException) { + null + } + } + if (worker == null) { + log.error("no worker available") + exitProcess(2) + } + when (worker) { + is BootV2 -> { + worker + .pack() + .sign() + .updateVbmeta() + .printPackSummary() + } + + is BootV3 -> { + worker + .pack() + .sign(targetFile) + .updateVbmeta() + .printPackSummary(worker.info.role) + } + + else -> { + log.error("unsupported boot image format") + exitProcess(2) + } + } + } + + fun packInternalLegacy(targetFile: String, workspace: String, fileName: String) { log.info("packInternal(targetFile: $targetFile, fileName: $fileName, workspace: $workspace)") Helper.setProp("workDir", workspace) val cfgFile = Helper.joinPath(outDir, targetFile.removeSuffix(".img") + ".json") @@ -140,7 +204,8 @@ class BootImgParser : IPackable { } override fun pack(fileName: String) { - packInternal(fileName, outDir, fileName) + val targetFile = Common.loadProperties(File(outDir, "workspace.ini").canonicalPath).getProperty("role") + packInternal(outDir, targetFile) } fun flash(fileName: String) { diff --git a/bbootimg/src/main/kotlin/packable/PackableLauncher.kt b/bbootimg/src/main/kotlin/packable/PackableLauncher.kt index 0fd68d9..1d3b734 100644 --- a/bbootimg/src/main/kotlin/packable/PackableLauncher.kt +++ b/bbootimg/src/main/kotlin/packable/PackableLauncher.kt @@ -14,10 +14,12 @@ package cfig.packable +import cfig.helper.Helper import org.slf4j.LoggerFactory import packable.DeviceTreeParser import rom.sparse.SparseImgParser import java.io.File +import java.util.Properties import java.util.regex.Pattern import kotlin.reflect.KClass import kotlin.reflect.full.createInstance @@ -28,6 +30,12 @@ class PackableLauncher fun main(args: Array) { val log = LoggerFactory.getLogger(PackableLauncher::class.java) + val devLog = LoggerFactory.getLogger("XXXX") + val loadProperties: (String) -> Properties = { fileName -> + Properties().apply { + File(fileName).inputStream().use { load(it) } + } + } val packablePool = mutableMapOf, KClass>() listOf( BootImgParser(), @@ -44,25 +52,43 @@ fun main(args: Array) { packablePool.put(it.capabilities(), it::class as KClass) } packablePool.forEach { - log.debug("" + it.key + "/" + it.value) + log.info("" + it.key + "/" + it.value) } var targetFile: String? = null var targetHandler: KClass? = null + var targetDir: String? = null - log.info("XXXX: args: " + args.asList().toString()) + devLog.info("args: " + args.asList().toString()) + args.forEachIndexed { index, s -> + devLog.info(" arg: #$index - $s") + } run found@{ for (currentLoopNo in 0..1) { //currently we have only 2 loops + devLog.info("loop #" + currentLoopNo) if (args.size > 1) { // manual mode - targetFile = if (File(args[1]).isFile) { + devLog.info("manual mode") + targetFile = if (File(args[1]).isFile) { //unpack File(args[1]).canonicalPath - } else if (File(args[1] + "/role").isFile) { - File(args[1] + "/role").readText().trim() - } else { - log.error("Not sure of what to do: " + args.asList().toString()) + } else if (File(args[1]).isDirectory and File(args[1], "workspace.ini").isFile) { //pack + loadProperties(File(args[1], "workspace.ini").canonicalPath).getProperty("role") + } else { //wrong + log.error("Not a file: " + args[1]) exitProcess(1) } - log.warn("manual mode: args= ${args[1]}, $targetFile") + devLog.warn("manual mode: file=$targetFile") + targetDir = when (args.size) { + 2 -> if (File(args[1]).isDirectory and File(args[1], "workspace.ini").isFile) { //arg = outDir + File(args[1]).canonicalPath + } else { + Helper.prop("workDir") // arg = outDir + } + 3 -> File(args[2]).canonicalPath // arg = file + else -> { + throw IllegalArgumentException("too many args") + } + } + devLog.warn("manual mode: file=$targetFile, dir=$targetDir") packablePool .filter { it.value.createInstance().loopNo == currentLoopNo } .forEach { p -> @@ -75,14 +101,15 @@ fun main(args: Array) { } } } else { // lazy mode + devLog.info("lazy mode (in current dir)") File(".").listFiles()!!.forEach { file -> packablePool .filter { it.value.createInstance().loopNo == currentLoopNo } .forEach { p -> for (item in p.key) { if (Pattern.compile(item).matcher(file.name).matches()) { - log.debug("Found: " + file.name + ", " + item) - targetFile = file.name + log.info("Found: " + file.name + ", " + item) + targetFile = File(file.name).canonicalPath targetHandler = p.value return@found } @@ -140,43 +167,26 @@ fun main(args: Array) { log.warn("'${args[0]}' sequence initialized") log.warn("XXXX: args.size: ${args.size}") - val convertedArgs = args.copyOf().apply { set(0, targetFile!!) } - functions[0].call(it.createInstance(), *convertedArgs) - -/* - val reflectRet = when (functions[0].parameters.size) { - 1 -> { - log.warn("1: call null") - functions[0].call(it.createInstance()) - } - - 2 -> { - log.warn("2: call $targetFile") - functions[0].call(it.createInstance(), targetFile!!) - } - - 3 -> { - if (args.size != 2) { - log.info("invoke: ${it.qualifiedName}, $targetFile, " + targetFile!!.removeSuffix(".img")) - functions[0].call(it.createInstance(), targetFile!!, targetFile!!.removeSuffix(".img")) - } else { - log.info("invoke: ${it.qualifiedName}, $targetFile, " + args[1]) - functions[0].call(it.createInstance(), targetFile!!, args[1]) - } - } - - else -> { - functions[0].parameters.forEach { kp -> - println("Param: " + kp.index + " " + kp.type + " " + kp.name) - } - log.error("I am confused by so many parameters") - exitProcess(4) + val c = (if (args.size == 1) { //lazy mode + args.drop(1).toMutableList().apply { + add(targetFile!!) + //add(System.getProperty("user.dir")) } + } else { + args.drop(1) + }).toTypedArray() + val convertedArgs = c + println("clazz = " + it.simpleName) + println("func = " + functions[0]) + println("orig args:") + args.forEachIndexed { index, s -> + println("$index: $s") } - if (functions[0].returnType.toString() != Unit.toString()) { - log.info("ret: $reflectRet") + println("Converted args:") + convertedArgs.forEachIndexed { index, s -> + println("$index: $s") } -*/ + functions[0].call(it.createInstance(), *convertedArgs) log.warn("'${args[0]}' sequence completed") } } diff --git a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt index 0f8059c..76a75b9 100644 --- a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt +++ b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt @@ -16,6 +16,7 @@ package cfig.packable import avb.AVBInfo import cfig.Avb +import cfig.bootimg.Common import cfig.helper.Dumpling import cfig.helper.Helper import com.fasterxml.jackson.databind.ObjectMapper @@ -35,20 +36,42 @@ class VBMetaParser : IPackable { return listOf("^vbmeta\\.img$", "^vbmeta\\_[a-z]+.img$") } + // lazy mode override fun unpack(fileName: String) { - File(Helper.prop("workDir")).let { + unpackInternal(fileName, Helper.prop("workDir")!!) + } + + // manual mode + fun unpackInternal(inFileName: String, unpackDir: String) { + //common + log.info("unpackInternal(fileName: $inFileName, unpackDir: $unpackDir)") + val fileName = File(inFileName).canonicalPath + Helper.setProp("workDir", File(unpackDir).canonicalPath) + //prepare workdir + File(Helper.prop("workDir")!!).let { if (!it.exists()) { it.mkdirs() } } + log.info("workspace set to $unpackDir") + Common.createWorkspaceIni(fileName) + val ai = AVBInfo.parseFrom(Dumpling(fileName)).dumpDefault(fileName) log.info("Signing Key: " + Avb.inspectKey(ai)) } override fun pack(fileName: String) { - val blob = ObjectMapper().readValue(File(Avb.getJsonFileName(fileName)), AVBInfo::class.java).encodePadded() - log.info("Writing padded vbmeta to file: $fileName.signed") - Files.write(Paths.get("$fileName.signed"), blob, StandardOpenOption.CREATE) + packInternal(outDir, fileName) + } + + // called via reflection + fun packInternal(workspace: String, outFileName: String) { + log.info("packInternal(workspace: $workspace)") + Helper.setProp("workDir", workspace) + val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("role") + val blob = ObjectMapper().readValue(File(Avb.getJsonFileName(iniRole)), AVBInfo::class.java).encodePadded() + log.info("Writing padded vbmeta to file: $outFileName.signed") + Files.write(Paths.get("$outFileName.signed"), blob, StandardOpenOption.CREATE) } override fun flash(fileName: String, deviceName: String) { diff --git a/bbootimg/src/main/kotlin/packable/VendorBootParser.kt b/bbootimg/src/main/kotlin/packable/VendorBootParser.kt index 6b70601..30b37e0 100644 --- a/bbootimg/src/main/kotlin/packable/VendorBootParser.kt +++ b/bbootimg/src/main/kotlin/packable/VendorBootParser.kt @@ -14,6 +14,7 @@ package cfig.packable +import cfig.bootimg.Common import cfig.bootimg.v3.VendorBoot import cfig.helper.Helper import com.fasterxml.jackson.databind.ObjectMapper @@ -30,19 +31,17 @@ class VendorBootParser : IPackable { } override fun unpack(fileName: String) { - clear() - val vb = VendorBoot - .parse(fileName) - .extractImages() - .extractVBMeta() - .printUnpackSummary() - log.debug(vb.toString()) + log.info("unpack(fileName: $fileName)") + unpackInternal(fileName, Helper.prop("workDir")!!) } - fun unpackInternal(targetFile: String, fileName: String, unpackDir: String) { + fun unpackInternal(fileName: String, unpackDir: String) { log.info("unpackInternal(fileName: $fileName, unpackDir: $unpackDir)") Helper.setProp("workDir", unpackDir) clear() + //create workspace file + Common.createWorkspaceIni(fileName) + //create workspace file done val vb = VendorBoot .parse(fileName) .extractImages() @@ -52,13 +51,32 @@ class VendorBootParser : IPackable { } override fun pack(fileName: String) { - val cfgFile = "$outDir/${fileName.removeSuffix(".img")}.json" + log.info("packInternal($fileName)") + packInternal(Helper.prop("workDir")!!, fileName) + } + + fun packInternal(workspace: String, outFileName: String) { + log.info("packInternal($workspace, $outFileName)") + Helper.setProp("workDir", workspace) + //intermediate + Helper.joinPath(workspace, "intermediate").also { intermediateDir -> + File(intermediateDir).let { + if (!it.exists()) { + it.mkdir() + } + } + Helper.setProp("intermediateDir", intermediateDir) + } + //workspace+cfg + val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("role") + val cfgFile = File(workspace, iniRole.removeSuffix(".img") + ".json").canonicalPath log.info("Loading config from $cfgFile") ObjectMapper().readValue(File(cfgFile), VendorBoot::class.java) .pack() .sign() + .postCopy(outFileName) .updateVbmeta() - .printPackSummary() + .printPackSummary(outFileName) } override fun `@verify`(fileName: String) { diff --git a/doc/feature_list.md b/doc/feature_list.md index 70fd6b9..b20a005 100644 --- a/doc/feature_list.md +++ b/doc/feature_list.md @@ -21,16 +21,16 @@ should be compatible with "/usr/bin/env sh" ## TODO: command line usage unpack ``` -abe unpack boot.img out +be unpack file +be unpack file dir ``` - pack ``` -abe pack out boot.img +be pack dir ``` properties: "out.file": the final output file -### something interesting in abe +### something interesting in be a zsh script, parse the input command line parameters, for example, if args are: "unpack file dir", the shell script will print 'gradle unpack --args="unpackInternal file dir"'; if args are "pack dir file", the shell script will print 'gradle pack --args="packInternal dir file"'. diff --git a/helper/build.gradle.kts b/helper/build.gradle.kts index 3a36d09..ae2c7f8 100644 --- a/helper/build.gradle.kts +++ b/helper/build.gradle.kts @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "2.1.0" + kotlin("jvm") version "2.1.20" `java-library` application } @@ -58,11 +58,13 @@ java { targetCompatibility = JavaVersion.VERSION_11 } -tasks.withType().all { - kotlinOptions { - freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" - freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes" - jvmTarget = "11" +tasks.withType().configureEach { + compilerOptions { + freeCompilerArgs.addAll( + "-opt-in=kotlin.RequiresOptIn", + "-opt-in=kotlin.ExperimentalUnsignedTypes" + ) + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) } } diff --git a/lazybox/build.gradle.kts b/lazybox/build.gradle.kts index 0fa67cd..8bf5937 100644 --- a/lazybox/build.gradle.kts +++ b/lazybox/build.gradle.kts @@ -8,7 +8,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "2.1.0" + kotlin("jvm") version "2.1.20" application } @@ -30,6 +30,7 @@ dependencies { // Use the JUnit 5 integration. testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.3") testRuntimeOnly("org.junit.platform:junit-platform-launcher") + implementation(kotlin("stdlib")) } java { @@ -37,11 +38,13 @@ java { targetCompatibility = JavaVersion.VERSION_11 } -tasks.withType().all { - kotlinOptions { - freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" - freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes" - jvmTarget = "11" +tasks.withType().configureEach { + compilerOptions { + freeCompilerArgs.addAll( + "-opt-in=kotlin.RequiresOptIn", + "-opt-in=kotlin.ExperimentalUnsignedTypes" + ) + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index ee1e2d1..3486129 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,8 @@ +pluginManagement { + plugins { + kotlin("jvm") version "2.1.20" + } +} rootProject.name = "boot" include("bbootimg") include("aosp:apksigner") diff --git a/tools/factory_image_parser.py b/tools/factory_image_parser.py index 9e89200..a068c65 100755 --- a/tools/factory_image_parser.py +++ b/tools/factory_image_parser.py @@ -73,6 +73,12 @@ unknown_list = [ "super_empty_all.img", #ADT-3 "bootloader.img", #many "dt.img", + "fastboot.img", #anon + "fastlogo.img", #anon + "bl.img", #anon + "tzl.img", #anon + "preboot.img", #anon + "tzk.img", #anon ] tmp2 = "tmp2" diff --git a/tools/local/bin/abe b/tools/local/bin/be similarity index 63% rename from tools/local/bin/abe rename to tools/local/bin/be index afc8f53..ee81660 100755 --- a/tools/local/bin/abe +++ b/tools/local/bin/be @@ -2,10 +2,12 @@ baseDir=${0:a:h} export baseDir +be_caller_dir=${PWD} +export be_caller_dir set -e # Parse command line arguments -if [[ $# -eq 0 ]]; then +if [[ $# -lt 2 ]]; then echo "Usage: $0 [ ]" exit 1 fi @@ -13,40 +15,48 @@ fi operation=$1 echo arg num: $# echo "args: $0 $1 $2" +echo "baseDir: $baseDir" +echo "callerDir: $be_caller_dir" source_code_dir=/home/yu/work/boot # Determine which operation to perform set -x case $operation in "unpack") - cd ${source_code_dir} - pwd if [[ $# -eq 2 ]]; then file=`realpath $2` dir=`realpath out` + cd ${source_code_dir} + pwd gradle unpack --args="unpackInternal $file $dir" elif [[ $# -eq 3 ]]; then file=`realpath $2` dir=`realpath $3` - cd ${baseDir}/../../../ + cd ${source_code_dir} gradle unpack --args="unpackInternal $file $dir" else + echo "Invalid args" cd ${baseDir}/../../../ gradle unpack fi ;; "pack") pwd - if [[ $# -eq 3 ]]; then + if [[ $# -eq 2 ]]; then + dir=`realpath $2` + cd ${source_code_dir} + file=${be_caller_dir}/$(grep -m1 '^role=' $dir/workspace.ini | cut -d'=' -f2-) + gradle pack --args="packInternal $dir $file" + elif [[ $# -eq 3 ]]; then dir=`realpath $2` file=`realpath $3` - #cd ${baseDir}/../../../ cd ${source_code_dir} gradle pack --args="packInternal $dir $file" else - #cd ${baseDir}/../../../ - cd ${source_code_dir} - gradle pack + ##cd ${baseDir}/../../../ + #cd ${source_code_dir} + #gradle pack + echo "Invalid args" fi ;; *)