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
for/win
cfig 4 years ago
parent 454c5e6ae8
commit 4e9d60db1a
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -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
}

@ -0,0 +1 @@
kotlin.code.style=official

@ -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")
}

@ -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<AndroidCpioEntry> = mutableSetOf()
private val fsConfigTemplate: List<FsConfigTemplate> = 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<FsConfigTemplate> {
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()
}
}
}

@ -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
}
}
}
}

@ -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
}
}
/* <bits/stat.h>
/* 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
*/

@ -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()
}

@ -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()

@ -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()

@ -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")

@ -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()
}
}
}

@ -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/*" }

@ -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()

@ -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")
}
}

@ -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

@ -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")

@ -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 <file>`

@ -1 +1 @@
Subproject commit 691d62c214ebd9bc0f383b819710ec4294974469
Subproject commit 5ff0d54252367d6bae8c8448dc0100c98823a212
Loading…
Cancel
Save