github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/mount/gpt/gpt_test.go (about) 1 // Copyright 2017-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !race 6 // +build !race 7 8 package gpt 9 10 import ( 11 "bytes" 12 "encoding/hex" 13 "io" 14 "reflect" 15 "testing" 16 17 "github.com/google/go-cmp/cmp" 18 ) 19 20 const ( 21 equalHeaderError = "p.Signature(0x5452415020494646) != b.Signature(0x5452415020494645); p.Revision(65537) != b.Revision(65536); p.HeaderSize(93) != b.HeaderSize(92); p.CurrentLBA(0x2) != b.BackupLBA(0x1); p.FirstLBA(0x23) != b.FirstLBA(0x22); p.LastLBA(0x43cf9f) != b.LastLBA(0x43cf9e); p.DiskGUID({0xbad41e2d 0x93ef 0xb04a 0x856e2e3a6a2d73bf}) != b.DiskGUID({0xbad41e2d 0x93ef 0xb04a 0x846e2e3a6a2d73bf}); p.NPart(127) != b.NPart(128); p.PartSize(127) != b.PartSize(128)" 22 equalPartsError = "Partition 3: p.PartGUID({0xfe3a2a5d 0x4f32 0x41a7 0xb825accc3285a309}) != b.PartGUID({0xfe3a2a5d 0x4f32 0x41a7 0xb725accc3285a309}); Partition 8: p.UniqueGUID({0x513a98ed 0xc43e 0x144a 0x8399a47a7e6ae42c}) != b.UniqueGUID({0x513a98ed 0xc43e 0x144a 0x8398a47a7e6ae42c}); Partition 11: p.FirstLBA(0x3d001) != b.FirstLBA(0x3d000); Partition 21: p.LastLBA(0x1) != b.LastLBA(0x0); Partition 61: p.Name(0x000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) != b.Name(0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)" 23 ) 24 25 var ( 26 header = Header{ 27 Signature: Signature, 28 Revision: Revision, 29 HeaderSize: HeaderSize, 30 CRC: 0x22519292, 31 Reserved: 0, 32 CurrentLBA: 1, 33 BackupLBA: 0x43cfbf, 34 FirstLBA: 0x22, 35 LastLBA: 0x43cf9e, 36 DiskGUID: GUID{L: 0xbad41e2d, W1: 0x93ef, W2: 0xb04a, B: [8]byte{0x84, 0x6e, 0x2e, 0x3a, 0x6a, 0x2d, 0x73, 0xbf}}, 37 PartStart: 2, 38 NPart: MaxNPart, 39 PartSize: 0x80, // This is not constant, but was used for this chromeos disk. 40 PartCRC: 0x8d728e57, 41 } 42 disk = make([]byte, 0x100000000) 43 ) 44 45 func InstallGPT() { 46 for i, d := range block { 47 copy(disk[i:], d) 48 } 49 } 50 51 // GPT is GUID Partition Table, so technically, this test name is 52 // Test Guid Partition Table Table. :-) 53 func TestGPTTable(t *testing.T) { 54 tests := []struct { 55 mangle int 56 msg string 57 }{ 58 {-1, ""}, 59 {0x8, "Primary GPT revision (100ff) is not supported value (10000)"}, 60 {0x0, "Primary GPT signature invalid (54524150204946ff), needs to be 5452415020494645"}, 61 {0xf, "Primary GPT HeaderSize (ff00005c) is not supported value (5c)"}, 62 {0x51, "Primary GPT MaxNPart (ff80) is above maximum of 80"}, 63 {0x59, "Primary Partition CRC: Header {\n\t\"Signature\": 6075990659671082565,\n\t\"Revision\": 65536,\n\t\"HeaderSize\": 92,\n\t\"CRC\": 575771282,\n\t\"Reserved\": 0,\n\t\"CurrentLBA\": 1,\n\t\"BackupLBA\": 4444095,\n\t\"FirstLBA\": 34,\n\t\"LastLBA\": 4444062,\n\t\"DiskGUID\": {\n\t\t\"L\": 3134463533,\n\t\t\"W1\": 37871,\n\t\t\"W2\": 45130,\n\t\t\"B\": [\n\t\t\t132,\n\t\t\t110,\n\t\t\t46,\n\t\t\t58,\n\t\t\t106,\n\t\t\t45,\n\t\t\t115,\n\t\t\t191\n\t\t]\n\t},\n\t\"PartStart\": 2,\n\t\"NPart\": 128,\n\t\"PartSize\": 128,\n\t\"PartCRC\": 2373123927,\n\t\"Parts\": null\n}, computed checksum is 8d728e57, header has 8d72ff57"}, 64 {0x10, "Primary Header CRC: computed checksum is 22519292, header has 225192ff"}, 65 } 66 67 for _, test := range tests { 68 InstallGPT() 69 if test.mangle > -1 { 70 disk[BlockSize+test.mangle] = 0xff 71 } 72 r := bytes.NewReader(disk) 73 g, err := Table(r, BlockSize) 74 if err != nil { 75 if err.Error() != test.msg { 76 t.Errorf("New GPT: got %q, want %q", err, test.msg) 77 continue 78 } 79 t.Logf("Got expected error %q", err) 80 continue 81 } 82 83 if err == nil && test.msg != "" { 84 t.Errorf("New GPT: got nil, want %s", test.msg) 85 continue 86 } 87 88 if !reflect.DeepEqual(header, g.Header) { 89 t.Errorf("Check GUID equality from\n%v to\n%v: got false, want true", header, g.Header) 90 continue 91 } 92 } 93 } 94 95 // TestGPTTtables tests whether we can match the primary and backup 96 // or, if they differ, we catch that error. 97 // We know from other tests that the tables read fine. 98 // This test verifies that they match and that therefore we 99 // are able to read the backup table and test that it is ok. 100 func TestGPTTables(t *testing.T) { 101 tests := []struct { 102 mangle int 103 what string 104 }{ 105 {-1, "No error test"}, 106 {0x10, "Should differ test"}, 107 } 108 109 for _, test := range tests { 110 InstallGPT() 111 if test.mangle > -1 { 112 disk[BlockSize+test.mangle] = 0xff 113 } 114 r := bytes.NewReader(disk) 115 _, err := New(r) 116 switch { 117 case err != nil && test.mangle > -1: 118 t.Logf("Got expected error %s", test.what) 119 case err != nil && test.mangle == -1: 120 t.Errorf("%s: got %s, want nil", test.what, err) 121 continue 122 case err == nil && test.mangle > -1: 123 t.Errorf("%s: got nil, want err", test.what) 124 continue 125 } 126 t.Logf("Passed %s", test.what) 127 } 128 } 129 130 // TestEqualHeader tests all variations of headers not being equal. 131 // We test to make sure it works, then break some aspect of the header 132 // and test that too. 133 func TestEqualHeader(t *testing.T) { 134 InstallGPT() 135 r := bytes.NewReader(disk) 136 p, err := New(r) 137 if err != nil { 138 t.Fatalf("TestEqualHeader: Reading in gpt: got %v, want nil", err) 139 } 140 141 if err := EqualHeader(p.Primary.Header, p.Backup.Header); err != nil { 142 t.Fatalf("TestEqualHeader: got %v, want nil", err) 143 } 144 // Yes, we assume a certain order, but it sure simplifies the test :-) 145 p.Primary.Signature++ 146 p.Primary.Revision++ 147 p.Primary.HeaderSize++ 148 p.Primary.CurrentLBA++ 149 p.Primary.FirstLBA++ 150 p.Primary.LastLBA++ 151 p.Primary.DiskGUID.B[0]++ 152 p.Primary.NPart-- 153 p.Primary.PartSize-- 154 p.Primary.PartCRC++ 155 if err = EqualHeader(p.Primary.Header, p.Backup.Header); err == nil { 156 t.Fatalf("TestEqualHeader: got %v, want nil", err) 157 } 158 t.Logf("TestEqualHeader: EqualHeader returns %v", err) 159 160 if err.Error() != equalHeaderError { 161 t.Fatalf("TestEqualHeader: got %v, want %v", err.Error(), equalHeaderError) 162 } 163 } 164 165 func TestEqualParts(t *testing.T) { 166 InstallGPT() 167 r := bytes.NewReader(disk) 168 p, err := New(r) 169 if err != nil { 170 t.Fatalf("TestEqualParts: Reading in gpt: got %v, want nil", err) 171 } 172 173 if err = EqualParts(p.Primary, p.Backup); err != nil { 174 t.Fatalf("TestEqualParts: Checking equality: got %v, want nil", err) 175 } 176 // Test some equality things before we do the 'length is the same' test 177 // Note that testing the NParts header variable is done in the HeaderTest 178 p.Primary.Parts[3].PartGUID.B[0]++ 179 p.Primary.Parts[8].UniqueGUID.B[1]++ 180 p.Primary.Parts[11].FirstLBA++ 181 p.Primary.Parts[21].LastLBA++ 182 p.Primary.Parts[53].Attribute++ 183 p.Primary.Parts[61].Name[1]++ 184 if err = EqualParts(p.Primary, p.Backup); err == nil { 185 t.Errorf("TestEqualParts: Checking equality: got nil, want '%v'", equalPartsError) 186 } 187 if err.Error() != equalPartsError { 188 t.Errorf("TestEqualParts: Checking equality: got '%v', want '%v'", err, equalPartsError) 189 } 190 191 if err = EqualParts(p.Primary, p.Backup); err == nil { 192 t.Errorf("TestEqualParts: Checking number of parts: got nil, want 'Primary Number of partitions (127) differs from Backup (128)'") 193 } 194 } 195 196 // writeLog is a history of []byte written to the iodisk. Each write to iodisk creates a new writeLog entry. 197 type writeLog [][]byte 198 199 // iodisk is a fake disk that is used for testing. 200 // Each write is logged into the `writes` map. 201 // iodisk implements the WriterAt interface and can be passed to Write() for testing. 202 type iodisk struct { 203 bytes []byte 204 205 // mapping of address=>writes. 206 // This is used for verifying that the correct data was written into the correct locations. 207 writes map[int64]writeLog 208 } 209 210 func newIOdisk(size int) *iodisk { 211 return &iodisk{ 212 bytes: make([]byte, size), 213 writes: make(map[int64]writeLog), 214 } 215 } 216 217 func (d *iodisk) WriteAt(b []byte, offset int64) (int, error) { 218 copy([]byte(d.bytes)[offset:], b) 219 d.writes[offset] = append(d.writes[offset], b) 220 return len(b), nil 221 } 222 223 func TestWrite(t *testing.T) { 224 InstallGPT() 225 r := bytes.NewReader(disk) 226 p, err := New(r) 227 if err != nil { 228 t.Fatalf("Reading partitions: got %v, want nil", err) 229 } 230 targ := newIOdisk(len(disk)) 231 232 if err := Write(targ, p); err != nil { 233 t.Fatalf("Writing: got %v, want nil", err) 234 } 235 if n, err := New(bytes.NewReader([]byte(targ.bytes))); err != nil { 236 t.Logf("Old GPT: %s", p.Primary) 237 var b bytes.Buffer 238 w := hex.Dumper(&b) 239 io.Copy(w, bytes.NewReader(disk[:4096])) 240 t.Logf("%s\n", b.String()) 241 t.Fatalf("Reading back new header: new:%s\n%v", n, err) 242 } 243 244 tests := []struct { 245 desc string 246 offset int64 247 size int64 248 }{ 249 { 250 desc: "Protective MBR", 251 offset: 0x00000000, 252 size: BlockSize, 253 }, 254 { 255 desc: "Primary GPT header", 256 offset: 0x00000200, 257 size: BlockSize, 258 }, 259 { 260 desc: "Backup GPT header", 261 offset: 0x879f7e00, 262 size: BlockSize, 263 }, 264 } 265 266 for _, tc := range tests { 267 t.Run(tc.desc, func(t *testing.T) { 268 // Verify that there was exactly one write. 269 if count := len(targ.writes[tc.offset]); count != 1 { 270 t.Fatalf("Expected exactly 1 write to address 0x%08x, got %d", tc.offset, count) 271 } 272 // Verify that the contents were exactly as expected. 273 if !cmp.Equal(targ.writes[tc.offset][0], disk[tc.offset:tc.offset+tc.size]) { 274 t.Fatalf("Data written to 0x%08x does not match the source data", tc.offset) 275 } 276 }) 277 } 278 }