github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/bootloader/lkenv/lkenv_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package lkenv_test 21 22 import ( 23 "bytes" 24 "compress/gzip" 25 "encoding/binary" 26 "fmt" 27 "hash/crc32" 28 "io" 29 "io/ioutil" 30 "os" 31 "path/filepath" 32 "testing" 33 34 "golang.org/x/xerrors" 35 . "gopkg.in/check.v1" 36 37 "github.com/snapcore/snapd/boot" 38 "github.com/snapcore/snapd/bootloader/lkenv" 39 "github.com/snapcore/snapd/logger" 40 "github.com/snapcore/snapd/testutil" 41 ) 42 43 // Hook up check.v1 into the "go test" runner 44 func Test(t *testing.T) { TestingT(t) } 45 46 type lkenvTestSuite struct { 47 envPath string 48 envPathbak string 49 } 50 51 var _ = Suite(&lkenvTestSuite{}) 52 53 var ( 54 lkversions = []lkenv.Version{ 55 lkenv.V1, 56 lkenv.V2Run, 57 lkenv.V2Recovery, 58 } 59 ) 60 61 func (l *lkenvTestSuite) SetUpTest(c *C) { 62 l.envPath = filepath.Join(c.MkDir(), "snapbootsel.bin") 63 l.envPathbak = l.envPath + "bak" 64 } 65 66 // unpack test data packed with gzip 67 func unpackTestData(data []byte) (resData []byte, err error) { 68 b := bytes.NewBuffer(data) 69 var r io.Reader 70 r, err = gzip.NewReader(b) 71 if err != nil { 72 return 73 } 74 var env bytes.Buffer 75 _, err = env.ReadFrom(r) 76 if err != nil { 77 return 78 } 79 return env.Bytes(), nil 80 } 81 82 func (l *lkenvTestSuite) TestCtoGoString(c *C) { 83 for _, t := range []struct { 84 input []byte 85 expected string 86 }{ 87 {[]byte{0, 0, 0, 0, 0}, ""}, 88 {[]byte{'a', 0, 0, 0, 0}, "a"}, 89 {[]byte{'a', 'b', 0, 0, 0}, "ab"}, 90 {[]byte{'a', 'b', 'c', 0, 0}, "abc"}, 91 {[]byte{'a', 'b', 'c', 'd', 0}, "abcd"}, 92 // no trailing \0 - assume corrupted "" ? 93 {[]byte{'a', 'b', 'c', 'd', 'e'}, ""}, 94 // first \0 is the cutof 95 {[]byte{'a', 'b', 0, 'z', 0}, "ab"}, 96 } { 97 c.Check(lkenv.CToGoString(t.input), Equals, t.expected) 98 } 99 } 100 101 func (l *lkenvTestSuite) TestCopyStringHappy(c *C) { 102 for _, t := range []struct { 103 input string 104 expected []byte 105 }{ 106 // input up to the size of the buffer works 107 {"", []byte{0, 0, 0, 0, 0}}, 108 {"a", []byte{'a', 0, 0, 0, 0}}, 109 {"ab", []byte{'a', 'b', 0, 0, 0}}, 110 {"abc", []byte{'a', 'b', 'c', 0, 0}}, 111 {"abcd", []byte{'a', 'b', 'c', 'd', 0}}, 112 // only what fit is copied 113 {"abcde", []byte{'a', 'b', 'c', 'd', 0}}, 114 {"abcdef", []byte{'a', 'b', 'c', 'd', 0}}, 115 // strange embedded stuff works 116 {"ab\000z", []byte{'a', 'b', 0, 'z', 0}}, 117 } { 118 b := make([]byte, 5) 119 lkenv.CopyString(b, t.input) 120 c.Check(b, DeepEquals, t.expected) 121 } 122 } 123 124 func (l *lkenvTestSuite) TestCopyStringNoPanic(c *C) { 125 // too long, string should get concatenate 126 b := make([]byte, 5) 127 defer lkenv.CopyString(b, "12345") 128 c.Assert(recover(), IsNil) 129 defer lkenv.CopyString(b, "123456") 130 c.Assert(recover(), IsNil) 131 } 132 133 func (l *lkenvTestSuite) TestGetBootImageName(c *C) { 134 for _, version := range lkversions { 135 for _, setValue := range []bool{true, false} { 136 env := lkenv.NewEnv(l.envPath, "", version) 137 c.Check(env, NotNil) 138 139 if setValue { 140 env.Set("bootimg_file_name", "some-boot-image-name") 141 } 142 143 name := env.GetBootImageName() 144 145 if setValue { 146 c.Assert(name, Equals, "some-boot-image-name") 147 } else { 148 c.Assert(name, Equals, "boot.img") 149 } 150 } 151 } 152 } 153 154 func (l *lkenvTestSuite) TestSet(c *C) { 155 tt := []struct { 156 version lkenv.Version 157 key string 158 val string 159 }{ 160 { 161 lkenv.V1, 162 "snap_mode", 163 boot.TryStatus, 164 }, 165 { 166 lkenv.V2Run, 167 "kernel_status", 168 boot.TryingStatus, 169 }, 170 { 171 lkenv.V2Recovery, 172 "snapd_recovery_mode", 173 "recover", 174 }, 175 } 176 for _, t := range tt { 177 env := lkenv.NewEnv(l.envPath, "", t.version) 178 c.Check(env, NotNil) 179 env.Set(t.key, t.val) 180 c.Check(env.Get(t.key), Equals, t.val) 181 } 182 } 183 184 func (l *lkenvTestSuite) TestSave(c *C) { 185 tt := []struct { 186 version lkenv.Version 187 keyValuePairs map[string]string 188 comment string 189 }{ 190 { 191 lkenv.V1, 192 map[string]string{ 193 "snap_mode": boot.TryingStatus, 194 "snap_kernel": "kernel-1", 195 "snap_try_kernel": "kernel-2", 196 "snap_core": "core-1", 197 "snap_try_core": "core-2", 198 "snap_gadget": "gadget-1", 199 "snap_try_gadget": "gadget-2", 200 "bootimg_file_name": "boot.img", 201 }, 202 "lkenv v1", 203 }, 204 { 205 lkenv.V2Run, 206 map[string]string{ 207 "kernel_status": boot.TryStatus, 208 "snap_kernel": "kernel-1", 209 "snap_try_kernel": "kernel-2", 210 "snap_gadget": "gadget-1", 211 "snap_try_gadget": "gadget-2", 212 "bootimg_file_name": "boot.img", 213 }, 214 "lkenv v2 run", 215 }, 216 { 217 lkenv.V2Recovery, 218 map[string]string{ 219 "snapd_recovery_mode": "recover", 220 "snapd_recovery_system": "11192020", 221 "bootimg_file_name": "boot.img", 222 "try_recovery_system": "1234", 223 "recovery_system_status": "tried", 224 }, 225 "lkenv v2 recovery", 226 }, 227 } 228 for _, t := range tt { 229 for _, makeBackup := range []bool{true, false} { 230 var comment CommentInterface 231 if makeBackup { 232 comment = Commentf("testcase %s with backup", t.comment) 233 } else { 234 comment = Commentf("testcase %s without backup", t.comment) 235 } 236 237 loggerBuf, restore := logger.MockLogger() 238 defer restore() 239 240 // make unique files per test case 241 testFile := filepath.Join(c.MkDir(), "lk.bin") 242 testFileBackup := testFile + "bak" 243 if makeBackup { 244 // create the backup file too 245 buf := make([]byte, 4096) 246 err := ioutil.WriteFile(testFileBackup, buf, 0644) 247 c.Assert(err, IsNil, comment) 248 } 249 250 buf := make([]byte, 4096) 251 err := ioutil.WriteFile(testFile, buf, 0644) 252 c.Assert(err, IsNil, comment) 253 254 env := lkenv.NewEnv(testFile, "", t.version) 255 c.Check(env, NotNil, comment) 256 257 for k, v := range t.keyValuePairs { 258 env.Set(k, v) 259 } 260 261 err = env.Save() 262 c.Assert(err, IsNil, comment) 263 264 env2 := lkenv.NewEnv(testFile, "", t.version) 265 err = env2.Load() 266 c.Assert(err, IsNil, comment) 267 268 for k, v := range t.keyValuePairs { 269 c.Check(env2.Get(k), Equals, v, comment) 270 } 271 272 // check the backup too 273 if makeBackup { 274 env3 := lkenv.NewEnv(testFileBackup, "", t.version) 275 err := env3.Load() 276 c.Assert(err, IsNil, comment) 277 278 for k, v := range t.keyValuePairs { 279 c.Check(env3.Get(k), Equals, v, comment) 280 } 281 282 // corrupt the main file and then try to load it - we should 283 // automatically fallback to the backup file since the backup 284 // file will not be corrupt 285 buf := make([]byte, 4096) 286 f, err := os.OpenFile(testFile, os.O_WRONLY, 0644) 287 c.Assert(err, IsNil) 288 _, err = io.Copy(f, bytes.NewBuffer(buf)) 289 c.Assert(err, IsNil, comment) 290 291 env4 := lkenv.NewEnv(testFile, "", t.version) 292 err = env4.Load() 293 c.Assert(err, IsNil, comment) 294 295 for k, v := range t.keyValuePairs { 296 c.Check(env4.Get(k), Equals, v, comment) 297 } 298 299 // we should have also had a logged message about being unable 300 // to load the main file 301 c.Assert(loggerBuf.String(), testutil.Contains, fmt.Sprintf("cannot load primary bootloader environment: cannot validate %s:", testFile)) 302 } 303 } 304 } 305 } 306 307 func (l *lkenvTestSuite) TestLoadValidatesCRC32(c *C) { 308 for _, version := range lkversions { 309 testFile := filepath.Join(c.MkDir(), "lk.bin") 310 311 // make an out of band lkenv object and set the wrong signature to be 312 // able to export it to a file 313 var rawStruct interface{} 314 switch version { 315 case lkenv.V1: 316 rawStruct = lkenv.SnapBootSelect_v1{ 317 Version: version.Number(), 318 Signature: version.Signature(), 319 } 320 case lkenv.V2Run: 321 rawStruct = lkenv.SnapBootSelect_v2_run{ 322 Version: version.Number(), 323 Signature: version.Signature(), 324 } 325 case lkenv.V2Recovery: 326 rawStruct = lkenv.SnapBootSelect_v2_recovery{ 327 Version: version.Number(), 328 Signature: version.Signature(), 329 } 330 } 331 332 buf := bytes.NewBuffer(nil) 333 ss := binary.Size(rawStruct) 334 buf.Grow(ss) 335 err := binary.Write(buf, binary.LittleEndian, rawStruct) 336 c.Assert(err, IsNil) 337 338 // calculate the expected checksum but don't put it into the object when 339 // we write it out so that the checksum is invalid 340 expCrc32 := crc32.ChecksumIEEE(buf.Bytes()[:ss-4]) 341 342 err = ioutil.WriteFile(testFile, buf.Bytes(), 0644) 343 c.Assert(err, IsNil) 344 345 // now try importing the file with LoadEnv() 346 env := lkenv.NewEnv(testFile, "", version) 347 c.Assert(env, NotNil) 348 349 err = env.LoadEnv(testFile) 350 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot validate %s: expected checksum 0x%X, got 0x%X", testFile, expCrc32, 0)) 351 } 352 353 } 354 355 func (l *lkenvTestSuite) TestNewBackupFileLocation(c *C) { 356 // creating with the second argument as the empty string falls back to 357 // the main path + "bak" 358 for _, version := range lkversions { 359 logbuf, restore := logger.MockLogger() 360 defer restore() 361 362 testFile := filepath.Join(c.MkDir(), "lk.bin") 363 c.Assert(testFile, testutil.FileAbsent) 364 c.Assert(testFile+"bak", testutil.FileAbsent) 365 // make empty files for Save() to overwrite 366 err := ioutil.WriteFile(testFile, nil, 0644) 367 c.Assert(err, IsNil) 368 err = ioutil.WriteFile(testFile+"bak", nil, 0644) 369 c.Assert(err, IsNil) 370 env := lkenv.NewEnv(testFile, "", version) 371 c.Assert(env, NotNil) 372 err = env.Save() 373 c.Assert(err, IsNil) 374 375 // make sure both the primary and backup files were written and can be 376 // successfully loaded 377 env2 := lkenv.NewEnv(testFile, "", version) 378 err = env2.Load() 379 c.Assert(err, IsNil) 380 381 env3 := lkenv.NewEnv(testFile+"bak", "", version) 382 err = env3.Load() 383 c.Assert(err, IsNil) 384 385 // no messages logged 386 c.Assert(logbuf.String(), Equals, "") 387 } 388 389 // now specify a different backup file location 390 for _, version := range lkversions { 391 logbuf, restore := logger.MockLogger() 392 defer restore() 393 testFile := filepath.Join(c.MkDir(), "lk.bin") 394 testFileBackup := filepath.Join(c.MkDir(), "lkbackup.bin") 395 err := ioutil.WriteFile(testFile, nil, 0644) 396 c.Assert(err, IsNil) 397 err = ioutil.WriteFile(testFileBackup, nil, 0644) 398 c.Assert(err, IsNil) 399 400 env := lkenv.NewEnv(testFile, testFileBackup, version) 401 c.Assert(env, NotNil) 402 err = env.Save() 403 c.Assert(err, IsNil) 404 405 // make sure both the primary and backup files were written and can be 406 // successfully loaded 407 env2 := lkenv.NewEnv(testFile, "", version) 408 err = env2.Load() 409 c.Assert(err, IsNil) 410 411 env3 := lkenv.NewEnv(testFileBackup, "", version) 412 err = env3.Load() 413 c.Assert(err, IsNil) 414 415 // no "bak" files present 416 c.Assert(testFile+"bak", testutil.FileAbsent) 417 c.Assert(testFileBackup+"bak", testutil.FileAbsent) 418 419 // no messages logged 420 c.Assert(logbuf.String(), Equals, "") 421 } 422 } 423 424 func (l *lkenvTestSuite) TestLoadValidatesVersionSignatureConsistency(c *C) { 425 426 tt := []struct { 427 version lkenv.Version 428 binVersion uint32 429 binSignature uint32 430 validateFailMode string 431 }{ 432 { 433 lkenv.V1, 434 lkenv.V2Recovery.Number(), 435 lkenv.V1.Signature(), 436 "version", 437 }, 438 { 439 lkenv.V1, 440 lkenv.V1.Number(), 441 lkenv.V2Recovery.Signature(), 442 "signature", 443 }, 444 { 445 lkenv.V2Run, 446 lkenv.V1.Number(), 447 lkenv.V2Run.Signature(), 448 "version", 449 }, 450 { 451 lkenv.V2Run, 452 lkenv.V2Run.Number(), 453 lkenv.V2Recovery.Signature(), 454 "signature", 455 }, 456 { 457 lkenv.V2Recovery, 458 lkenv.V1.Number(), 459 lkenv.V2Recovery.Signature(), 460 "version", 461 }, 462 { 463 lkenv.V2Recovery, 464 lkenv.V2Recovery.Number(), 465 lkenv.V2Run.Signature(), 466 "signature", 467 }, 468 } 469 470 for _, t := range tt { 471 testFile := filepath.Join(c.MkDir(), "lk.bin") 472 473 // make an out of band lkenv object and set the wrong signature to be 474 // able to export it to a file 475 var rawStruct interface{} 476 switch t.version { 477 case lkenv.V1: 478 rawStruct = lkenv.SnapBootSelect_v1{ 479 Version: t.binVersion, 480 Signature: t.binSignature, 481 } 482 case lkenv.V2Run: 483 rawStruct = lkenv.SnapBootSelect_v2_run{ 484 Version: t.binVersion, 485 Signature: t.binSignature, 486 } 487 case lkenv.V2Recovery: 488 rawStruct = lkenv.SnapBootSelect_v2_recovery{ 489 Version: t.binVersion, 490 Signature: t.binSignature, 491 } 492 } 493 494 buf := bytes.NewBuffer(nil) 495 ss := binary.Size(rawStruct) 496 buf.Grow(ss) 497 err := binary.Write(buf, binary.LittleEndian, rawStruct) 498 c.Assert(err, IsNil) 499 500 // calculate crc32 501 newCrc32 := crc32.ChecksumIEEE(buf.Bytes()[:ss-4]) 502 // note for efficiency's sake to avoid re-writing the whole structure, 503 // we re-write _just_ the crc32 to w as little-endian 504 buf.Truncate(ss - 4) 505 binary.Write(buf, binary.LittleEndian, &newCrc32) 506 507 err = ioutil.WriteFile(testFile, buf.Bytes(), 0644) 508 c.Assert(err, IsNil) 509 510 // now try importing the file with LoadEnv() 511 env := lkenv.NewEnv(testFile, "", t.version) 512 c.Assert(env, NotNil) 513 514 var expNum, gotNum uint32 515 switch t.validateFailMode { 516 case "signature": 517 expNum = t.version.Signature() 518 gotNum = t.binSignature 519 case "version": 520 expNum = t.version.Number() 521 gotNum = t.binVersion 522 } 523 expErr := fmt.Sprintf( 524 "cannot validate %s: expected %s 0x%X, got 0x%X", 525 testFile, 526 t.validateFailMode, 527 expNum, 528 gotNum, 529 ) 530 531 err = env.LoadEnv(testFile) 532 c.Assert(err, ErrorMatches, expErr) 533 } 534 } 535 536 func (l *lkenvTestSuite) TestLoadPropagatesErrNotExist(c *C) { 537 // make sure that if the env file doesn't exist, the error returned from 538 // Load() is os.ErrNotExist, even if it isn't exactly that 539 env := lkenv.NewEnv("some-nonsense-file-this-doesnt-exist", "", lkenv.V1) 540 c.Check(env, NotNil) 541 542 err := env.Load() 543 c.Assert(xerrors.Is(err, os.ErrNotExist), Equals, true, Commentf("err is %+v", err)) 544 c.Assert(err, ErrorMatches, "cannot open LK env file: open some-nonsense-file-this-doesnt-existbak: no such file or directory") 545 } 546 547 func (l *lkenvTestSuite) TestLoad(c *C) { 548 for _, version := range lkversions { 549 for _, makeBackup := range []bool{true, false} { 550 loggerBuf, restore := logger.MockLogger() 551 defer restore() 552 // make unique files per test case 553 testFile := filepath.Join(c.MkDir(), "lk.bin") 554 testFileBackup := testFile + "bak" 555 if makeBackup { 556 buf := make([]byte, 100000) 557 err := ioutil.WriteFile(testFileBackup, buf, 0644) 558 c.Assert(err, IsNil) 559 } 560 561 buf := make([]byte, 100000) 562 err := ioutil.WriteFile(testFile, buf, 0644) 563 c.Assert(err, IsNil) 564 565 // create an env for this file and try to load it 566 env := lkenv.NewEnv(testFile, "", version) 567 c.Check(env, NotNil) 568 569 err = env.Load() 570 // possible error messages could be "cannot open LK env file: ..." 571 // or "cannot valid <file>: ..." 572 if makeBackup { 573 // here we will read the backup file which exists but like the 574 // primary file is corrupted 575 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot validate %s: expected version 0x%X, got 0x0", testFileBackup, version.Number())) 576 } else { 577 // here we fail to read the normal file, and automatically try 578 // to read the backup, but fail because it doesn't exist 579 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot open LK env file: open %s: no such file or directory", testFileBackup)) 580 } 581 582 c.Assert(loggerBuf.String(), testutil.Contains, fmt.Sprintf("cannot load primary bootloader environment: cannot validate %s:", testFile)) 583 c.Assert(loggerBuf.String(), testutil.Contains, "attempting to load backup bootloader environment") 584 } 585 } 586 } 587 588 func (l *lkenvTestSuite) TestGetAndSetAndFindBootPartition(c *C) { 589 tt := []struct { 590 version lkenv.Version 591 // use slices instead of a map since we need a consistent ordering 592 bootMatrixKeys []string 593 bootMatrixValues []string 594 matrixType string 595 comment string 596 }{ 597 { 598 lkenv.V1, 599 []string{ 600 "boot_a", 601 "boot_b", 602 }, 603 []string{ 604 "kernel-1", 605 "kernel-2", 606 }, 607 "kernel", 608 "v1", 609 }, 610 { 611 lkenv.V2Run, 612 []string{ 613 "boot_a", 614 "boot_b", 615 }, 616 []string{ 617 "kernel-1", 618 "kernel-2", 619 }, 620 "kernel", 621 "v2 run", 622 }, 623 { 624 lkenv.V2Recovery, 625 []string{ 626 "boot_recovery_1", 627 }, 628 []string{ 629 "20201123", 630 }, 631 "recovery-system", 632 "v2 recovery 1 slot", 633 }, 634 { 635 lkenv.V2Recovery, 636 []string{ 637 "boot_recovery_1", 638 "boot_recovery_2", 639 }, 640 []string{ 641 "20201123", 642 "20201124", 643 }, 644 "recovery-system", 645 "v2 recovery 2 slots", 646 }, 647 { 648 lkenv.V2Recovery, 649 []string{ 650 "boot_recovery_1", 651 "boot_recovery_2", 652 "boot_recovery_3", 653 }, 654 []string{ 655 "20201123", 656 "20201124", 657 "20201125", 658 }, 659 "recovery-system", 660 "v2 recovery 3 slots", 661 }, 662 { 663 lkenv.V2Recovery, 664 []string{ 665 "boot_recovery_1", 666 "boot_recovery_2", 667 "boot_recovery_3", 668 "boot_recovery_4", 669 "boot_recovery_5", 670 "boot_recovery_6", 671 "boot_recovery_7", 672 "boot_recovery_8", 673 "boot_recovery_9", 674 "boot_recovery_10", 675 }, 676 []string{ 677 "20201123", 678 "20201124", 679 "20201125", 680 "20201126", 681 "20201127", 682 "20201128", 683 "20201129", 684 "20201130", 685 "20201131", 686 "20201132", 687 }, 688 "recovery-system", 689 "v2 recovery max slots", 690 }, 691 } 692 693 for _, t := range tt { 694 comment := Commentf(t.comment) 695 // make sure the key and values are the same length for test case 696 // consistency check 697 c.Assert(t.bootMatrixKeys, HasLen, len(t.bootMatrixValues), comment) 698 699 buf := make([]byte, 4096) 700 err := ioutil.WriteFile(l.envPath, buf, 0644) 701 c.Assert(err, IsNil, comment) 702 703 env := lkenv.NewEnv(l.envPath, "", t.version) 704 c.Assert(env, Not(IsNil), comment) 705 706 var findFunc func(string) (string, error) 707 var setFunc func(string, string) error 708 var getFunc func(string) (string, error) 709 var deleteFunc func(string) error 710 switch t.matrixType { 711 case "recovery-system": 712 findFunc = func(s string) (string, error) { return env.FindFreeRecoverySystemBootPartition(s) } 713 setFunc = func(s1, s2 string) error { return env.SetBootPartitionRecoverySystem(s1, s2) } 714 getFunc = func(s1 string) (string, error) { return env.GetRecoverySystemBootPartition(s1) } 715 deleteFunc = func(s1 string) error { return env.RemoveRecoverySystemFromBootPartition(s1) } 716 case "kernel": 717 findFunc = func(s string) (string, error) { return env.FindFreeKernelBootPartition(s) } 718 setFunc = func(s1, s2 string) error { 719 // for assigning the kernel, we need to also set the 720 // snap_kernel, since that is used to detect if we should return 721 // an unset variable or not 722 723 err := env.SetBootPartitionKernel(s1, s2) 724 c.Assert(err, IsNil, comment) 725 if err != nil { 726 return err 727 } 728 if env.Get("snap_kernel") == "" { 729 // only set it the first time so that the delete logic test 730 // works and we only set the first kernel to be snap_kernel 731 env.Set("snap_kernel", s2) 732 } 733 return nil 734 } 735 getFunc = func(s1 string) (string, error) { return env.GetKernelBootPartition(s1) } 736 deleteFunc = func(s1 string) error { return env.RemoveKernelFromBootPartition(s1) } 737 default: 738 c.Errorf("unexpected matrix type, test setup broken (%s)", comment) 739 } 740 741 err = env.InitializeBootPartitions(t.bootMatrixKeys...) 742 c.Assert(err, IsNil, comment) 743 744 // before assigning any values to the boot matrix, check that all 745 // values we try to assign would go to the first bootPartLabel 746 for _, bootPartValue := range t.bootMatrixKeys { 747 // we haven't assigned anything yet, so all values should get mapped 748 // to the first boot image partition 749 bootPartFound, err := findFunc(bootPartValue) 750 c.Assert(err, IsNil, comment) 751 c.Assert(bootPartFound, Equals, t.bootMatrixKeys[0], comment) 752 } 753 754 // now go and assign them, checking that along the way we are assigning 755 // to the next slot 756 // iterate over the key list to keep the same order 757 for i, bootPart := range t.bootMatrixKeys { 758 bootPartValue := t.bootMatrixValues[i] 759 // now we will be assigning things, so we should check that the 760 // assigned boot image partition matches what we expect 761 bootPartFound, err := findFunc(bootPartValue) 762 c.Assert(err, IsNil, comment) 763 c.Assert(bootPartFound, Equals, bootPart, comment) 764 765 err = setFunc(bootPart, bootPartValue) 766 c.Assert(err, IsNil, comment) 767 768 // now check that it has the right value 769 val, err := getFunc(bootPartValue) 770 c.Assert(err, IsNil, comment) 771 c.Assert(val, Equals, bootPart, comment) 772 773 // double-check that finding a free slot for this value returns the 774 // existing slot - this logic specifically is important for uc16 and 775 // uc18 where during seeding we will end up extracting a kernel to 776 // the already extracted slot (since the kernel will already have 777 // been extracted during image build time) 778 bootPartFound2, err := findFunc(bootPartValue) 779 c.Assert(err, IsNil, comment) 780 c.Assert(bootPartFound2, Equals, bootPart, comment) 781 } 782 783 // now check that trying to find a free slot for a new recovery system 784 // fails because we are full 785 if t.matrixType == "recovery-system" { 786 thing, err := findFunc("some-random-value") 787 c.Check(thing, Equals, "") 788 c.Assert(err, ErrorMatches, "cannot find free boot image partition", comment) 789 } 790 791 // test that removing the last one works 792 lastIndex := len(t.bootMatrixValues) - 1 793 lastValue := t.bootMatrixValues[lastIndex] 794 lastKey := t.bootMatrixKeys[lastIndex] 795 err = deleteFunc(lastValue) 796 c.Assert(err, IsNil, comment) 797 798 // trying to delete again will fail since it won't exist 799 err = deleteFunc(lastValue) 800 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot find %q in boot image partitions", lastValue), comment) 801 802 // trying to find it will return the last slot 803 slot, err := findFunc(lastValue) 804 c.Assert(err, IsNil, comment) 805 c.Assert(slot, Equals, lastKey, comment) 806 } 807 } 808 809 func (l *lkenvTestSuite) TestV1NoRecoverySystemSupport(c *C) { 810 env := lkenv.NewEnv(l.envPath, "", lkenv.V1) 811 c.Assert(env, NotNil) 812 813 _, err := env.FindFreeRecoverySystemBootPartition("blah") 814 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 815 816 err = env.SetBootPartitionRecoverySystem("blah", "blah") 817 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 818 819 _, err = env.GetRecoverySystemBootPartition("blah") 820 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 821 822 err = env.RemoveRecoverySystemFromBootPartition("blah") 823 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 824 } 825 826 func (l *lkenvTestSuite) TestV2RunNoRecoverySystemSupport(c *C) { 827 env := lkenv.NewEnv(l.envPath, "", lkenv.V2Run) 828 c.Assert(env, NotNil) 829 830 _, err := env.FindFreeRecoverySystemBootPartition("blah") 831 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 832 833 err = env.SetBootPartitionRecoverySystem("blah", "blah") 834 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 835 836 _, err = env.GetRecoverySystemBootPartition("blah") 837 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 838 839 err = env.RemoveRecoverySystemFromBootPartition("blah") 840 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 841 } 842 843 func (l *lkenvTestSuite) TestV2RecoveryNoKernelSupport(c *C) { 844 env := lkenv.NewEnv(l.envPath, "", lkenv.V2Recovery) 845 c.Assert(env, NotNil) 846 847 _, err := env.FindFreeKernelBootPartition("blah") 848 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 849 850 err = env.SetBootPartitionKernel("blah", "blah") 851 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 852 853 _, err = env.GetKernelBootPartition("blah") 854 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 855 856 err = env.RemoveKernelFromBootPartition("blah") 857 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 858 } 859 860 func (l *lkenvTestSuite) TestZippedDataSample(c *C) { 861 // TODO: add binary data test for v2 structures generated with gadget build 862 // tool when it has been updated for v2 863 864 // test data is generated with gadget build helper tool: 865 // $ parts/snap-boot-sel-env/build/lk-boot-env -w test.bin \ 866 // --snap-mode="trying" --snap-kernel="kernel-1" --snap-try-kernel="kernel-2" \ 867 // --snap-core="core-1" --snap-try-core="core-2" --reboot-reason="" \ 868 // --boot-0-part="boot_a" --boot-1-part="boot_b" --boot-0-snap="kernel-1" \ 869 // --boot-1-snap="kernel-3" --bootimg-file="boot.img" 870 // $ cat test.bin | gzip | xxd -i 871 gzipedData := []byte{ 872 0x1f, 0x8b, 0x08, 0x00, 0x95, 0x88, 0x77, 0x5d, 0x00, 0x03, 0xed, 0xd7, 873 0xc1, 0x09, 0xc2, 0x40, 0x10, 0x05, 0xd0, 0xa4, 0x20, 0x05, 0x63, 0x07, 874 0x96, 0xa0, 0x05, 0x88, 0x91, 0x25, 0x04, 0x35, 0x0b, 0x6b, 0x2e, 0x1e, 875 0xac, 0xcb, 0xf6, 0xc4, 0x90, 0x1e, 0x06, 0xd9, 0xf7, 0x2a, 0xf8, 0xc3, 876 0x1f, 0x18, 0xe6, 0x74, 0x78, 0xa6, 0xb6, 0x69, 0x9b, 0xb9, 0xbc, 0xc6, 877 0x69, 0x68, 0xaa, 0x75, 0xcd, 0x25, 0x6d, 0x76, 0xd1, 0x29, 0xe2, 0x2c, 878 0xf3, 0x77, 0xd1, 0x29, 0xe2, 0xdc, 0x52, 0x99, 0xd2, 0xbd, 0xde, 0x0d, 879 0x58, 0xe7, 0xaf, 0x78, 0x03, 0x80, 0x5a, 0xf5, 0x39, 0xcf, 0xe7, 0x4b, 880 0x74, 0x8a, 0x38, 0xb5, 0xdf, 0xbf, 0xa5, 0xff, 0x3e, 0x3a, 0x45, 0x9c, 881 0xb5, 0xff, 0x7d, 0x74, 0x8e, 0x28, 0xbf, 0xfe, 0xb7, 0xe3, 0xa3, 0xe2, 882 0x0f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 883 0xf8, 0x17, 0xc7, 0xf7, 0xa7, 0xfb, 0x02, 0x1c, 0xdf, 0x44, 0x21, 0x0c, 884 0x3a, 0x00, 0x00} 885 886 // uncompress test data to sample env file 887 rawData, err := unpackTestData(gzipedData) 888 c.Assert(err, IsNil) 889 err = ioutil.WriteFile(l.envPath, rawData, 0644) 890 c.Assert(err, IsNil) 891 err = ioutil.WriteFile(l.envPathbak, rawData, 0644) 892 c.Assert(err, IsNil) 893 894 env := lkenv.NewEnv(l.envPath, "", lkenv.V1) 895 c.Check(env, NotNil) 896 err = env.Load() 897 c.Assert(err, IsNil) 898 c.Check(env.Get("snap_mode"), Equals, boot.TryingStatus) 899 c.Check(env.Get("snap_kernel"), Equals, "kernel-1") 900 c.Check(env.Get("snap_try_kernel"), Equals, "kernel-2") 901 c.Check(env.Get("snap_core"), Equals, "core-1") 902 c.Check(env.Get("snap_try_core"), Equals, "core-2") 903 c.Check(env.Get("bootimg_file_name"), Equals, "boot.img") 904 c.Check(env.Get("reboot_reason"), Equals, "") 905 // first partition should be with label 'boot_a' and 'kernel-1' revision 906 p, err := env.GetKernelBootPartition("kernel-1") 907 c.Check(p, Equals, "boot_a") 908 c.Assert(err, IsNil) 909 // test second boot partition is free with label "boot_b" 910 p, err = env.FindFreeKernelBootPartition("kernel-2") 911 c.Check(p, Equals, "boot_b") 912 c.Assert(err, IsNil) 913 }