From 4e9d60db1a176eb60c83604ff5feb9a80df62a16 Mon Sep 17 00:00:00 2001 From: cfig Date: Wed, 16 Dec 2020 00:12:53 +0800 Subject: [PATCH] cpio: replace mkbootfs with java CPIO reimplement android mkbootfs with pure java, features include: unpack: - using commons.compress - save cpio entry info on unpacking, and reload it on packing, this is called 'exact-matching' pack: using new code with the help of commons.compress - for newly added ramdisk file, its file mode falls back to 'pattern-matching' - for newly added ramdisk file that doesn't match any pattern, it will use default file mode, 'default' integration test: - enable strict cpio checking --- bbootimg/build.gradle.kts | 3 +- bbootimg/gradle.properties | 1 + bbootimg/src/main/kotlin/bootimg/Common.kt | 62 +++-- .../main/kotlin/bootimg/cpio/AndroidCpio.kt | 251 ++++++++++++++++++ .../kotlin/bootimg/cpio/AndroidCpioEntry.kt | 129 +++++++++ .../main/kotlin/bootimg/cpio/NewAsciiCpio.kt | 106 ++++++++ bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt | 5 +- bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt | 5 +- .../src/main/kotlin/bootimg/v3/VendorBoot.kt | 5 +- .../bootloader_message/BootloaderMsg.kt | 2 +- bbootimg/src/main/kotlin/helper/ZipHelper.kt | 76 ++---- bbootimg/src/main/resources/fsconfig.txt | 99 +++++++ bbootimg/src/test/kotlin/avb/FooterTest.kt | 4 +- .../kotlin/bootimg/AndroidCpioEntryTest.kt | 60 +++++ .../bootloader_message/BootloaderMsgTest.kt | 8 + build.gradle.kts | 11 +- doc/additional_tricks.md | 2 + src/integrationTest/resources | 2 +- 18 files changed, 747 insertions(+), 84 deletions(-) create mode 100644 bbootimg/gradle.properties create mode 100644 bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt create mode 100644 bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpioEntry.kt create mode 100644 bbootimg/src/main/kotlin/bootimg/cpio/NewAsciiCpio.kt create mode 100644 bbootimg/src/main/resources/fsconfig.txt create mode 100644 bbootimg/src/test/kotlin/bootimg/AndroidCpioEntryTest.kt diff --git a/bbootimg/build.gradle.kts b/bbootimg/build.gradle.kts index 6a76014..893176a 100644 --- a/bbootimg/build.gradle.kts +++ b/bbootimg/build.gradle.kts @@ -1,8 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.4.20" - kotlin("plugin.serialization") version "1.4.20" + kotlin("jvm") version "1.4.21" application } diff --git a/bbootimg/gradle.properties b/bbootimg/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/bbootimg/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/bbootimg/src/main/kotlin/bootimg/Common.kt b/bbootimg/src/main/kotlin/bootimg/Common.kt index ef1aac1..5b42d50 100644 --- a/bbootimg/src/main/kotlin/bootimg/Common.kt +++ b/bbootimg/src/main/kotlin/bootimg/Common.kt @@ -1,6 +1,7 @@ package cfig.bootimg import cfig.EnvironmentVerifier +import cfig.bootimg.cpio.AndroidCpio import cfig.dtb_util.DTC import cfig.helper.Helper import cfig.helper.ZipHelper @@ -24,17 +25,18 @@ import java.util.regex.Pattern @OptIn(ExperimentalUnsignedTypes::class) class Common { data class VeritySignature( - var type: String = "dm-verity", - var path: String = "/boot", - var verity_pk8: String = "", - var verity_pem: String = "", - var jarPath: String = "") + var type: String = "dm-verity", + var path: String = "/boot", + var verity_pk8: String = "", + var verity_pem: String = "", + var jarPath: String = "" + ) data class Slice( - var srcFile: String, - var offset: Int, - var length: Int, - var dumpFile: String + var srcFile: String, + var offset: Int, + var length: Int, + var dumpFile: String ) companion object { @@ -155,9 +157,11 @@ class Common { val md = MessageDigest.getInstance("SHA1") for (item in inFiles) { if (null == item) { - md.update(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN) + md.update( + ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN) .putInt(0) - .array()) + .array() + ) log.debug("update null $item: " + Helper.toHexString((md.clone() as MessageDigest).digest())) } else { val currentFile = File(item) @@ -172,9 +176,11 @@ class Common { md.update(dataRead, 0, byteRead) } log.debug("update file $item: " + Helper.toHexString((md.clone() as MessageDigest).digest())) - md.update(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN) + md.update( + ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN) .putInt(currentFile.length().toInt()) - .array()) + .array() + ) log.debug("update SIZE $item: " + Helper.toHexString((md.clone() as MessageDigest).digest())) } } @@ -195,6 +201,7 @@ class Common { } } + //using mkbootfs fun packRootfs(rootDir: String, ramdiskGz: String, osMajor: Int = 10) { val mkbootfs = String.format(Helper.prop("mkbootfsBin"), osMajor) log.info("Packing rootfs $rootDir ...") @@ -219,6 +226,28 @@ class Common { log.info("$ramdiskGz is ready") } + //using preset fs_config + fun packRootfs(rootDir: String, ramdiskGz: String) { + log.info("Packing rootfs $rootDir ...") + val fsConfig = File(ramdiskGz).parentFile.path + "/ramdisk_filelist.txt" + when { + ramdiskGz.endsWith(".gz") -> { + val f = ramdiskGz.removeSuffix(".gz") + AndroidCpio().pack(rootDir, f, fsConfig) + ZipHelper.gnuZipFile2(ramdiskGz, FileInputStream(f)) + } + ramdiskGz.endsWith(".lz4") -> { + val f = ramdiskGz.removeSuffix(".lz4") + AndroidCpio().pack(rootDir, f, fsConfig) + ZipHelper.compressLZ4(ramdiskGz, FileInputStream(f)) + } + else -> { + throw IllegalArgumentException("$ramdiskGz is not supported") + } + } + log.info("$ramdiskGz is ready") + } + fun padFile(inBF: ByteBuffer, padding: Int) { val pad = padding - (inBF.position() and padding - 1) and padding - 1 inBF.put(ByteArray(pad)) @@ -264,8 +293,11 @@ class Common { mkdirs() } - //("cpio -idmv -F " + File(ramdisk).canonicalPath).check_call(rootFile.canonicalPath) - ZipHelper.decompressCPIO(File(ramdisk).canonicalPath, rootFile.canonicalPath, File(ramdisk).canonicalPath + ".filelist") + AndroidCpio.decompressCPIO( + File(ramdisk).canonicalPath, + rootFile.canonicalPath, + File(ramdisk).canonicalPath.removeSuffix(".img") + "_filelist.txt" + ) log.info(" ramdisk extracted : $ramdisk -> $rootFile") } diff --git a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt new file mode 100644 index 0000000..a872a6d --- /dev/null +++ b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt @@ -0,0 +1,251 @@ +package cfig.bootimg.cpio + +import cfig.helper.Helper +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.ObjectMapper +import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream +import org.apache.commons.compress.archivers.cpio.CpioConstants +import org.slf4j.LoggerFactory +import java.io.* +import java.nio.file.Files +import java.nio.file.Paths +import java.util.regex.Pattern + +class AndroidCpio { + private var inodeNumber: Long = 0L + private val fsConfig: MutableSet = mutableSetOf() + private val fsConfigTemplate: List = loadFsConfigTemplate() + + data class FsConfigTemplate( + var type: String = "REG", + var mode: String = "", + var uid: Int = 0, + var gid: Int = 0, + var prefix: String = "" + ) + + private fun packItem(root: File, item: File, outStream: OutputStream) { + if ((item.path == root.path) && !Files.isDirectory(item.toPath())) { + throw IllegalArgumentException("root path is not dir") + } + + if (item.path == root.path) { //first visit to root + val fileList = item.listFiles()?.apply { sortBy { it.name } } + if (fileList != null) { + for (subItem in fileList) { + packItem(root, subItem, outStream) + } + } + } else { //later visit + log.debug(item.path + " ~ " + root.path) + val newOutname = item.path.substring(root.path.length + 1) //remove leading slash + val entry = when { + Files.isSymbolicLink(item.toPath()) -> { + val target = Files.readSymbolicLink(Paths.get(item.path)) + log.debug("LNK: " + item.path + " --> " + target) + AndroidCpioEntry( + name = newOutname, + statMode = java.lang.Long.valueOf("120644", 8), + data = target.toString().toByteArray(), + ino = inodeNumber++ + ) + } + Files.isDirectory(item.toPath()) -> { + log.debug("DIR: " + item.path + ", " + item.toPath()) + AndroidCpioEntry( + name = newOutname, + statMode = java.lang.Long.valueOf("40755", 8), + data = byteArrayOf(), + ino = inodeNumber++ + ) + } + Files.isRegularFile(item.toPath()) -> { + log.debug("REG: " + item.path) + AndroidCpioEntry( + name = newOutname, + statMode = java.lang.Long.valueOf("100644", 8), + data = item.readBytes(), + ino = inodeNumber++ + ) + } + else -> { + throw IllegalArgumentException("do not support file " + item.name) + } + } + log.debug("_eject: " + item.path) + //fix_stat + fixStat(entry) + outStream.write(entry.encode()) + if (Files.isDirectory(item.toPath()) && !Files.isSymbolicLink(item.toPath())) { + val fileList = item.listFiles()?.apply { sortBy { it.name } } + if (fileList != null) { + for (subItem in fileList) { + packItem(root, subItem, outStream) + } + } + } + } + } + + private fun fnmatch(fileName: String, pattern: String): Boolean { + return if (fileName == pattern) { + true + } else { + Pattern.compile( + "^" + pattern.replace(".", "\\.") + .replace("/", "\\/") + .replace("*", ".*") + "$" + ).matcher(fileName).find() + } + } + + private fun fixStat(entry: AndroidCpioEntry) { + val itemConfig = fsConfig.filter { it.name == entry.name } + when (itemConfig.size) { + 0 -> { /* do nothing */ + val matches = fsConfigTemplate + .filter { fnmatch(entry.name, it.prefix) } + .sortedByDescending { it.prefix.length } + if (matches.isNotEmpty()) { + val ftBits = NewAsciiCpio(c_mode = entry.statMode).let { + when { + it.isSymbolicLink() -> CpioConstants.C_ISLNK + it.isRegularFile() -> CpioConstants.C_ISREG + it.isDirectory() -> CpioConstants.C_ISDIR + else -> throw IllegalArgumentException("unsupported st_mode " + it.c_mode) + } + } + entry.statMode = ftBits.toLong() or java.lang.Long.valueOf(matches[0].mode, 8) + log.debug("${entry.name} ~ " + matches.map { it.prefix }.reduce { acc, s -> "$acc, $s" } + + ", stMode=" + java.lang.Long.toOctalString(entry.statMode)) + } else { + log.debug("${entry.name} has NO fsconfig/prefix match") + } + } + 1 -> { + log.debug("${entry.name} == preset fsconfig") + entry.statMode = itemConfig[0].statMode + } + else -> { + throw IllegalArgumentException("${entry.name} as multiple exact-match fsConfig") + } + } + } + + fun pack(inDir: String, outFile: String, propFile: String? = null) { + inodeNumber = 300000L + fsConfig.clear() + propFile?.let { + if (File(propFile).exists()) { + File(propFile).readLines().forEach { line -> + fsConfig.add(ObjectMapper().readValue(line, AndroidCpioEntry::class.java)) + } + } else { + log.warn("fsConfig file has been deleted, using fsConfig prefix matcher") + } + } + FileOutputStream(outFile).use { fos -> + packItem(File(inDir), File(inDir), fos) + val trailer = AndroidCpioEntry( + name = "TRAILER!!!", + statMode = java.lang.Long.valueOf("0755", 8), + ino = inodeNumber++ + ) + fixStat(trailer) + fos.write(trailer.encode()) + } + val len = File(outFile).length() + val rounded = Helper.round_to_multiple(len, 256) //file in page 256 + if (len != rounded) { + FileOutputStream(outFile, true).use { fos -> + fos.write(ByteArray((rounded - len).toInt())) + } + } + } + + private fun loadFsConfigTemplate(): List { + val reader = + BufferedReader(InputStreamReader(AndroidCpio::class.java.classLoader.getResourceAsStream("fsconfig.txt")!!)) + val oM = ObjectMapper().apply { + configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) + } + return reader.readLines().map { oM.readValue(it, FsConfigTemplate::class.java) } + } + + companion object { + private val log = LoggerFactory.getLogger(AndroidCpio::class.java) + 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 + + while (true) { + val entry = cis.nextCPIOEntry ?: break + val entryInfo = AndroidCpioEntry( + name = entry.name, + statMode = entry.mode, + ino = entry.inode, + note = String.format("%6s", java.lang.Long.toOctalString(entry.mode)) + ) + 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.isSymbolicLink -> { + entryInfo.note = ("LNK " + entryInfo.note) + Files.createSymbolicLink(Paths.get(outEntryName), Paths.get(String(buffer))) + } + entry.isRegularFile -> { + entryInfo.note = ("REG " + entryInfo.note) + File(outEntryName).writeBytes(buffer) + Files.setPosixFilePermissions( + Paths.get(outEntryName), + Helper.modeToPermissions((entry.mode and 0xfff).toInt()) + ) + } + entry.isDirectory -> { + entryInfo.note = ("DIR " + entryInfo.note) + 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()) + fileListDump?.write(ObjectMapper().writeValueAsString(entryInfo).toByteArray()) + fileListDump?.write("\n".toByteArray()) + } + val bytesRead = cis.bytesRead + cis.close() + val fis = FileInputStream(cpioFile) + fis.skip(bytesRead - 128) + val remaining = fis.readAllBytes() + val foundIndex = String(remaining).lastIndexOf("070701") + val entryInfo = AndroidCpioEntry( + name = CpioConstants.CPIO_TRAILER, + statMode = java.lang.Long.valueOf("755", 8) + ) + if (foundIndex != -1) { + entryInfo.statMode = + java.lang.Long.valueOf(String(remaining).substring(foundIndex + 14, foundIndex + 22), 16) + log.info("cpio trailer found, mode=" + String(remaining).substring(foundIndex + 14, foundIndex + 22)) + } else { + log.error("no cpio trailer found") + } + fileListDump?.write(ObjectMapper().writeValueAsString(entryInfo).toByteArray()) + fileListDump?.close() + } + } +} diff --git a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpioEntry.kt b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpioEntry.kt new file mode 100644 index 0000000..8455d73 --- /dev/null +++ b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpioEntry.kt @@ -0,0 +1,129 @@ +package cfig.bootimg.cpio + +import cfig.helper.Helper +import com.fasterxml.jackson.annotation.JsonIgnore +import org.apache.commons.compress.archivers.cpio.CpioArchiveEntry +import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream +import org.apache.commons.compress.archivers.cpio.CpioConstants +import org.slf4j.LoggerFactory +import java.io.ByteArrayOutputStream + +class AndroidCpioEntry( + var name: String = "", + var statMode: Long = 0,// stat.st_mode + @JsonIgnore + val data: ByteArray = byteArrayOf(), + var ino: Long = 0, + var note: String = "" +) { + fun encode(): ByteArray { + //new ascii cpio + var header = Helper.join( + NewAsciiCpio( + c_ino = ino, + c_mode = statMode, + c_filesize = data.size, + c_namesize = name.length + 1 + ).encode(), + name.toByteArray(), + ByteArray(1) + ) //NULL terminated c-string + //padding header if necessary + Helper.round_to_multiple(header.size, 4).let { roundedSize -> + if (roundedSize != header.size) { + log.debug("header: meta ${header.size - 1 - name.length}, name ${name.length}, null 1, pad ${roundedSize - header.size} -> $roundedSize") + header = Helper.join(header, ByteArray(roundedSize - header.size)) + } + } + var payload = data + //padding data if necessary + Helper.round_to_multiple(payload.size, 4).let { roundedSize -> + if (roundedSize != payload.size) { + log.debug("data : payload ${payload.size}, pad ${roundedSize - payload.size} -> $roundedSize") + payload = Helper.join(payload, ByteArray(roundedSize - payload.size)) + } + } + log.debug("entry($name): header ${header.size} + data ${payload.size} = ${header.size + payload.size}") + return Helper.join(header, payload) + } + + fun encode2(): ByteArray { + val baos = ByteArrayOutputStream() + val cpio = CpioArchiveOutputStream(baos) + val entry = CpioArchiveEntry(CpioConstants.FORMAT_NEW, name).apply { + inode = ino + uid = 0 + gid = 0 + mode = statMode + numberOfLinks = 1 + time = 0 + size = data.size.toLong() + deviceMaj = 0 + deviceMin = 0 + remoteDeviceMaj = 0 + remoteDeviceMin = 0 + chksum = 0 + } + cpio.putArchiveEntry(entry) + cpio.write(data) + cpio.closeArchiveEntry() + return baos.toByteArray() + } + + data class FileMode( + var type: String = "", + var sbits: String = "", //suid, sgid, sticky + var perm: String = "" + ) + + companion object { + private val log = LoggerFactory.getLogger(AndroidCpioEntry::class.java) + private val S_IFDIR = java.lang.Long.valueOf("040000", 8) + private val S_IFCHR = java.lang.Long.valueOf("020000", 8) + private val S_IFBLK = java.lang.Long.valueOf("060000", 8) + private val S_IFREG = java.lang.Long.valueOf("100000", 8) + private val S_IFIFO = java.lang.Long.valueOf("010000", 8) + private val S_IFLNK = java.lang.Long.valueOf("120000", 8) + private val S_IFSOCK = java.lang.Long.valueOf("140000", 8) + + private val S_ISUID = java.lang.Long.valueOf("4000", 8) + private val S_ISGID = java.lang.Long.valueOf("2000", 8) + private val S_ISVTX = java.lang.Long.valueOf("1000", 8) + + private val S_IREAD = java.lang.Long.valueOf("400", 8) + private val S_IWRITE = java.lang.Long.valueOf("200", 8) + private val S_IEXEC = java.lang.Long.valueOf("100", 8) + + private val MASK_S_IRWXU = S_IREAD or S_IWRITE or S_IEXEC + private val MASK_S_IRWXG = MASK_S_IRWXU shr 3 + private val MASK_S_IRWXO = MASK_S_IRWXG shr 3 + private val MASK_ACCESSPERMS = MASK_S_IRWXU or MASK_S_IRWXG or MASK_S_IRWXO + + fun interpretMode(mode: Long): FileMode { + return FileMode().let { fm -> + val m = mode and java.lang.Long.valueOf("0170000", 8) // S_IFMT + fm.type = when (m) { + S_IFREG -> "REG" + S_IFCHR -> "CHR" + S_IFBLK -> "BLK" + S_IFDIR -> "DIR" + S_IFIFO -> "FIFO" + S_IFLNK -> "LNK" + S_IFSOCK -> "SOCK" + else -> throw IllegalArgumentException("unknown file type " + java.lang.Long.toOctalString(m)) + } + if ((mode and S_ISUID) != 0L) { + fm.sbits += if (fm.sbits.isEmpty()) "suid" else "|suid" + } + if ((mode and S_ISGID) != 0L) { + fm.sbits += if (fm.sbits.isEmpty()) "sgid" else "|sgid" + } + if ((mode and S_ISVTX) != 0L) { + fm.sbits += if (fm.sbits.isEmpty()) "sticky" else "|sticky" + } + fm.perm = java.lang.Long.toOctalString(mode and MASK_ACCESSPERMS) + fm + } + } + } +} \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/bootimg/cpio/NewAsciiCpio.kt b/bbootimg/src/main/kotlin/bootimg/cpio/NewAsciiCpio.kt new file mode 100644 index 0000000..20f8658 --- /dev/null +++ b/bbootimg/src/main/kotlin/bootimg/cpio/NewAsciiCpio.kt @@ -0,0 +1,106 @@ +package cfig.bootimg.cpio + +import cfig.io.Struct3 +import org.apache.commons.compress.archivers.cpio.CpioConstants + +/* + cpio "New ASCII Format" with 070701 as magic + */ +class NewAsciiCpio( + var c_magic: String = "070701", //start-of-header + var c_ino: Long = 0,//var + var c_mode: Long = 0,//var + var c_uid: Int = 0, + var c_gid: Int = 0, + var c_nlink: Int = 1, + var c_mtime: Long = 0, + var c_filesize: Int = 0,//var + var c_devmajor: Int = 0, + var c_devminor: Int = 0, + var c_rdevmajor: Int = 0, + var c_rdevminor: Int = 0, //end-of-header + var c_namesize: Int = 0, //c_string name with '\0', aka. name_len + 1 + var c_check: Int = 0 +) { + init { + if (SIZE != Struct3(FORMAT_STRING).calcSize()) { + throw RuntimeException() + } + } + + fun encode(): ByteArray { + return Struct3(FORMAT_STRING).pack( + String.format("%s", c_magic), + String.format("%08x", c_ino), + String.format("%08x", c_mode), + String.format("%08x", c_uid), + String.format("%08x", c_gid), + String.format("%08x", c_nlink), + String.format("%08x", c_mtime), + String.format("%08x", c_filesize), + String.format("%08x", c_devmajor), + String.format("%08x", c_devminor), + String.format("%08x", c_rdevmajor), + String.format("%08x", c_rdevminor), + String.format("%08x", c_namesize), + String.format("%08x", c_check), + ) + } + + private fun fileType(): Long { + return c_mode and CpioConstants.S_IFMT.toLong() + } + + fun isRegularFile(): Boolean { + return fileType() == CpioConstants.C_ISREG.toLong() + } + + fun isDirectory(): Boolean { + return fileType() == CpioConstants.C_ISDIR.toLong() + } + + fun isSymbolicLink(): Boolean { + return fileType() == CpioConstants.C_ISLNK.toLong() + } + + companion object { + const val SIZE = 110 + const val FORMAT_STRING = "6s8s8s8s8s8s8s8s8s8s8s8s8s8s" //6 + 8 *13 + } +} + +/* + /* File types. */ +#define __S_IFDIR 0040000 /* Directory. */ +#define __S_IFCHR 0020000 /* Character device. */ +#define __S_IFBLK 0060000 /* Block device. */ +#define __S_IFREG 0100000 /* Regular file. */ +#define __S_IFIFO 0010000 /* FIFO. */ +#define __S_IFLNK 0120000 /* Symbolic link. */ +#define __S_IFSOCK 0140000 /* Socket. */ + + /* Protection bits. */ +#define __S_ISUID 04000 /* Set user ID on execution. */ +#define __S_ISGID 02000 /* Set group ID on execution. */ +#define __S_ISVTX 01000 /* Save swapped text after use (sticky). */ +#define __S_IREAD 0400 /* Read by owner. */ +#define __S_IWRITE 0200 /* Write by owner. */ +#define __S_IEXEC 0100 /* Execute by owner. */ +/* Read, write, and execute by owner. */ +#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC) + +#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */ +#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */ +#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */ +Read, write, and execute by group. +#define S_IRWXG (S_IRWXU >> 3) + +#define S_IROTH (S_IRGRP >> 3) /* Read by others. */ +#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */ +#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */ +Read, write, and execute by others. +#define S_IRWXO (S_IRWXG >> 3) + +# define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) 0777 + + */ diff --git a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt index 6221637..46f9059 100644 --- a/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt +++ b/bbootimg/src/main/kotlin/bootimg/v2/BootV2.kt @@ -1,11 +1,11 @@ package cfig.bootimg.v2 import cfig.Avb -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.helper.Helper import cfig.packable.VBMetaParser import com.fasterxml.jackson.databind.ObjectMapper import de.vandermeer.asciitable.AsciiTable @@ -323,7 +323,8 @@ data class BootV2( } 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("${workDir}/root", this.ramdisk.file!!, Common.parseOsMajor(info.osVersion.toString())) + Common.packRootfs("${workDir}/root", this.ramdisk.file!!) } this.ramdisk.size = File(this.ramdisk.file!!).length().toInt() } diff --git a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt index 81cf2ff..d5698e3 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/BootV3.kt @@ -79,7 +79,10 @@ data class BootV3(var info: MiscInfo = MiscInfo(), } else { File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file.replaceFirst("[.][^.]+$", "")).deleleIfExists() - C.packRootfs("$workDir/root", this.ramdisk.file, C.parseOsMajor(info.osVersion)) + //TODO: remove cpio in C/C++ + //C.packRootfs("$workDir/root", this.ramdisk.file, C.parseOsMajor(info.osVersion)) + // enable advance JAVA cpio + C.packRootfs("$workDir/root", this.ramdisk.file) } this.kernel.size = File(this.kernel.file).length().toInt() this.ramdisk.size = File(this.ramdisk.file).length().toInt() diff --git a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt index bc60d67..ab39fed 100644 --- a/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt +++ b/bbootimg/src/main/kotlin/bootimg/v3/VendorBoot.kt @@ -87,7 +87,10 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(), } else { File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file.removeSuffix(".gz")).deleleIfExists() - C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor()) + //TODO: remove cpio in C/C++ + //C.packRootfs("$workDir/root", this.ramdisk.file, parseOsMajor()) + //enable advance JAVA cpio + C.packRootfs("$workDir/root", this.ramdisk.file) } this.ramdisk.size = File(this.ramdisk.file).length().toInt() this.dtb.size = File(this.dtb.file).length().toInt() diff --git a/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt b/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt index 0d65dd8..f1ed734 100644 --- a/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt +++ b/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt @@ -17,7 +17,7 @@ data class BootloaderMsg(//offset 0, size 2k ) { companion object { private const val FORMAT_STRING = "32s32s768s32s1184b" - private const val miscFile = "misc.file" + const val miscFile = "misc.file" const val SIZE = 2048 private val log = LoggerFactory.getLogger("BootloaderMsg") diff --git a/bbootimg/src/main/kotlin/helper/ZipHelper.kt b/bbootimg/src/main/kotlin/helper/ZipHelper.kt index 127e137..cbac56b 100644 --- a/bbootimg/src/main/kotlin/helper/ZipHelper.kt +++ b/bbootimg/src/main/kotlin/helper/ZipHelper.kt @@ -1,9 +1,12 @@ package cfig.helper +import cfig.bootimg.cpio.AndroidCpioEntry import cfig.helper.Helper.Companion.check_call import cfig.helper.Helper.Companion.check_output import cfig.io.Struct3 +import com.fasterxml.jackson.databind.ObjectMapper import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream +import org.apache.commons.compress.archivers.cpio.CpioConstants import org.apache.commons.compress.archivers.zip.* import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream import org.apache.commons.compress.compressors.gzip.GzipParameters @@ -122,7 +125,11 @@ class ZipHelper { } } - fun ZipArchiveOutputStream.packFile(inFile: File, entryName: String, zipMethod: ZipMethod = ZipMethod.DEFLATED) { + 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 @@ -131,7 +138,11 @@ class ZipHelper { this.closeArchiveEntry() } - fun ZipArchiveOutputStream.packEntry(inBuf: ByteArray, entryName: String, zipMethod: ZipMethod = ZipMethod.DEFLATED) { + 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 @@ -140,7 +151,11 @@ class ZipHelper { this.closeArchiveEntry() } - fun ZipArchiveOutputStream.packStream(inStream: InputStream, entryName: String, zipMethod: ZipMethod = ZipMethod.DEFLATED) { + 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 @@ -264,7 +279,8 @@ class ZipHelper { fun decompressLZ4(framedLz4: String, outFile: String) { FramedLZ4CompressorInputStream( - Files.newInputStream(Paths.get(framedLz4))).use { zIn -> + Files.newInputStream(Paths.get(framedLz4)) + ).use { zIn -> Files.newOutputStream(Paths.get(outFile)).use { out -> log.info("decompress lz4: $framedLz4 -> $outFile") val buffer = ByteArray(8192) @@ -360,57 +376,5 @@ class ZipHelper { } } - 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/resources/fsconfig.txt b/bbootimg/src/main/resources/fsconfig.txt new file mode 100644 index 0000000..62bb692 --- /dev/null +++ b/bbootimg/src/main/resources/fsconfig.txt @@ -0,0 +1,99 @@ +{ type:"REG", mode:"770", uid:1000, gid:2001, prefix:"cache" } +{ type:"REG", mode:"555", uid:0, gid:0, prefix:"config" } +{ type:"REG", mode:"771", uid:1000, gid:1000, prefix:"data/app" } +{ type:"REG", mode:"771", uid:1000, gid:1000, prefix:"data/app-private" } +{ type:"REG", mode:"771", uid:1000, gid:1000, prefix:"data/app-ephemeral" } +{ type:"REG", mode:"771", uid:0, gid:0, prefix:"data/dalvik-cache" } +{ type:"REG", mode:"771", uid:1000, gid:1000, prefix:"data/data" } +{ type:"REG", mode:"771", uid:2000, gid:2000, prefix:"data/local/tmp" } +{ type:"REG", mode:"771", uid:2000, gid:2000, prefix:"data/local" } +{ type:"REG", mode:"770", uid:1014, gid:1014, prefix:"data/misc/dhcp" } +{ type:"REG", mode:"771", uid:1037, gid:1037, prefix:"data/misc/shared_relro" } +{ type:"REG", mode:"1771", uid:1000, gid:9998, prefix:"data/misc" } +{ type:"REG", mode:"775", uid:1023, gid:1023, prefix:"data/media/Music" } +{ type:"REG", mode:"775", uid:1023, gid:1023, prefix:"data/media" } +{ type:"REG", mode:"750", uid:0, gid:2000, prefix:"data/nativetest" } +{ type:"REG", mode:"750", uid:0, gid:2000, prefix:"data/nativetest64" } +{ type:"REG", mode:"750", uid:0, gid:2000, prefix:"data/benchmarktest" } +{ type:"REG", mode:"750", uid:0, gid:2000, prefix:"data/benchmarktest64" } +{ type:"REG", mode:"775", uid:0, gid:0, prefix:"data/preloads" } +{ type:"REG", mode:"771", uid:1000, gid:1000, prefix:"data" } +{ type:"REG", mode:"755", uid:0, gid:1000, prefix:"mnt" } +{ type:"REG", mode:"751", uid:0, gid:2000, prefix:"product/bin" } +{ type:"REG", mode:"777", uid:0, gid:0, prefix:"sdcard" } +{ type:"REG", mode:"751", uid:0, gid:1028, prefix:"storage" } +{ type:"REG", mode:"751", uid:0, gid:2000, prefix:"system/bin" } +{ type:"REG", mode:"755", uid:0, gid:0, prefix:"system/etc/ppp" } +{ type:"REG", mode:"755", uid:0, gid:2000, prefix:"system/vendor" } +{ type:"REG", mode:"751", uid:0, gid:2000, prefix:"system/xbin" } +{ type:"REG", mode:"751", uid:0, gid:2000, prefix:"system/apex/*/bin" } +{ type:"REG", mode:"751", uid:0, gid:2000, prefix:"system_ext/bin" } +{ type:"REG", mode:"751", uid:0, gid:2000, prefix:"system_ext/apex/*/bin" } +{ type:"REG", mode:"751", uid:0, gid:2000, prefix:"vendor/bin" } +{ type:"REG", mode:"755", uid:0, gid:2000, prefix:"vendor" } +{ type:"DIR", mode:"644", uid:1000, gid:1000, prefix:"data/app/*" } +{ type:"DIR", mode:"644", uid:1000, gid:1000, prefix:"data/app-ephemeral/*" } +{ type:"DIR", mode:"644", uid:1000, gid:1000, prefix:"data/app-private/*" } +{ type:"DIR", mode:"644", uid:10000, gid:10000, prefix:"data/data/*" } +{ type:"DIR", mode:"644", uid:1023, gid:1023, prefix:"data/media/*" } +{ type:"DIR", mode:"640", uid:0, gid:2000, prefix:"data/nativetest/tests.txt" } +{ type:"DIR", mode:"640", uid:0, gid:2000, prefix:"data/nativetest64/tests.txt" } +{ type:"DIR", mode:"750", uid:0, gid:2000, prefix:"data/nativetest/*" } +{ type:"DIR", mode:"750", uid:0, gid:2000, prefix:"data/nativetest64/*" } +{ type:"DIR", mode:"750", uid:0, gid:2000, prefix:"data/benchmarktest/*" } +{ type:"DIR", mode:"750", uid:0, gid:2000, prefix:"data/benchmarktest64/*" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"default.prop" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"system/etc/prop.default" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"odm/build.prop" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"odm/default.prop" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"odm/etc/build.prop" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"odm/etc/fs_config_dirs" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"odm/etc/fs_config_files" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"oem/etc/fs_config_dirs" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"oem/etc/fs_config_files" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"product/build.prop" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"product/etc/fs_config_dirs" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"product/etc/fs_config_files" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"system_ext/build.prop" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"system_ext/etc/fs_config_dirs" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"system_ext/etc/fs_config_files" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system/bin/crash_dump32" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system/bin/crash_dump64" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system/bin/debuggerd" } +{ type:"DIR", mode:"550", uid:1036, gid:1036, prefix:"system/bin/logd" } +{ type:"DIR", mode:"700", uid:0, gid:0, prefix:"system/bin/secilc" } +{ type:"DIR", mode:"750", uid:0, gid:0, prefix:"system/bin/uncrypt" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"system/build.prop" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"system/etc/fs_config_dirs" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"system/etc/fs_config_files" } +{ type:"DIR", mode:"440", uid:0, gid:2000, prefix:"system/etc/init.goldfish.rc" } +{ type:"DIR", mode:"550", uid:0, gid:2000, prefix:"system/etc/init.goldfish.sh" } +{ type:"DIR", mode:"550", uid:0, gid:2000, prefix:"system/etc/init.ril" } +{ type:"DIR", mode:"555", uid:0, gid:0, prefix:"system/etc/ppp/*" } +{ type:"DIR", mode:"555", uid:0, gid:0, prefix:"system/etc/rc.*" } +{ type:"DIR", mode:"750", uid:0, gid:0, prefix:"vendor/bin/install-recovery.sh" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"vendor/build.prop" } +{ type:"DIR", mode:"600", uid:0, gid:0, prefix:"vendor/default.prop" } +{ type:"DIR", mode:"440", uid:0, gid:0, prefix:"vendor/etc/recovery.img" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"vendor/etc/fs_config_dirs" } +{ type:"DIR", mode:"444", uid:0, gid:0, prefix:"vendor/etc/fs_config_files" } +{ type:"DIR", mode:"6755", uid:0, gid:0, prefix:"system/xbin/procmem" } +{ type:"DIR", mode:"4750", uid:0, gid:2000, prefix:"system/xbin/su" } +{ type:"DIR", mode:"700", uid:1000, gid:2000, prefix:"system/bin/inputflinger" } +{ type:"DIR", mode:"750", uid:0, gid:2000, prefix:"system/bin/run-as" } +{ type:"DIR", mode:"750", uid:0, gid:2000, prefix:"system/bin/simpleperf_app_runner" } +{ type:"DIR", mode:"755", uid:0, gid:0, prefix:"first_stage_ramdisk/system/bin/e2fsck" } +{ type:"DIR", mode:"755", uid:0, gid:0, prefix:"first_stage_ramdisk/system/bin/tune2fs" } +{ type:"DIR", mode:"755", uid:0, gid:0, prefix:"first_stage_ramdisk/system/bin/resize2fs" } +{ type:"DIR", mode:"755", uid:0, gid:0, prefix:"bin/*" } +{ type:"DIR", mode:"640", uid:0, gid:2000, prefix:"fstab.*" } +{ type:"DIR", mode:"750", uid:0, gid:2000, prefix:"init*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"odm/bin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"product/bin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system/bin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system/xbin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system/apex/*/bin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system_ext/bin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"system_ext/apex/*/bin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"vendor/bin/*" } +{ type:"DIR", mode:"755", uid:0, gid:2000, prefix:"vendor/xbin/*" } diff --git a/bbootimg/src/test/kotlin/avb/FooterTest.kt b/bbootimg/src/test/kotlin/avb/FooterTest.kt index f7157b0..78c280b 100644 --- a/bbootimg/src/test/kotlin/avb/FooterTest.kt +++ b/bbootimg/src/test/kotlin/avb/FooterTest.kt @@ -1,15 +1,17 @@ package avb import avb.blob.Footer +import cfig.bootimg.cpio.AndroidCpioEntry import org.apache.commons.codec.binary.Hex import org.junit.Test import org.junit.Assert.* import java.io.ByteArrayInputStream +import java.nio.file.Files +import java.nio.file.Paths @OptIn(ExperimentalUnsignedTypes::class) class FooterTest { - @Test fun readAVBfooter() { val footerBytes = this.javaClass.classLoader.getResourceAsStream("taimen.avbfooter").readBytes() diff --git a/bbootimg/src/test/kotlin/bootimg/AndroidCpioEntryTest.kt b/bbootimg/src/test/kotlin/bootimg/AndroidCpioEntryTest.kt new file mode 100644 index 0000000..9e8d153 --- /dev/null +++ b/bbootimg/src/test/kotlin/bootimg/AndroidCpioEntryTest.kt @@ -0,0 +1,60 @@ +package bootimg + +import cfig.bootimg.cpio.AndroidCpio +import cfig.bootimg.cpio.AndroidCpioEntry +import cfig.helper.Helper +import org.junit.Assert +import org.junit.Assert.assertTrue +import org.junit.Test + +class AndroidCpioEntryTest { + @Test + fun dirEntry() { + run {//dir, fileMode 040755 + val entry1 = AndroidCpioEntry(name = "acct", statMode = 0x41ed, data = byteArrayOf(), ino = 300000) + val exp = Helper.fromHexString("3037303730313030303439336530303030303431656430303030303030303030303030303030303030303030303130303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030353030303030303030616363740000") + Assert.assertTrue(entry1.encode().contentEquals(exp)) + Assert.assertTrue(entry1.encode2().contentEquals(exp)) + } + + run {//dir, fileMode 040755 + val entry2 = AndroidCpioEntry(name = "apex", statMode = 0x41ed, data = byteArrayOf(), ino = 300001) + val exp2 = Helper.fromHexString("3037303730313030303439336531303030303431656430303030303030303030303030303030303030303030303130303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030353030303030303030617065780000") + assertTrue(entry2.encode().contentEquals(exp2)) + assertTrue(entry2.encode2().contentEquals(exp2)) + } + } + + @Test + fun linkEntry() { + run {//link, fileMode 0120777 + val entry3 = AndroidCpioEntry(name = "bin", statMode = 0xa1a4, data = "/system/bin".toByteArray(), ino = 300002) + val exp3 = Helper.fromHexString("303730373031303030343933653230303030613161343030303030303030303030303030303030303030303030313030303030303030303030303030306230303030303030303030303030303030303030303030303030303030303030303030303030303034303030303030303062696e0000002f73797374656d2f62696e00") + entry3.encode() + entry3.encode2() + entry3.encode() + entry3.encode2() + assertTrue(exp3.contentEquals(entry3.encode())) + assertTrue(exp3.contentEquals(entry3.encode2())) + } + } + + @Test + fun fileEntry() { + //init.environ.rc + val initrc = "23207365742075702074686520676c6f62616c20656e7669726f6e6d656e740a6f6e206561726c792d696e69740a202020206578706f727420414e44524f49445f424f4f544c4f474f20310a202020206578706f727420414e44524f49445f524f4f54202f73797374656d0a202020206578706f727420414e44524f49445f415353455453202f73797374656d2f6170700a202020206578706f727420414e44524f49445f44415441202f646174610a202020206578706f727420414e44524f49445f53544f52414745202f73746f726167650a202020206578706f727420414e44524f49445f4152545f524f4f54202f617065782f636f6d2e616e64726f69642e6172740a202020206578706f727420414e44524f49445f4931384e5f524f4f54202f617065782f636f6d2e616e64726f69642e6931386e0a202020206578706f727420414e44524f49445f545a444154415f524f4f54202f617065782f636f6d2e616e64726f69642e747a646174610a202020206578706f72742045585445524e414c5f53544f52414745202f7364636172640a202020206578706f727420415345435f4d4f554e54504f494e54202f6d6e742f617365630a202020206578706f727420424f4f54434c41535350415448202f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f636f72652d6f6a2e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f636f72652d6c69626172742e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f636f72652d696375346a2e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f6f6b687474702e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f626f756e6379636173746c652e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f6170616368652d786d6c2e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2e6a61723a2f73797374656d2f6672616d65776f726b2f6578742e6a61723a2f73797374656d2f6672616d65776f726b2f74656c6570686f6e792d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f766f69702d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f696d732d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2d6174622d6261636b776172642d636f6d7061746962696c6974792e6a61723a2f617065782f636f6d2e616e64726f69642e636f6e7363727970742f6a6176616c69622f636f6e7363727970742e6a61723a2f617065782f636f6d2e616e64726f69642e6d656469612f6a6176616c69622f757064617461626c652d6d656469612e6a61723a2f617065782f636f6d2e616e64726f69642e6d6564696170726f76696465722f6a6176616c69622f6672616d65776f726b2d6d6564696170726f76696465722e6a61723a2f617065782f636f6d2e616e64726f69642e6f732e7374617473642f6a6176616c69622f6672616d65776f726b2d7374617473642e6a61723a2f617065782f636f6d2e616e64726f69642e7065726d697373696f6e2f6a6176616c69622f6672616d65776f726b2d7065726d697373696f6e2e6a61723a2f617065782f636f6d2e616e64726f69642e73646b6578742f6a6176616c69622f6672616d65776f726b2d73646b657874656e73696f6e732e6a61723a2f617065782f636f6d2e616e64726f69642e776966692f6a6176616c69622f6672616d65776f726b2d776966692e6a61723a2f617065782f636f6d2e616e64726f69642e746574686572696e672f6a6176616c69622f6672616d65776f726b2d746574686572696e672e6a61720a202020206578706f727420444558324f4154424f4f54434c41535350415448202f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f636f72652d6f6a2e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f636f72652d6c69626172742e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f636f72652d696375346a2e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f6f6b687474702e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f626f756e6379636173746c652e6a61723a2f617065782f636f6d2e616e64726f69642e6172742f6a6176616c69622f6170616368652d786d6c2e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2e6a61723a2f73797374656d2f6672616d65776f726b2f6578742e6a61723a2f73797374656d2f6672616d65776f726b2f74656c6570686f6e792d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f766f69702d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f696d732d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2d6174622d6261636b776172642d636f6d7061746962696c6974792e6a61720a202020206578706f72742053595354454d534552564552434c41535350415448202f73797374656d2f6672616d65776f726b2f636f6d2e616e64726f69642e6c6f636174696f6e2e70726f76696465722e6a61723a2f73797374656d2f6672616d65776f726b2f73657276696365732e6a61723a2f73797374656d2f6672616d65776f726b2f65746865726e65742d736572766963652e6a61723a2f617065782f636f6d2e616e64726f69642e7065726d697373696f6e2f6a6176616c69622f736572766963652d7065726d697373696f6e2e6a61723a2f617065782f636f6d2e616e64726f69642e776966692f6a6176616c69622f736572766963652d776966692e6a61723a2f617065782f636f6d2e616e64726f69642e69707365632f6a6176616c69622f616e64726f69642e6e65742e69707365632e696b652e6a61720a202020200a202020200a202020200a202020200a" + println("<" + String(Helper.fromHexString(initrc)) + ">") + val entry = AndroidCpioEntry(name = "/init.environ.rc", + statMode = java.lang.Long.valueOf("100644", 8), + data = Helper.fromHexString(initrc), + ino = 300003) + assertTrue(entry.encode().contentEquals(entry.encode2())) + } + + fun packTest() { + val dir = "/home/yu/work/boot/build/unzip_boot/root" + val oF = "/home/yu/work/boot/root.cpio" + val acpio = AndroidCpio() + acpio.pack(dir, oF, "/home/yu/work/boot/build/unzip_boot/ramdisk_filelist.txt") + } +} diff --git a/bbootimg/src/test/kotlin/bootloader_message/BootloaderMsgTest.kt b/bbootimg/src/test/kotlin/bootloader_message/BootloaderMsgTest.kt index 58a0a80..3eb8fb2 100644 --- a/bbootimg/src/test/kotlin/bootloader_message/BootloaderMsgTest.kt +++ b/bbootimg/src/test/kotlin/bootloader_message/BootloaderMsgTest.kt @@ -1,13 +1,21 @@ package bootloader_message +import cfig.bootimg.Common.Companion.deleleIfExists import cfig.bootloader_message.BootloaderMsg +import org.junit.After import org.junit.Test import org.junit.Assert.* import org.slf4j.LoggerFactory +import java.io.File @OptIn(ExperimentalUnsignedTypes::class) class BootloaderMsgTest { + @After + fun tearDown() { + File(BootloaderMsg.miscFile).deleleIfExists() + } + private val log = LoggerFactory.getLogger(BootloaderMsgTest::class.java) @Test diff --git a/build.gradle.kts b/build.gradle.kts index ba78fca..afaa218 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,7 @@ import org.apache.commons.exec.DefaultExecutor import org.apache.commons.exec.PumpStreamHandler val GROUP_ANDROID = "android" +val localHack = false if (parseGradleVersion(gradle.gradleVersion) < 6) { logger.error("ERROR: Gradle Version MUST >= 6.0, current is {}", gradle.gradleVersion) throw RuntimeException("ERROR: Gradle Version") @@ -73,10 +74,12 @@ tasks { pullTask.dependsOn("bbootimg:jar") //sparse image dependencies - packTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") - packTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") - unpackTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") - unpackTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") + if (localHack) { + packTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") + packTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") + unpackTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") + unpackTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") + } if (System.getProperty("os.name").contains("Mac")) { unpackTask.dependsOn("aosp:libsparse:simg2img:installReleaseMacos") packTask.dependsOn("aosp:libsparse:img2simg:installReleaseMacos") diff --git a/doc/additional_tricks.md b/doc/additional_tricks.md index ddd69c2..a942775 100644 --- a/doc/additional_tricks.md +++ b/doc/additional_tricks.md @@ -62,3 +62,5 @@ This part is contributed by @Surendrajat, thanks! ## using pre-packed ramdisk.img.gz place 'ramdisk.img.gz' in directory, delete "root/", program will use it as prebuilt. +## cpio +decompress cpio with commandline `cpio -idmv -F ` diff --git a/src/integrationTest/resources b/src/integrationTest/resources index 691d62c..5ff0d54 160000 --- a/src/integrationTest/resources +++ b/src/integrationTest/resources @@ -1 +1 @@ -Subproject commit 691d62c214ebd9bc0f383b819710ec4294974469 +Subproject commit 5ff0d54252367d6bae8c8448dc0100c98823a212