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 checkingfor/win
parent
454c5e6ae8
commit
4e9d60db1a
@ -0,0 +1 @@
|
||||
kotlin.code.style=official
|
@ -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
|
||||
|
||||
*/
|
@ -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/*" }
|
@ -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 +1 @@
|
||||
Subproject commit 691d62c214ebd9bc0f383b819710ec4294974469
|
||||
Subproject commit 5ff0d54252367d6bae8c8448dc0100c98823a212
|
Loading…
Reference in New Issue