github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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 }, 223 "lkenv v2 recovery", 224 }, 225 } 226 for _, t := range tt { 227 for _, makeBackup := range []bool{true, false} { 228 var comment CommentInterface 229 if makeBackup { 230 comment = Commentf("testcase %s with backup", t.comment) 231 } else { 232 comment = Commentf("testcase %s without backup", t.comment) 233 } 234 235 loggerBuf, restore := logger.MockLogger() 236 defer restore() 237 238 // make unique files per test case 239 testFile := filepath.Join(c.MkDir(), "lk.bin") 240 testFileBackup := testFile + "bak" 241 if makeBackup { 242 // create the backup file too 243 buf := make([]byte, 4096) 244 err := ioutil.WriteFile(testFileBackup, buf, 0644) 245 c.Assert(err, IsNil, comment) 246 } 247 248 buf := make([]byte, 4096) 249 err := ioutil.WriteFile(testFile, buf, 0644) 250 c.Assert(err, IsNil, comment) 251 252 env := lkenv.NewEnv(testFile, "", t.version) 253 c.Check(env, NotNil, comment) 254 255 for k, v := range t.keyValuePairs { 256 env.Set(k, v) 257 } 258 259 err = env.Save() 260 c.Assert(err, IsNil, comment) 261 262 env2 := lkenv.NewEnv(testFile, "", t.version) 263 err = env2.Load() 264 c.Assert(err, IsNil, comment) 265 266 for k, v := range t.keyValuePairs { 267 c.Check(env2.Get(k), Equals, v, comment) 268 } 269 270 // check the backup too 271 if makeBackup { 272 env3 := lkenv.NewEnv(testFileBackup, "", t.version) 273 err := env3.Load() 274 c.Assert(err, IsNil, comment) 275 276 for k, v := range t.keyValuePairs { 277 c.Check(env3.Get(k), Equals, v, comment) 278 } 279 280 // corrupt the main file and then try to load it - we should 281 // automatically fallback to the backup file since the backup 282 // file will not be corrupt 283 buf := make([]byte, 4096) 284 f, err := os.OpenFile(testFile, os.O_WRONLY, 0644) 285 c.Assert(err, IsNil) 286 _, err = io.Copy(f, bytes.NewBuffer(buf)) 287 c.Assert(err, IsNil, comment) 288 289 env4 := lkenv.NewEnv(testFile, "", t.version) 290 err = env4.Load() 291 c.Assert(err, IsNil, comment) 292 293 for k, v := range t.keyValuePairs { 294 c.Check(env4.Get(k), Equals, v, comment) 295 } 296 297 // we should have also had a logged message about being unable 298 // to load the main file 299 c.Assert(loggerBuf.String(), testutil.Contains, fmt.Sprintf("cannot load primary bootloader environment: cannot validate %s:", testFile)) 300 } 301 } 302 } 303 } 304 305 func (l *lkenvTestSuite) TestLoadValidatesCRC32(c *C) { 306 for _, version := range lkversions { 307 testFile := filepath.Join(c.MkDir(), "lk.bin") 308 309 // make an out of band lkenv object and set the wrong signature to be 310 // able to export it to a file 311 var rawStruct interface{} 312 switch version { 313 case lkenv.V1: 314 rawStruct = lkenv.SnapBootSelect_v1{ 315 Version: version.Number(), 316 Signature: version.Signature(), 317 } 318 case lkenv.V2Run: 319 rawStruct = lkenv.SnapBootSelect_v2_run{ 320 Version: version.Number(), 321 Signature: version.Signature(), 322 } 323 case lkenv.V2Recovery: 324 rawStruct = lkenv.SnapBootSelect_v2_recovery{ 325 Version: version.Number(), 326 Signature: version.Signature(), 327 } 328 } 329 330 buf := bytes.NewBuffer(nil) 331 ss := binary.Size(rawStruct) 332 buf.Grow(ss) 333 err := binary.Write(buf, binary.LittleEndian, rawStruct) 334 c.Assert(err, IsNil) 335 336 // calculate the expected checksum but don't put it into the object when 337 // we write it out so that the checksum is invalid 338 expCrc32 := crc32.ChecksumIEEE(buf.Bytes()[:ss-4]) 339 340 err = ioutil.WriteFile(testFile, buf.Bytes(), 0644) 341 c.Assert(err, IsNil) 342 343 // now try importing the file with LoadEnv() 344 env := lkenv.NewEnv(testFile, "", version) 345 c.Assert(env, NotNil) 346 347 err = env.LoadEnv(testFile) 348 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot validate %s: expected checksum 0x%X, got 0x%X", testFile, expCrc32, 0)) 349 } 350 351 } 352 353 func (l *lkenvTestSuite) TestNewBackupFileLocation(c *C) { 354 // creating with the second argument as the empty string falls back to 355 // the main path + "bak" 356 for _, version := range lkversions { 357 logbuf, restore := logger.MockLogger() 358 defer restore() 359 360 testFile := filepath.Join(c.MkDir(), "lk.bin") 361 c.Assert(testFile, testutil.FileAbsent) 362 c.Assert(testFile+"bak", testutil.FileAbsent) 363 // make empty files for Save() to overwrite 364 err := ioutil.WriteFile(testFile, nil, 0644) 365 c.Assert(err, IsNil) 366 err = ioutil.WriteFile(testFile+"bak", nil, 0644) 367 c.Assert(err, IsNil) 368 env := lkenv.NewEnv(testFile, "", version) 369 c.Assert(env, NotNil) 370 err = env.Save() 371 c.Assert(err, IsNil) 372 373 // make sure both the primary and backup files were written and can be 374 // successfully loaded 375 env2 := lkenv.NewEnv(testFile, "", version) 376 err = env2.Load() 377 c.Assert(err, IsNil) 378 379 env3 := lkenv.NewEnv(testFile+"bak", "", version) 380 err = env3.Load() 381 c.Assert(err, IsNil) 382 383 // no messages logged 384 c.Assert(logbuf.String(), Equals, "") 385 } 386 387 // now specify a different backup file location 388 for _, version := range lkversions { 389 logbuf, restore := logger.MockLogger() 390 defer restore() 391 testFile := filepath.Join(c.MkDir(), "lk.bin") 392 testFileBackup := filepath.Join(c.MkDir(), "lkbackup.bin") 393 err := ioutil.WriteFile(testFile, nil, 0644) 394 c.Assert(err, IsNil) 395 err = ioutil.WriteFile(testFileBackup, nil, 0644) 396 c.Assert(err, IsNil) 397 398 env := lkenv.NewEnv(testFile, testFileBackup, version) 399 c.Assert(env, NotNil) 400 err = env.Save() 401 c.Assert(err, IsNil) 402 403 // make sure both the primary and backup files were written and can be 404 // successfully loaded 405 env2 := lkenv.NewEnv(testFile, "", version) 406 err = env2.Load() 407 c.Assert(err, IsNil) 408 409 env3 := lkenv.NewEnv(testFileBackup, "", version) 410 err = env3.Load() 411 c.Assert(err, IsNil) 412 413 // no "bak" files present 414 c.Assert(testFile+"bak", testutil.FileAbsent) 415 c.Assert(testFileBackup+"bak", testutil.FileAbsent) 416 417 // no messages logged 418 c.Assert(logbuf.String(), Equals, "") 419 } 420 } 421 422 func (l *lkenvTestSuite) TestLoadValidatesVersionSignatureConsistency(c *C) { 423 424 tt := []struct { 425 version lkenv.Version 426 binVersion uint32 427 binSignature uint32 428 validateFailMode string 429 }{ 430 { 431 lkenv.V1, 432 lkenv.V2Recovery.Number(), 433 lkenv.V1.Signature(), 434 "version", 435 }, 436 { 437 lkenv.V1, 438 lkenv.V1.Number(), 439 lkenv.V2Recovery.Signature(), 440 "signature", 441 }, 442 { 443 lkenv.V2Run, 444 lkenv.V1.Number(), 445 lkenv.V2Run.Signature(), 446 "version", 447 }, 448 { 449 lkenv.V2Run, 450 lkenv.V2Run.Number(), 451 lkenv.V2Recovery.Signature(), 452 "signature", 453 }, 454 { 455 lkenv.V2Recovery, 456 lkenv.V1.Number(), 457 lkenv.V2Recovery.Signature(), 458 "version", 459 }, 460 { 461 lkenv.V2Recovery, 462 lkenv.V2Recovery.Number(), 463 lkenv.V2Run.Signature(), 464 "signature", 465 }, 466 } 467 468 for _, t := range tt { 469 testFile := filepath.Join(c.MkDir(), "lk.bin") 470 471 // make an out of band lkenv object and set the wrong signature to be 472 // able to export it to a file 473 var rawStruct interface{} 474 switch t.version { 475 case lkenv.V1: 476 rawStruct = lkenv.SnapBootSelect_v1{ 477 Version: t.binVersion, 478 Signature: t.binSignature, 479 } 480 case lkenv.V2Run: 481 rawStruct = lkenv.SnapBootSelect_v2_run{ 482 Version: t.binVersion, 483 Signature: t.binSignature, 484 } 485 case lkenv.V2Recovery: 486 rawStruct = lkenv.SnapBootSelect_v2_recovery{ 487 Version: t.binVersion, 488 Signature: t.binSignature, 489 } 490 } 491 492 buf := bytes.NewBuffer(nil) 493 ss := binary.Size(rawStruct) 494 buf.Grow(ss) 495 err := binary.Write(buf, binary.LittleEndian, rawStruct) 496 c.Assert(err, IsNil) 497 498 // calculate crc32 499 newCrc32 := crc32.ChecksumIEEE(buf.Bytes()[:ss-4]) 500 // note for efficiency's sake to avoid re-writing the whole structure, 501 // we re-write _just_ the crc32 to w as little-endian 502 buf.Truncate(ss - 4) 503 binary.Write(buf, binary.LittleEndian, &newCrc32) 504 505 err = ioutil.WriteFile(testFile, buf.Bytes(), 0644) 506 c.Assert(err, IsNil) 507 508 // now try importing the file with LoadEnv() 509 env := lkenv.NewEnv(testFile, "", t.version) 510 c.Assert(env, NotNil) 511 512 var expNum, gotNum uint32 513 switch t.validateFailMode { 514 case "signature": 515 expNum = t.version.Signature() 516 gotNum = t.binSignature 517 case "version": 518 expNum = t.version.Number() 519 gotNum = t.binVersion 520 } 521 expErr := fmt.Sprintf( 522 "cannot validate %s: expected %s 0x%X, got 0x%X", 523 testFile, 524 t.validateFailMode, 525 expNum, 526 gotNum, 527 ) 528 529 err = env.LoadEnv(testFile) 530 c.Assert(err, ErrorMatches, expErr) 531 } 532 } 533 534 func (l *lkenvTestSuite) TestLoadPropagatesErrNotExist(c *C) { 535 // make sure that if the env file doesn't exist, the error returned from 536 // Load() is os.ErrNotExist, even if it isn't exactly that 537 env := lkenv.NewEnv("some-nonsense-file-this-doesnt-exist", "", lkenv.V1) 538 c.Check(env, NotNil) 539 540 err := env.Load() 541 c.Assert(xerrors.Is(err, os.ErrNotExist), Equals, true, Commentf("err is %+v", err)) 542 c.Assert(err, ErrorMatches, "cannot open LK env file: open some-nonsense-file-this-doesnt-existbak: no such file or directory") 543 } 544 545 func (l *lkenvTestSuite) TestLoad(c *C) { 546 for _, version := range lkversions { 547 for _, makeBackup := range []bool{true, false} { 548 loggerBuf, restore := logger.MockLogger() 549 defer restore() 550 // make unique files per test case 551 testFile := filepath.Join(c.MkDir(), "lk.bin") 552 testFileBackup := testFile + "bak" 553 if makeBackup { 554 buf := make([]byte, 100000) 555 err := ioutil.WriteFile(testFileBackup, buf, 0644) 556 c.Assert(err, IsNil) 557 } 558 559 buf := make([]byte, 100000) 560 err := ioutil.WriteFile(testFile, buf, 0644) 561 c.Assert(err, IsNil) 562 563 // create an env for this file and try to load it 564 env := lkenv.NewEnv(testFile, "", version) 565 c.Check(env, NotNil) 566 567 err = env.Load() 568 // possible error messages could be "cannot open LK env file: ..." 569 // or "cannot valid <file>: ..." 570 if makeBackup { 571 // here we will read the backup file which exists but like the 572 // primary file is corrupted 573 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot validate %s: expected version 0x%X, got 0x0", testFileBackup, version.Number())) 574 } else { 575 // here we fail to read the normal file, and automatically try 576 // to read the backup, but fail because it doesn't exist 577 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot open LK env file: open %s: no such file or directory", testFileBackup)) 578 } 579 580 c.Assert(loggerBuf.String(), testutil.Contains, fmt.Sprintf("cannot load primary bootloader environment: cannot validate %s:", testFile)) 581 c.Assert(loggerBuf.String(), testutil.Contains, "attempting to load backup bootloader environment") 582 } 583 } 584 } 585 586 func (l *lkenvTestSuite) TestGetAndSetAndFindBootPartition(c *C) { 587 tt := []struct { 588 version lkenv.Version 589 // use slices instead of a map since we need a consistent ordering 590 bootMatrixKeys []string 591 bootMatrixValues []string 592 matrixType string 593 comment string 594 }{ 595 { 596 lkenv.V1, 597 []string{ 598 "boot_a", 599 "boot_b", 600 }, 601 []string{ 602 "kernel-1", 603 "kernel-2", 604 }, 605 "kernel", 606 "v1", 607 }, 608 { 609 lkenv.V2Run, 610 []string{ 611 "boot_a", 612 "boot_b", 613 }, 614 []string{ 615 "kernel-1", 616 "kernel-2", 617 }, 618 "kernel", 619 "v2 run", 620 }, 621 { 622 lkenv.V2Recovery, 623 []string{ 624 "boot_recovery_1", 625 }, 626 []string{ 627 "20201123", 628 }, 629 "recovery-system", 630 "v2 recovery 1 slot", 631 }, 632 { 633 lkenv.V2Recovery, 634 []string{ 635 "boot_recovery_1", 636 "boot_recovery_2", 637 }, 638 []string{ 639 "20201123", 640 "20201124", 641 }, 642 "recovery-system", 643 "v2 recovery 2 slots", 644 }, 645 { 646 lkenv.V2Recovery, 647 []string{ 648 "boot_recovery_1", 649 "boot_recovery_2", 650 "boot_recovery_3", 651 }, 652 []string{ 653 "20201123", 654 "20201124", 655 "20201125", 656 }, 657 "recovery-system", 658 "v2 recovery 3 slots", 659 }, 660 { 661 lkenv.V2Recovery, 662 []string{ 663 "boot_recovery_1", 664 "boot_recovery_2", 665 "boot_recovery_3", 666 "boot_recovery_4", 667 "boot_recovery_5", 668 "boot_recovery_6", 669 "boot_recovery_7", 670 "boot_recovery_8", 671 "boot_recovery_9", 672 "boot_recovery_10", 673 }, 674 []string{ 675 "20201123", 676 "20201124", 677 "20201125", 678 "20201126", 679 "20201127", 680 "20201128", 681 "20201129", 682 "20201130", 683 "20201131", 684 "20201132", 685 }, 686 "recovery-system", 687 "v2 recovery max slots", 688 }, 689 } 690 691 for _, t := range tt { 692 comment := Commentf(t.comment) 693 // make sure the key and values are the same length for test case 694 // consistency check 695 c.Assert(t.bootMatrixKeys, HasLen, len(t.bootMatrixValues), comment) 696 697 buf := make([]byte, 4096) 698 err := ioutil.WriteFile(l.envPath, buf, 0644) 699 c.Assert(err, IsNil, comment) 700 701 env := lkenv.NewEnv(l.envPath, "", t.version) 702 c.Assert(env, Not(IsNil), comment) 703 704 var findFunc func(string) (string, error) 705 var setFunc func(string, string) error 706 var getFunc func(string) (string, error) 707 var deleteFunc func(string) error 708 switch t.matrixType { 709 case "recovery-system": 710 findFunc = func(s string) (string, error) { return env.FindFreeRecoverySystemBootPartition(s) } 711 setFunc = func(s1, s2 string) error { return env.SetBootPartitionRecoverySystem(s1, s2) } 712 getFunc = func(s1 string) (string, error) { return env.GetRecoverySystemBootPartition(s1) } 713 deleteFunc = func(s1 string) error { return env.RemoveRecoverySystemFromBootPartition(s1) } 714 case "kernel": 715 findFunc = func(s string) (string, error) { return env.FindFreeKernelBootPartition(s) } 716 setFunc = func(s1, s2 string) error { 717 // for assigning the kernel, we need to also set the 718 // snap_kernel, since that is used to detect if we should return 719 // an unset variable or not 720 721 err := env.SetBootPartitionKernel(s1, s2) 722 c.Assert(err, IsNil, comment) 723 if err != nil { 724 return err 725 } 726 if env.Get("snap_kernel") == "" { 727 // only set it the first time so that the delete logic test 728 // works and we only set the first kernel to be snap_kernel 729 env.Set("snap_kernel", s2) 730 } 731 return nil 732 } 733 getFunc = func(s1 string) (string, error) { return env.GetKernelBootPartition(s1) } 734 deleteFunc = func(s1 string) error { return env.RemoveKernelFromBootPartition(s1) } 735 default: 736 c.Errorf("unexpected matrix type, test setup broken (%s)", comment) 737 } 738 739 err = env.InitializeBootPartitions(t.bootMatrixKeys...) 740 c.Assert(err, IsNil, comment) 741 742 // before assigning any values to the boot matrix, check that all 743 // values we try to assign would go to the first bootPartLabel 744 for _, bootPartValue := range t.bootMatrixKeys { 745 // we haven't assigned anything yet, so all values should get mapped 746 // to the first boot image partition 747 bootPartFound, err := findFunc(bootPartValue) 748 c.Assert(err, IsNil, comment) 749 c.Assert(bootPartFound, Equals, t.bootMatrixKeys[0], comment) 750 } 751 752 // now go and assign them, checking that along the way we are assigning 753 // to the next slot 754 // iterate over the key list to keep the same order 755 for i, bootPart := range t.bootMatrixKeys { 756 bootPartValue := t.bootMatrixValues[i] 757 // now we will be assigning things, so we should check that the 758 // assigned boot image partition matches what we expect 759 bootPartFound, err := findFunc(bootPartValue) 760 c.Assert(err, IsNil, comment) 761 c.Assert(bootPartFound, Equals, bootPart, comment) 762 763 err = setFunc(bootPart, bootPartValue) 764 c.Assert(err, IsNil, comment) 765 766 // now check that it has the right value 767 val, err := getFunc(bootPartValue) 768 c.Assert(err, IsNil, comment) 769 c.Assert(val, Equals, bootPart, comment) 770 771 // double-check that finding a free slot for this value returns the 772 // existing slot - this logic specifically is important for uc16 and 773 // uc18 where during seeding we will end up extracting a kernel to 774 // the already extracted slot (since the kernel will already have 775 // been extracted during image build time) 776 bootPartFound2, err := findFunc(bootPartValue) 777 c.Assert(err, IsNil, comment) 778 c.Assert(bootPartFound2, Equals, bootPart, comment) 779 } 780 781 // now check that trying to find a free slot for a new recovery system 782 // fails because we are full 783 if t.matrixType == "recovery-system" { 784 thing, err := findFunc("some-random-value") 785 c.Check(thing, Equals, "") 786 c.Assert(err, ErrorMatches, "cannot find free boot image partition", comment) 787 } 788 789 // test that removing the last one works 790 lastIndex := len(t.bootMatrixValues) - 1 791 lastValue := t.bootMatrixValues[lastIndex] 792 lastKey := t.bootMatrixKeys[lastIndex] 793 err = deleteFunc(lastValue) 794 c.Assert(err, IsNil, comment) 795 796 // trying to delete again will fail since it won't exist 797 err = deleteFunc(lastValue) 798 c.Assert(err, ErrorMatches, fmt.Sprintf("cannot find %q in boot image partitions", lastValue), comment) 799 800 // trying to find it will return the last slot 801 slot, err := findFunc(lastValue) 802 c.Assert(err, IsNil, comment) 803 c.Assert(slot, Equals, lastKey, comment) 804 } 805 } 806 807 func (l *lkenvTestSuite) TestV1NoRecoverySystemSupport(c *C) { 808 env := lkenv.NewEnv(l.envPath, "", lkenv.V1) 809 c.Assert(env, NotNil) 810 811 _, err := env.FindFreeRecoverySystemBootPartition("blah") 812 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 813 814 err = env.SetBootPartitionRecoverySystem("blah", "blah") 815 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 816 817 _, err = env.GetRecoverySystemBootPartition("blah") 818 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 819 820 err = env.RemoveRecoverySystemFromBootPartition("blah") 821 c.Assert(err, ErrorMatches, "internal error: v1 lkenv has no boot image partition recovery system matrix") 822 } 823 824 func (l *lkenvTestSuite) TestV2RunNoRecoverySystemSupport(c *C) { 825 env := lkenv.NewEnv(l.envPath, "", lkenv.V2Run) 826 c.Assert(env, NotNil) 827 828 _, err := env.FindFreeRecoverySystemBootPartition("blah") 829 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 830 831 err = env.SetBootPartitionRecoverySystem("blah", "blah") 832 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 833 834 _, err = env.GetRecoverySystemBootPartition("blah") 835 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 836 837 err = env.RemoveRecoverySystemFromBootPartition("blah") 838 c.Assert(err, ErrorMatches, "internal error: v2 run lkenv has no boot image partition recovery system matrix") 839 } 840 841 func (l *lkenvTestSuite) TestV2RecoveryNoKernelSupport(c *C) { 842 env := lkenv.NewEnv(l.envPath, "", lkenv.V2Recovery) 843 c.Assert(env, NotNil) 844 845 _, err := env.FindFreeKernelBootPartition("blah") 846 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 847 848 err = env.SetBootPartitionKernel("blah", "blah") 849 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 850 851 _, err = env.GetKernelBootPartition("blah") 852 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 853 854 err = env.RemoveKernelFromBootPartition("blah") 855 c.Assert(err, ErrorMatches, "internal error: v2 recovery lkenv has no boot image partition kernel matrix") 856 } 857 858 func (l *lkenvTestSuite) TestZippedDataSample(c *C) { 859 // TODO: add binary data test for v2 structures generated with gadget build 860 // tool when it has been updated for v2 861 862 // test data is generated with gadget build helper tool: 863 // $ parts/snap-boot-sel-env/build/lk-boot-env -w test.bin \ 864 // --snap-mode="trying" --snap-kernel="kernel-1" --snap-try-kernel="kernel-2" \ 865 // --snap-core="core-1" --snap-try-core="core-2" --reboot-reason="" \ 866 // --boot-0-part="boot_a" --boot-1-part="boot_b" --boot-0-snap="kernel-1" \ 867 // --boot-1-snap="kernel-3" --bootimg-file="boot.img" 868 // $ cat test.bin | gzip | xxd -i 869 gzipedData := []byte{ 870 0x1f, 0x8b, 0x08, 0x00, 0x95, 0x88, 0x77, 0x5d, 0x00, 0x03, 0xed, 0xd7, 871 0xc1, 0x09, 0xc2, 0x40, 0x10, 0x05, 0xd0, 0xa4, 0x20, 0x05, 0x63, 0x07, 872 0x96, 0xa0, 0x05, 0x88, 0x91, 0x25, 0x04, 0x35, 0x0b, 0x6b, 0x2e, 0x1e, 873 0xac, 0xcb, 0xf6, 0xc4, 0x90, 0x1e, 0x06, 0xd9, 0xf7, 0x2a, 0xf8, 0xc3, 874 0x1f, 0x18, 0xe6, 0x74, 0x78, 0xa6, 0xb6, 0x69, 0x9b, 0xb9, 0xbc, 0xc6, 875 0x69, 0x68, 0xaa, 0x75, 0xcd, 0x25, 0x6d, 0x76, 0xd1, 0x29, 0xe2, 0x2c, 876 0xf3, 0x77, 0xd1, 0x29, 0xe2, 0xdc, 0x52, 0x99, 0xd2, 0xbd, 0xde, 0x0d, 877 0x58, 0xe7, 0xaf, 0x78, 0x03, 0x80, 0x5a, 0xf5, 0x39, 0xcf, 0xe7, 0x4b, 878 0x74, 0x8a, 0x38, 0xb5, 0xdf, 0xbf, 0xa5, 0xff, 0x3e, 0x3a, 0x45, 0x9c, 879 0xb5, 0xff, 0x7d, 0x74, 0x8e, 0x28, 0xbf, 0xfe, 0xb7, 0xe3, 0xa3, 0xe2, 880 0x0f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 881 0xf8, 0x17, 0xc7, 0xf7, 0xa7, 0xfb, 0x02, 0x1c, 0xdf, 0x44, 0x21, 0x0c, 882 0x3a, 0x00, 0x00} 883 884 // uncompress test data to sample env file 885 rawData, err := unpackTestData(gzipedData) 886 c.Assert(err, IsNil) 887 err = ioutil.WriteFile(l.envPath, rawData, 0644) 888 c.Assert(err, IsNil) 889 err = ioutil.WriteFile(l.envPathbak, rawData, 0644) 890 c.Assert(err, IsNil) 891 892 env := lkenv.NewEnv(l.envPath, "", lkenv.V1) 893 c.Check(env, NotNil) 894 err = env.Load() 895 c.Assert(err, IsNil) 896 c.Check(env.Get("snap_mode"), Equals, boot.TryingStatus) 897 c.Check(env.Get("snap_kernel"), Equals, "kernel-1") 898 c.Check(env.Get("snap_try_kernel"), Equals, "kernel-2") 899 c.Check(env.Get("snap_core"), Equals, "core-1") 900 c.Check(env.Get("snap_try_core"), Equals, "core-2") 901 c.Check(env.Get("bootimg_file_name"), Equals, "boot.img") 902 c.Check(env.Get("reboot_reason"), Equals, "") 903 // first partition should be with label 'boot_a' and 'kernel-1' revision 904 p, err := env.GetKernelBootPartition("kernel-1") 905 c.Check(p, Equals, "boot_a") 906 c.Assert(err, IsNil) 907 // test second boot partition is free with label "boot_b" 908 p, err = env.FindFreeKernelBootPartition("kernel-2") 909 c.Check(p, Equals, "boot_b") 910 c.Assert(err, IsNil) 911 }