package recoveryrequest import ( "bytes" "os" "path/filepath" "testing" ) func TestEncodeMatchesRockchipBootloaderMessageLayout(t *testing.T) { encoded, err := Encode("fw-0123456789abcdef") if err != nil { t.Fatal(err) } if len(encoded) != Size { t.Fatalf("size = %d, want %d", len(encoded), Size) } if got := field(encoded, commandOffset, commandSize); got != Command { t.Fatalf("command = %q", got) } if got := field(encoded, statusOffset, statusSize); got != "" { t.Fatalf("status = %q", got) } if got := field(encoded, recoveryOffset, recoverySize); got != "recovery\n--tera-update\n--operation=fw-0123456789abcdef\n" { t.Fatalf("recovery = %q", got) } if got := field(encoded, stageOffset, stageSize); got != "" { t.Fatalf("stage = %q", got) } message, err := Decode(encoded) if err != nil || message.OperationID != "fw-0123456789abcdef" { t.Fatalf("Decode = %+v, %v", message, err) } } func TestWriteAtTouchesOnlyTheBCBAndReadsBack(t *testing.T) { device := bytes.NewReader(bytes.Repeat([]byte{0xa5}, int(Offset)+Size+4096)) buffer := &memoryDevice{data: make([]byte, device.Size())} if _, err := device.ReadAt(buffer.data, 0); err != nil { t.Fatal(err) } encoded, _ := Encode("operation-1") if err := WriteAt(buffer, encoded); err != nil { t.Fatal(err) } if !bytes.Equal(buffer.data[:Offset], bytes.Repeat([]byte{0xa5}, int(Offset))) { t.Fatal("bytes before the BCB changed") } if !bytes.Equal(buffer.data[Offset+Size:], bytes.Repeat([]byte{0xa5}, len(buffer.data)-int(Offset)-Size)) { t.Fatal("bytes after the BCB changed") } message, err := ReadAt(buffer) if err != nil || message.OperationID != "operation-1" { t.Fatalf("ReadAt = %+v, %v", message, err) } } func TestRejectsInvalidOrForeignMessages(t *testing.T) { for _, value := range []string{"", " space", "slash/value", string(bytes.Repeat([]byte{'a'}, 65))} { if _, err := Encode(value); err == nil { t.Fatalf("Encode(%q) succeeded", value) } } foreign := make([]byte, Size) copy(foreign, "boot-recovery") copy(foreign[recoveryOffset:], "recovery\n--wipe_data\n") if _, err := Decode(foreign); err == nil { t.Fatal("foreign recovery message was accepted") } if _, err := Decode(Clear()); err == nil { t.Fatal("cleared message was accepted") } } func TestClearIsExactZeroRecord(t *testing.T) { cleared := Clear() if len(cleared) != Size { t.Fatalf("Clear size = %d, want %d", len(cleared), Size) } if !bytes.Equal(cleared, make([]byte, Size)) { t.Fatal("Clear returned a non-zero BCB byte") } } func TestWriteRequestFileRequiresClearRecordAndPreservesSurroundingBytes(t *testing.T) { path := filepath.Join(t.TempDir(), "misc.img") contents := bytes.Repeat([]byte{0xa5}, int(Offset)+Size+4096) copy(contents[Offset:Offset+Size], Clear()) if err := os.WriteFile(path, contents, 0o600); err != nil { t.Fatal(err) } if err := WriteRequestFile(path, "operation-1", true); err != nil { t.Fatal(err) } updated, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if !bytes.Equal(updated[:Offset], contents[:Offset]) || !bytes.Equal(updated[Offset+Size:], contents[Offset+Size:]) { t.Fatal("WriteRequestFile changed bytes outside the BCB") } if IsClear(updated[Offset : Offset+Size]) { t.Fatal("request remained clear") } if err := WriteRequestFile(path, "operation-2", true); err == nil { t.Fatal("non-cleared recovery request was overwritten") } } type memoryDevice struct{ data []byte } func (d *memoryDevice) ReadAt(p []byte, off int64) (int, error) { return copy(p, d.data[int(off):]), nil } func (d *memoryDevice) WriteAt(p []byte, off int64) (int, error) { return copy(d.data[int(off):], p), nil }