blob: df2970bb2eec22bfa642e568a08bf8b58f2f5684 [file] [log] [blame]
package gpt
import (
"bytes"
"crypto/sha256"
"io"
"os"
"testing"
"github.com/google/uuid"
)
func TestUUIDTranspose(t *testing.T) {
testUUID := uuid.MustParse("00112233-4455-6677-c899-aabbccddeeff")
mixedEndianUUID := mangleUUID(testUUID)
expectedMixedEndianUUID := [16]byte{0x33, 0x22, 0x11, 0x00, 0x55, 0x44, 0x77, 0x66, 0xc8, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}
if mixedEndianUUID != expectedMixedEndianUUID {
t.Errorf("mangleUUID(%s) = %x, expected %x", testUUID, mixedEndianUUID, expectedMixedEndianUUID)
}
roundTrippedUUID := unmangleUUID(mixedEndianUUID)
if testUUID != roundTrippedUUID {
t.Errorf("unmangleUUID(mangleUUID(%s)) = %s, expected input", testUUID, roundTrippedUUID)
}
}
func TestFreeSpaces(t *testing.T) {
cases := []struct {
name string
parts []*Partition
expected [][2]int64
expectedOverlap bool
}{
{"Empty", []*Partition{}, [][2]int64{{34, 2015}}, false},
{"OnePart", []*Partition{
{Type: PartitionTypeEFISystem, FirstBlock: 200, LastBlock: 1499},
}, [][2]int64{
{34, 200},
{1500, 2015},
}, false},
{"TwoOverlappingParts", []*Partition{
{Type: PartitionTypeEFISystem, FirstBlock: 200, LastBlock: 1499},
{Type: PartitionTypeEFISystem, FirstBlock: 1000, LastBlock: 1999},
}, [][2]int64{
{34, 200},
{2000, 2015},
}, true},
{"Full", []*Partition{
{Type: PartitionTypeEFISystem, FirstBlock: 34, LastBlock: 999},
{Type: PartitionTypeEFISystem, FirstBlock: 1000, LastBlock: 2014},
}, [][2]int64{}, false},
{"TwoSpacedParts", []*Partition{
{Type: PartitionTypeEFISystem, FirstBlock: 500, LastBlock: 899},
{Type: PartitionTypeEFISystem, FirstBlock: 1200, LastBlock: 1799},
}, [][2]int64{
{34, 500},
{900, 1200},
{1800, 2015},
}, false},
}
// Partitions are created manually as AddPartition calls FreeSpaces itself,
// which makes the test unreliable as well as making failures very hard to
// debug.
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
g := Table{
BlockSize: 512,
BlockCount: 2048, // 0.5KiB * 2048 = 1MiB
Partitions: c.parts,
}
fs, overlap, err := g.GetFreeSpaces()
if err != nil {
t.Fatal(err)
}
if overlap != c.expectedOverlap {
t.Errorf("expected overlap %v, got %v", c.expectedOverlap, overlap)
}
if len(fs) != len(c.expected) {
t.Fatalf("expected %v, got %v", c.expected, fs)
}
for i := range fs {
if fs[i] != c.expected[i] {
t.Errorf("free space mismatch at pos %d: got [%d, %d), expected [%d, %d)", i, fs[i][0], fs[i][1], c.expected[i][0], c.expected[i][1])
}
}
})
}
}
func TestRoundTrip(t *testing.T) {
if os.Getenv("IN_KTEST") == "true" {
t.Skip("In ktest")
}
g := Table{
ID: uuid.NewSHA1(zeroUUID, []byte("test")),
BlockSize: 512,
BlockCount: 2048,
BootCode: []byte("just some test code"),
Partitions: []*Partition{
nil,
// This emoji is very complex and exercises UTF16 surrogate encoding
// and composing.
{Name: "Test 🏃‍♂️", FirstBlock: 10, LastBlock: 19, Type: PartitionTypeEFISystem, ID: uuid.NewSHA1(zeroUUID, []byte("test1")), Attributes: AttrNoBlockIOProto},
nil,
{Name: "Test2", FirstBlock: 20, LastBlock: 49, Type: PartitionTypeEFISystem, ID: uuid.NewSHA1(zeroUUID, []byte("test2")), Attributes: 0},
},
}
f, err := os.CreateTemp("", "")
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
defer os.Remove(f.Name())
if err := Write(f, &g); err != nil {
t.Fatalf("Error while wrinting Table: %v", err)
}
f.Seek(0, io.SeekStart)
originalHash := sha256.New()
if _, err := io.Copy(originalHash, f); err != nil {
panic(err)
}
g2, err := Read(f, 512, 2048)
if err != nil {
t.Fatalf("Failed to read back GPT: %v", err)
}
if g2.ID != g.ID {
t.Errorf("Disk UUID changed when reading back: %v", err)
}
// Destroy primary GPT
f.Seek(1*g.BlockSize, io.SeekStart)
f.Write(make([]byte, 512))
f.Seek(0, io.SeekStart)
g3, err := Read(f, 512, 2048)
if err != nil {
t.Fatalf("Failed to read back GPT with primary GPT destroyed: %v", err)
}
if g3.ID != g.ID {
t.Errorf("Disk UUID changed when reading back: %v", err)
}
f.Seek(0, io.SeekStart)
if err := Write(f, g3); err != nil {
t.Fatalf("Error while writing back GPT: %v", err)
}
f.Seek(0, io.SeekStart)
rewrittenHash := sha256.New()
if _, err := io.Copy(rewrittenHash, f); err != nil {
panic(err)
}
if !bytes.Equal(originalHash.Sum([]byte{}), rewrittenHash.Sum([]byte{})) {
t.Errorf("Write/Read/Write test was not reproducible: %x != %x", originalHash.Sum([]byte{}), rewrittenHash.Sum([]byte{}))
}
}