You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
2.8 KiB
95 lines
2.8 KiB
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
)
|
|
|
|
func TestWriteInitramfsIsDeterministicAndContainsExecutableInit(t *testing.T) {
|
|
first := filepath.Join(t.TempDir(), "first.cpio.gz")
|
|
second := filepath.Join(t.TempDir(), "second.cpio.gz")
|
|
for _, output := range []string{first, second} {
|
|
if err := writeInitramfs(output, []byte("static-arm64-init")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
one, _ := os.ReadFile(first)
|
|
two, _ := os.ReadFile(second)
|
|
if !bytes.Equal(one, two) {
|
|
t.Fatal("initramfs output is not deterministic")
|
|
}
|
|
entries := readNewc(t, one)
|
|
if got := string(entries["init"].data); got != "static-arm64-init" {
|
|
t.Fatalf("init contents = %q", got)
|
|
}
|
|
if entries["init"].mode != 0o100755 {
|
|
t.Fatalf("init mode = %#o", entries["init"].mode)
|
|
}
|
|
for _, directory := range []string{"dev", "proc", "sys", "userdata"} {
|
|
if entries[directory].mode&0o170000 != 0o040000 {
|
|
t.Fatalf("%s is not a directory", directory)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWriteInitramfsIncludesReadOnlyTrustAnchor(t *testing.T) {
|
|
output := filepath.Join(t.TempDir(), "recovery.cpio.gz")
|
|
if err := writeInitramfsWithEntries(output, []byte("init"), []archiveEntry{{name: "etc/tera/update-trust/production.json", mode: 0o100444, data: []byte(`{"format_version":1}`)}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
compressed, err := os.ReadFile(output)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entries := readNewc(t, compressed)
|
|
anchor := entries["etc/tera/update-trust/production.json"]
|
|
if anchor.mode != 0o100444 || string(anchor.data) != `{"format_version":1}` {
|
|
t.Fatalf("trust anchor = %+v", anchor)
|
|
}
|
|
}
|
|
|
|
func readNewc(t *testing.T, compressed []byte) map[string]archiveEntry {
|
|
t.Helper()
|
|
reader, err := gzip.NewReader(bytes.NewReader(compressed))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
data, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entries := map[string]archiveEntry{}
|
|
for offset := 0; ; {
|
|
if offset+110 > len(data) || string(data[offset:offset+6]) != "070701" {
|
|
t.Fatalf("invalid newc header at %d", offset)
|
|
}
|
|
mode := parseHex(t, data[offset+14:offset+22])
|
|
size := int(parseHex(t, data[offset+54:offset+62]))
|
|
nameSize := int(parseHex(t, data[offset+94:offset+102]))
|
|
nameStart := offset + 110
|
|
name := string(data[nameStart : nameStart+nameSize-1])
|
|
dataStart := align4(nameStart + nameSize)
|
|
if name == "TRAILER!!!" {
|
|
break
|
|
}
|
|
entries[name] = archiveEntry{name: name, mode: mode, data: append([]byte(nil), data[dataStart:dataStart+size]...)}
|
|
offset = align4(dataStart + size)
|
|
}
|
|
return entries
|
|
}
|
|
|
|
func parseHex(t *testing.T, value []byte) uint32 {
|
|
t.Helper()
|
|
parsed, err := strconv.ParseUint(string(value), 16, 32)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return uint32(parsed)
|
|
}
|
|
|
|
func align4(value int) int { return (value + 3) &^ 3 }
|
|
|