blob: 924d4f76dff3f9da59858b62b9f3ee020c023c4c [file] [log] [blame]
Lorenz Brunee17d832022-10-18 12:02:45 +00001package gpt
2
3import (
4 "bytes"
5 "crypto/sha256"
6 "io"
7 "os"
8 "testing"
9
10 "github.com/google/uuid"
11)
12
Lorenz Brunee17d832022-10-18 12:02:45 +000013func TestFreeSpaces(t *testing.T) {
14 cases := []struct {
15 name string
16 parts []*Partition
17 expected [][2]int64
18 expectedOverlap bool
19 }{
20 {"Empty", []*Partition{}, [][2]int64{{34, 2015}}, false},
21 {"OnePart", []*Partition{
22 {Type: PartitionTypeEFISystem, FirstBlock: 200, LastBlock: 1499},
23 }, [][2]int64{
24 {34, 200},
25 {1500, 2015},
26 }, false},
27 {"TwoOverlappingParts", []*Partition{
28 {Type: PartitionTypeEFISystem, FirstBlock: 200, LastBlock: 1499},
29 {Type: PartitionTypeEFISystem, FirstBlock: 1000, LastBlock: 1999},
30 }, [][2]int64{
31 {34, 200},
32 {2000, 2015},
33 }, true},
34 {"Full", []*Partition{
35 {Type: PartitionTypeEFISystem, FirstBlock: 34, LastBlock: 999},
36 {Type: PartitionTypeEFISystem, FirstBlock: 1000, LastBlock: 2014},
37 }, [][2]int64{}, false},
38 {"TwoSpacedParts", []*Partition{
39 {Type: PartitionTypeEFISystem, FirstBlock: 500, LastBlock: 899},
40 {Type: PartitionTypeEFISystem, FirstBlock: 1200, LastBlock: 1799},
41 }, [][2]int64{
42 {34, 500},
43 {900, 1200},
44 {1800, 2015},
45 }, false},
46 }
47
48 // Partitions are created manually as AddPartition calls FreeSpaces itself,
49 // which makes the test unreliable as well as making failures very hard to
50 // debug.
51 for _, c := range cases {
52 t.Run(c.name, func(t *testing.T) {
53 g := Table{
54 BlockSize: 512,
55 BlockCount: 2048, // 0.5KiB * 2048 = 1MiB
56 Partitions: c.parts,
57 }
58 fs, overlap, err := g.GetFreeSpaces()
59 if err != nil {
60 t.Fatal(err)
61 }
62 if overlap != c.expectedOverlap {
63 t.Errorf("expected overlap %v, got %v", c.expectedOverlap, overlap)
64 }
65 if len(fs) != len(c.expected) {
66 t.Fatalf("expected %v, got %v", c.expected, fs)
67 }
68 for i := range fs {
69 if fs[i] != c.expected[i] {
70 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])
71 }
72 }
73 })
74 }
75}
76
77func TestRoundTrip(t *testing.T) {
78 if os.Getenv("IN_KTEST") == "true" {
79 t.Skip("In ktest")
80 }
81 g := Table{
82 ID: uuid.NewSHA1(zeroUUID, []byte("test")),
83 BlockSize: 512,
84 BlockCount: 2048,
85 BootCode: []byte("just some test code"),
86 Partitions: []*Partition{
87 nil,
88 // This emoji is very complex and exercises UTF16 surrogate encoding
89 // and composing.
90 {Name: "Test 🏃‍♂️", FirstBlock: 10, LastBlock: 19, Type: PartitionTypeEFISystem, ID: uuid.NewSHA1(zeroUUID, []byte("test1")), Attributes: AttrNoBlockIOProto},
91 nil,
92 {Name: "Test2", FirstBlock: 20, LastBlock: 49, Type: PartitionTypeEFISystem, ID: uuid.NewSHA1(zeroUUID, []byte("test2")), Attributes: 0},
93 },
94 }
95 f, err := os.CreateTemp("", "")
96 if err != nil {
97 t.Fatalf("Failed to create temporary file: %v", err)
98 }
99 defer os.Remove(f.Name())
100 if err := Write(f, &g); err != nil {
101 t.Fatalf("Error while wrinting Table: %v", err)
102 }
103 f.Seek(0, io.SeekStart)
104 originalHash := sha256.New()
105 if _, err := io.Copy(originalHash, f); err != nil {
106 panic(err)
107 }
108
109 g2, err := Read(f, 512, 2048)
110 if err != nil {
111 t.Fatalf("Failed to read back GPT: %v", err)
112 }
113 if g2.ID != g.ID {
114 t.Errorf("Disk UUID changed when reading back: %v", err)
115 }
116 // Destroy primary GPT
117 f.Seek(1*g.BlockSize, io.SeekStart)
118 f.Write(make([]byte, 512))
119 f.Seek(0, io.SeekStart)
120 g3, err := Read(f, 512, 2048)
121 if err != nil {
122 t.Fatalf("Failed to read back GPT with primary GPT destroyed: %v", err)
123 }
124 if g3.ID != g.ID {
125 t.Errorf("Disk UUID changed when reading back: %v", err)
126 }
127 f.Seek(0, io.SeekStart)
128 if err := Write(f, g3); err != nil {
129 t.Fatalf("Error while writing back GPT: %v", err)
130 }
131 f.Seek(0, io.SeekStart)
132 rewrittenHash := sha256.New()
133 if _, err := io.Copy(rewrittenHash, f); err != nil {
134 panic(err)
135 }
136 if !bytes.Equal(originalHash.Sum([]byte{}), rewrittenHash.Sum([]byte{})) {
137 t.Errorf("Write/Read/Write test was not reproducible: %x != %x", originalHash.Sum([]byte{}), rewrittenHash.Sum([]byte{}))
138 }
139}