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