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.

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