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 5 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 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
kotlin("jvm") version "1.4.20" kotlin("jvm") version "1.4.21"
kotlin("plugin.serialization") version "1.4.20"
application application
} }

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

@ -1,6 +1,7 @@
package cfig.bootimg package cfig.bootimg
import cfig.EnvironmentVerifier import cfig.EnvironmentVerifier
import cfig.bootimg.cpio.AndroidCpio
import cfig.dtb_util.DTC import cfig.dtb_util.DTC
import cfig.helper.Helper import cfig.helper.Helper
import cfig.helper.ZipHelper import cfig.helper.ZipHelper
@ -28,7 +29,8 @@ class Common {
var path: String = "/boot", var path: String = "/boot",
var verity_pk8: String = "", var verity_pk8: String = "",
var verity_pem: String = "", var verity_pem: String = "",
var jarPath: String = "") var jarPath: String = ""
)
data class Slice( data class Slice(
var srcFile: String, var srcFile: String,
@ -155,9 +157,11 @@ class Common {
val md = MessageDigest.getInstance("SHA1") val md = MessageDigest.getInstance("SHA1")
for (item in inFiles) { for (item in inFiles) {
if (null == item) { if (null == item) {
md.update(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN) md.update(
ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
.putInt(0) .putInt(0)
.array()) .array()
)
log.debug("update null $item: " + Helper.toHexString((md.clone() as MessageDigest).digest())) log.debug("update null $item: " + Helper.toHexString((md.clone() as MessageDigest).digest()))
} else { } else {
val currentFile = File(item) val currentFile = File(item)
@ -172,9 +176,11 @@ class Common {
md.update(dataRead, 0, byteRead) md.update(dataRead, 0, byteRead)
} }
log.debug("update file $item: " + Helper.toHexString((md.clone() as MessageDigest).digest())) 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()) .putInt(currentFile.length().toInt())
.array()) .array()
)
log.debug("update SIZE $item: " + Helper.toHexString((md.clone() as MessageDigest).digest())) 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) { fun packRootfs(rootDir: String, ramdiskGz: String, osMajor: Int = 10) {
val mkbootfs = String.format(Helper.prop("mkbootfsBin"), osMajor) val mkbootfs = String.format(Helper.prop("mkbootfsBin"), osMajor)
log.info("Packing rootfs $rootDir ...") log.info("Packing rootfs $rootDir ...")
@ -219,6 +226,28 @@ class Common {
log.info("$ramdiskGz is ready") 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) { fun padFile(inBF: ByteBuffer, padding: Int) {
val pad = padding - (inBF.position() and padding - 1) and padding - 1 val pad = padding - (inBF.position() and padding - 1) and padding - 1
inBF.put(ByteArray(pad)) inBF.put(ByteArray(pad))
@ -264,8 +293,11 @@ class Common {
mkdirs() mkdirs()
} }
//("cpio -idmv -F " + File(ramdisk).canonicalPath).check_call(rootFile.canonicalPath) AndroidCpio.decompressCPIO(
ZipHelper.decompressCPIO(File(ramdisk).canonicalPath, rootFile.canonicalPath, File(ramdisk).canonicalPath + ".filelist") File(ramdisk).canonicalPath,
rootFile.canonicalPath,
File(ramdisk).canonicalPath.removeSuffix(".img") + "_filelist.txt"
)
log.info(" ramdisk extracted : $ramdisk -> $rootFile") 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 package cfig.bootimg.v2
import cfig.Avb import cfig.Avb
import cfig.helper.Helper
import cfig.bootimg.Common import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.deleleIfExists import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Slice import cfig.bootimg.Common.Slice
import cfig.bootimg.Signer import cfig.bootimg.Signer
import cfig.helper.Helper
import cfig.packable.VBMetaParser import cfig.packable.VBMetaParser
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import de.vandermeer.asciitable.AsciiTable import de.vandermeer.asciitable.AsciiTable
@ -323,7 +323,8 @@ data class BootV2(
} else { } else {
File(this.ramdisk.file!!).deleleIfExists() File(this.ramdisk.file!!).deleleIfExists()
File(this.ramdisk.file!!.removeSuffix(".gz")).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() this.ramdisk.size = File(this.ramdisk.file!!).length().toInt()
} }

@ -79,7 +79,10 @@ data class BootV3(var info: MiscInfo = MiscInfo(),
} else { } else {
File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file).deleleIfExists()
File(this.ramdisk.file.replaceFirst("[.][^.]+$", "")).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.kernel.size = File(this.kernel.file).length().toInt()
this.ramdisk.size = File(this.ramdisk.file).length().toInt() this.ramdisk.size = File(this.ramdisk.file).length().toInt()

@ -87,7 +87,10 @@ data class VendorBoot(var info: MiscInfo = MiscInfo(),
} else { } else {
File(this.ramdisk.file).deleleIfExists() File(this.ramdisk.file).deleleIfExists()
File(this.ramdisk.file.removeSuffix(".gz")).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.ramdisk.size = File(this.ramdisk.file).length().toInt()
this.dtb.size = File(this.dtb.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 { companion object {
private const val FORMAT_STRING = "32s32s768s32s1184b" private const val FORMAT_STRING = "32s32s768s32s1184b"
private const val miscFile = "misc.file" const val miscFile = "misc.file"
const val SIZE = 2048 const val SIZE = 2048
private val log = LoggerFactory.getLogger("BootloaderMsg") private val log = LoggerFactory.getLogger("BootloaderMsg")

@ -1,9 +1,12 @@
package cfig.helper package cfig.helper
import cfig.bootimg.cpio.AndroidCpioEntry
import cfig.helper.Helper.Companion.check_call import cfig.helper.Helper.Companion.check_call
import cfig.helper.Helper.Companion.check_output import cfig.helper.Helper.Companion.check_output
import cfig.io.Struct3 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.CpioArchiveInputStream
import org.apache.commons.compress.archivers.cpio.CpioConstants
import org.apache.commons.compress.archivers.zip.* import org.apache.commons.compress.archivers.zip.*
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
import org.apache.commons.compress.compressors.gzip.GzipParameters 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()} ...") log.info("packing $entryName($zipMethod) from file $inFile (size=${inFile.length()} ...")
val entry = ZipArchiveEntry(inFile, entryName) val entry = ZipArchiveEntry(inFile, entryName)
entry.method = zipMethod.ordinal entry.method = zipMethod.ordinal
@ -131,7 +138,11 @@ class ZipHelper {
this.closeArchiveEntry() 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}...") log.info("packing $entryName($zipMethod) from memory data (size=${inBuf.size}...")
val entry = ZipArchiveEntry(entryName) val entry = ZipArchiveEntry(entryName)
entry.method = zipMethod.ordinal entry.method = zipMethod.ordinal
@ -140,7 +151,11 @@ class ZipHelper {
this.closeArchiveEntry() 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...") log.info("packing $entryName($zipMethod) from input stream (size=unknown...")
val entry = ZipArchiveEntry(entryName) val entry = ZipArchiveEntry(entryName)
entry.method = zipMethod.ordinal entry.method = zipMethod.ordinal
@ -264,7 +279,8 @@ class ZipHelper {
fun decompressLZ4(framedLz4: String, outFile: String) { fun decompressLZ4(framedLz4: String, outFile: String) {
FramedLZ4CompressorInputStream( FramedLZ4CompressorInputStream(
Files.newInputStream(Paths.get(framedLz4))).use { zIn -> Files.newInputStream(Paths.get(framedLz4))
).use { zIn ->
Files.newOutputStream(Paths.get(outFile)).use { out -> Files.newOutputStream(Paths.get(outFile)).use { out ->
log.info("decompress lz4: $framedLz4 -> $outFile") log.info("decompress lz4: $framedLz4 -> $outFile")
val buffer = ByteArray(8192) 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 package avb
import avb.blob.Footer import avb.blob.Footer
import cfig.bootimg.cpio.AndroidCpioEntry
import org.apache.commons.codec.binary.Hex import org.apache.commons.codec.binary.Hex
import org.junit.Test import org.junit.Test
import org.junit.Assert.* import org.junit.Assert.*
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.nio.file.Files
import java.nio.file.Paths
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
class FooterTest { class FooterTest {
@Test @Test
fun readAVBfooter() { fun readAVBfooter() {
val footerBytes = this.javaClass.classLoader.getResourceAsStream("taimen.avbfooter").readBytes() 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 package bootloader_message
import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootloader_message.BootloaderMsg import cfig.bootloader_message.BootloaderMsg
import org.junit.After
import org.junit.Test import org.junit.Test
import org.junit.Assert.* import org.junit.Assert.*
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
class BootloaderMsgTest { class BootloaderMsgTest {
@After
fun tearDown() {
File(BootloaderMsg.miscFile).deleleIfExists()
}
private val log = LoggerFactory.getLogger(BootloaderMsgTest::class.java) private val log = LoggerFactory.getLogger(BootloaderMsgTest::class.java)
@Test @Test

@ -5,6 +5,7 @@ import org.apache.commons.exec.DefaultExecutor
import org.apache.commons.exec.PumpStreamHandler import org.apache.commons.exec.PumpStreamHandler
val GROUP_ANDROID = "android" val GROUP_ANDROID = "android"
val localHack = false
if (parseGradleVersion(gradle.gradleVersion) < 6) { if (parseGradleVersion(gradle.gradleVersion) < 6) {
logger.error("ERROR: Gradle Version MUST >= 6.0, current is {}", gradle.gradleVersion) logger.error("ERROR: Gradle Version MUST >= 6.0, current is {}", gradle.gradleVersion)
throw RuntimeException("ERROR: Gradle Version") throw RuntimeException("ERROR: Gradle Version")
@ -73,10 +74,12 @@ tasks {
pullTask.dependsOn("bbootimg:jar") pullTask.dependsOn("bbootimg:jar")
//sparse image dependencies //sparse image dependencies
if (localHack) {
packTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") packTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable")
packTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") packTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable")
unpackTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable") unpackTask.dependsOn("aosp:mkbootfs.10:mkbootfsExecutable")
unpackTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable") unpackTask.dependsOn("aosp:mkbootfs.11:mkbootfsExecutable")
}
if (System.getProperty("os.name").contains("Mac")) { if (System.getProperty("os.name").contains("Mac")) {
unpackTask.dependsOn("aosp:libsparse:simg2img:installReleaseMacos") unpackTask.dependsOn("aosp:libsparse:simg2img:installReleaseMacos")
packTask.dependsOn("aosp:libsparse:img2simg:installReleaseMacos") packTask.dependsOn("aosp:libsparse:img2simg:installReleaseMacos")

@ -62,3 +62,5 @@ This part is contributed by @Surendrajat, thanks!
## using pre-packed ramdisk.img.gz ## using pre-packed ramdisk.img.gz
place 'ramdisk.img.gz' in directory, delete "root/", program will use it as prebuilt. 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